From a9ed31593fa4fdef068f3dd11d12dae4a15176a5 Mon Sep 17 00:00:00 2001 From: Max Bucknell Date: Sat, 11 Jan 2025 01:25:03 +0000 Subject: [PATCH] Initial commit --- .gitignore | 1 + README.md | 21 ++ Ultisnips/mbnotes.snippets | 69 +++++++ autoload/mbnotes.vim | 109 +++++++++++ doc/mbnotes.txt | 206 ++++++++++++++++++++ ftdetect/mbnotes.vim | 11 ++ plugin/mbnotes.vim | 79 ++++++++ python3/__pycache__/mbnotes.cpython-313.pyc | Bin 0 -> 1715 bytes python3/mbnotes.py | 32 +++ syntax/mbnotes.vim | 25 +++ 10 files changed, 553 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 Ultisnips/mbnotes.snippets create mode 100644 autoload/mbnotes.vim create mode 100644 doc/mbnotes.txt create mode 100644 ftdetect/mbnotes.vim create mode 100644 plugin/mbnotes.vim create mode 100644 python3/__pycache__/mbnotes.cpython-313.pyc create mode 100644 python3/mbnotes.py create mode 100644 syntax/mbnotes.vim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a56e3f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b1c005 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# MBNotes + +This plugin represents my attempt to incorporate all of my digital notes into +Vim. The basis of this foundation is Quarto, which is required for most of this +to work. Requires Vim 9. + +## Installation + +This plugin can be installed using whatever plugin installer you so desire. It +also depends on quarto-vim, which itself depends on vim-pandoc-syntax. For +`vim-plug`: + +```vim + Plug 'vim-pandoc/vim-pandoc-syntax' + Plug 'quarto-dev/quarto-vim' + Plug 'maxbucknell/vim-mbnotes' +``` + +## Licence + +Distributed under the Vim Licence. See [`:help licence`](https://github.com/vim/vim/blob/master/LICENSE) diff --git a/Ultisnips/mbnotes.snippets b/Ultisnips/mbnotes.snippets new file mode 100644 index 0000000..534e9fa --- /dev/null +++ b/Ultisnips/mbnotes.snippets @@ -0,0 +1,69 @@ +snippet note "Note Front Matter" b +--- +title: "${1:Title}" +date: "${3:`!v strftime(g:mbnotes_date_format_short, localtime() + (g:mbnotes_new_day_time * -3600))`} +--- + +# $1 + +$0 +endsnippet + +snippet :::n "Callout: Note" b +::: {.callout-note} +${0:${VISUAL:text...}} +::: +endsnippet + +snippet :::t "Callout: Tip" b +::: {.callout-tip} +${0:${VISUAL:text...}} +::: +endsnippet + +snippet :::w "Callout: Warning" b +::: {.callout-warning} +${0:${VISUAL:text...}} +::: +endsnippet + +snippet :::i "Callout: Important" b +::: {.callout-important} +${0:${VISUAL:text...}} +::: +endsnippet + +snippet `r "R Code Block" b +\`\`\`{r} +${1:#| output: ${2:false}} +${3:#| echo: ${4:false}} +${0:${VISUAL:# code...}} +\`\`\` +endsnippet + +snippet `py "Python Code Block" b +\`\`\`{python} +${1:#| output: ${2:false}} +${3:#| echo: ${4:false}} +${0:${VISUAL:# code...}} +\`\`\` +endsnippet + +snippet `ju "Julia Code Block" b +\`\`\`{julia} +${1:#| output: ${2:false}} +${3:#| echo: ${4:false}} +${0:${VISUAL:# code...}} +\`\`\` +endsnippet + +snippet todo "Make a To-Do List!" b +- [ ] ${0:What would you like to do..?} +endsnippet + +snippet --- "Horizontal Rule" b +-------------------------------------------------------------------------------- + +$0 +endsnippet + diff --git a/autoload/mbnotes.vim b/autoload/mbnotes.vim new file mode 100644 index 0000000..45a4904 --- /dev/null +++ b/autoload/mbnotes.vim @@ -0,0 +1,109 @@ +vim9s + +if !exists("g:mbnotes_loaded") + finish +endif + +export def OpenDailyNote(offset: number = 0) + var diff = (g:mbnotes_new_day_time * -3600) + (offset * 86400) + var date = strftime(g:mbnotes_date_format_short, localtime() + diff) + var filename = g:mbnotes_dir .. "/daily/" .. date .. "-daily.qmd" + + execute "silent edit " .. fnameescape(filename) + + if !filereadable(filename) + python3 import mbnotes + python3 import vim + + execute "python3 vim.current.buffer[:] = mbnotes.generate_daily_note(" .. diff .. ").splitlines()" + + write + + normal G + endif +enddef + +export def RenderNote(format: string, buffer = "%") + var input = expand(buffer) + var output = substitute( + fnamemodify(input, ":r"), + g:mbnotes_dir, + g:mbnotes_out_dir, + "" + ) .. "." .. format + + def ExitCb(job: job, exit: number) + if exit != 0 + execute "botright sbuf " .. b:mbnotes_renderer_buffer + elseif exists("g:mbnotes_open_command") + execute "botright sbuf " .. b:mbnotes_renderer_buffer + execute "!" .. g:mbnotes_open_command .. " " .. output + execute "bwipeout " .. b:mbnotes_renderer_buffer + endif + + unlet b:mbnotes_renderer_buffer + enddef + + b:mbnotes_renderer_buffer = term_start([ + "quarto", + "render", + input, + "--to", + format, + "--output-dir", + g:mbnotes_out_dir + ], { + cwd: g:mbnotes_dir, + hidden: true, + exit_cb: ExitCb + }) +enddef + +export def BeforeNoteSave() + var full_path = expand('%:p') + var daily_path = g:mbnotes_dir .. "/daily" + + # Don't touch daily notes + if daily_path != full_path[0 : len(daily_path) - 1] + var base = expand('%:t') + var date = base[0 : 9] + + var original_mark = getpos("'s") + + normal! ms + + cursor(1, 1) + var title_line = search('^#\s\+') + + if title_line == 0 + throw "Unable to save note: No title found." + endif + + var title = substitute(getline(title_line), '^#\s\+', '', '') + var sanitised = substitute( + tolower(title), + '[^a-z0-9]\+', + "-", + "g" + ) + + normal! `s + setpos("'s", original_mark) + + b:new_name = date .. "_" .. sanitised .. ".qmd" + endif +enddef + +export def AfterNoteSave() + if exists("b:new_name") + execute "silent Move " .. fnameescape(g:mbnotes_dir) .. "/" .. b:new_name + endif +enddef + +export def NewNote() + var diff = (g:mbnotes_new_day_time * -3600) + var date = strftime(g:mbnotes_date_format_short, localtime() + diff) + + var file = date .. "_new-note.qmd" + execute "edit " .. g:mbnotes_dir .. "/" .. file +enddef diff --git a/doc/mbnotes.txt b/doc/mbnotes.txt new file mode 100644 index 0000000..e96baae --- /dev/null +++ b/doc/mbnotes.txt @@ -0,0 +1,206 @@ +*mbnotes.txt* Max Bucknell's notes framework + +============================================================================== +1. Contents *mbnotes-contents* + + 1. Introduction ...................... |mbnotes| + 2. Setup ............................. |mbnotes-setup| + 3. Configuration ..................... |mbnotes-config| + 4. Commands .......................... |mbnotes-commands| + 5. Front Matter ...................... |mbnotes-front-matter| + +============================================================================== +1. Introduction *mbnotes* + +At the start of 2025, I started to use Vim for my digital notes, aiming to +combine all of the features I liked from the various tools I had used over the +years. This included: + +* Markdown +* Convenient exporting to PDF and HTML +* Callout blocks of varying colours +* Linking +* Tagging +* Daily notes that I can write in currently, retrospectively, and in advance +* Interpolation of both mathematical text and code +* Embedding of code to be executed. + +Most of this came out of the box with Quarto, at least with some configuration. +This plugin consists of support for that, along with other conveniences to +better glue it all together. + +============================================================================== +2. Setup *mbnotes-setup* + +Vim 9 is required for this plugin to work, since it is written in Vim9Script. + +This plugin can be installed using whatever plugin installer you so desire. It +also depends on quarto-vim, which itself depends on vim-pandoc-syntax. For +vim-plug >vim + + Plug 'vim-pandoc/vim-pandoc-syntax' + Plug 'quarto-dev/quarto-vim' + Plug 'maxbucknell/vim-mbnotes' + +Please note that you must also set |g:mbnotes_dir| + +g:mbnotes_dir *g:mbnotes_dir* + +This is the root directory of all notes. It must be set before any +functionality can be used. If it is not set, the plugin will not start, and +will echo an error message. + +Set in vimrc >vim + + let g:mbnotes_dir = $HOME .. "/notes" + +This directory and a subdirectory `daily` will be created. The latter is used +for storing daily notes. See |mbnotes-front-matter| for configuring rendering +options. + +============================================================================== +3. Configuration *mbnotes-config* + +g:mbnotes_out_dir *g:mbnotes_out_dir* + +This is where rendered outputs will be placed. By default this is a temporary +directory, but it can be set manually. If the project `output-dir` is set in +the Quarto project `_metadata.yml` like so: + +>yaml +project + output-dir: _output +< + +Then `g:mbnotes_out_dir` should be set accordingly: + +>vim + let g:mbnnotes_out_dir = g:mbnotes_dir .. "/_output" +< + +g:mbnotes_open_daily_on_startup *g:mbnotes_open_daily_on_startup* + +When launching Vim with no additional arguments, open the current daily note +rather than the default Vim startup screen. Defaults to 0. Enable by: >vim + + let g:mbnotes_open_daily_on_startup = 1 + +g:mbnotes_new_day_time *g:mbnotes_new_day_time* + +Sets the time of day that a new day begins, as far as daily note designation is +concerned. By default, a new day starts at 4am, but to start a new day at +midnight, use >vim + + let g:mbnotes_new_day_time = 0 + +g:mbnotes_date_format_short *g:mbnotes_date_format_short* + +This date format is used in file names for new notes, and for daily notes. It +is recommended to keep this as something that sorts alphabetically. This string +should be something that can be passed to `strftime`. See `man 3 strftime` for +more details. + +The default format outputs dates like "2023-09-21" + +>vim + let g:mbnotes_date_format_short = "%Y-%m-%d" +< + +g:mbnotes_date_format_long *g:mbnotes_date_format_long* + +This date format is used to set the `date` metadata field in document front +matter, as well as the title for daily notes. See |g:mbnotes_date_format_short| +for formatting details. + +The default format outputs dates like "Thursday, 18 May 2024" + +>vim + let g:mbnotes_date_format_long = "%A, %-e %B %Y" +< + +g:mbnotes_open_command *g:mbnotes_open_command* + +External command to open built files. By default, the following commands are +tried (in order): + +* `open` +* `xdg-open` + +If none of the above are defined, and this variable is not set explicitly, then +the render commands (e.g. |:MBNotesRenderPDF|) will not open the output file +after rendering. + +============================================================================== +4. Commands *mbnotes-commands* + +`:MBNotesNew` *:MBNotesNew* + +Creates a new note in the current window, with today's date in the file name. +The date used rolls over at |g:mbnotes_new_day_time|. + +`:MBNotesNewSplit` *:MBNotesNewSplit* + +The same as `:MBNotesNew`, but opens in a split. Can be controlled by modifier +commands, such as `:above`. + +`:MBNotesOpenDaily` *:MBNotesOpenDaily* + +Open the daily note in the current window. This command takes a single optional +integer argument reprenting an offset. To open tomorrow's daily note >vim + + :MBNotesOpenDaily 1 + +Or to open last week's >vim + + :MBNotesOpenDaily -7 + +`:MBNotesOpenDailySplit` *:MBNotesOpenDailySplit* + +The same as `:MBNotesOpenDaily`, but opens in a split. Can be controlled by +modifier commands, such as `:vertical`. + +`:MBNotesRenderPDF` *:MBNotesRenderPDF* + +Render the current buffer as a PDF. It will open the PDF using +`g:mbnotes_open_command` if it successfully builds. + +If the document fails to render, a terminal buffer is displayed showing the +results of the quarto render command that was attempted. + +`:MBNotesRenderHTML` *:MBNotesRenderHTML* + +Render the current buffer as an HTML file and open it. See |:MBNotesRenderPDF| +for details on behaviour. + +============================================================================== +5. Front Matter *mbnotes-front-matter* + +Quarto supports a wide variety of configuration for document metadata. This can +be placed in the document front matter, but it gets unwieldy. It is recommended +that `g:mbnotes_dir` point to a Quarto project. Then, a file called +`_metadata.yml` can be placed at the root. + +A good place to start is: + +>yaml +author: "{Your Name}" +format: + html: + embed-resources: true + html-math-method: katex + theme: + light: flatly + dark: darkly + pdf: + documentclass: article + geometry: + - top=30mm + - left=20mm + - heightrounded +< + +Anything supported by Quarto can be specified here and will be imported and +merged with any front matter specified at the top of any file. See the Quarto +docs for more information, specifically on projects. + + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/ftdetect/mbnotes.vim b/ftdetect/mbnotes.vim new file mode 100644 index 0000000..3aa960d --- /dev/null +++ b/ftdetect/mbnotes.vim @@ -0,0 +1,11 @@ +vim9s + +def IsMBNotes() + var path_prefix = expand('%:p')[0 : len(g:mbnotes_dir) - 1] + + if path_prefix == g:mbnotes_dir + set filetype=mbnotes + endif +enddef + +au BufNewFile,BufRead *.qmd IsMBNotes() diff --git a/plugin/mbnotes.vim b/plugin/mbnotes.vim new file mode 100644 index 0000000..27662fe --- /dev/null +++ b/plugin/mbnotes.vim @@ -0,0 +1,79 @@ +vim9s + +if exists("g:mbnotes_loaded") + finish +endif + +if !exists("g:mbnotes_dir") + echoerr "MBNotes: Error: g:mbnotes_dir not set." + finish +else + silent mkdir(fnameescape(g:mbnotes_dir .. "/daily"), "p") +endif + +if !exists("g:mbnotes_out_dir") + g:mbnotes_out_dir = $TMPDIR .. "xyz.mpwb.vim.mbnotes" +endif + +if !exists("g:mbnotes_open_command") + if executable("open") + g:mbnotes_open_command = "open" + elseif executable("xdg-open") + g:mbnotes_open_command = "xdg-open" + endif +endif + +silent mkdir(g:mbnotes_out_dir, "p") + +if !exists("g:mbnotes_open_daily_on_startup") + g:mbnotes_open_daily_on_startup = false +endif + +if !exists("g:mbnotes_new_day_time") + g:mbnotes_new_day_time = 4 +endif + +if !exists("g:mbnotes_date_format_short") + g:mbnotes_date_format_short = "%Y-%m-%d" +endif + +if !exists("g:mbnotes_date_format_long") + g:mbnotes_date_format_long = "%A, %-e %B %Y" +endif + +import autoload 'mbnotes.vim' + +augroup MBNotes + autocmd! + + if g:mbnotes_open_daily_on_startup + au VimEnter * ++nested { + if @% == "" + mbnotes.OpenDailyNote() + endif + } + endif + + execute "autocmd BufWritePre " + .. fnameescape(g:mbnotes_dir) .. "/*.qmd mbnotes.BeforeNoteSave()" + + execute "autocmd BufWritePost " + .. fnameescape(g:mbnotes_dir) .. "/*.qmd mbnotes.AfterNoteSave()" +augroup END + +command -nargs=? MBNotesOpenDaily mbnotes.OpenDailyNote() +command -nargs=? MBNotesOpenDailySplit { + execute " new" + mbnotes.OpenDailyNote() +} + +command -nargs=0 MBNotesRenderPDF mbnotes.RenderNote("pdf") +command -nargs=0 MBNotesRenderHTML mbnotes.RenderNote("html") + +command -nargs=0 MBNotesNew mbnotes.NewNote() +command -nargs=0 MBNotesNewSplit { + execute " new" + mbnotes.NewNote() +} + +g:mbnotes_loaded = 1 diff --git a/python3/__pycache__/mbnotes.cpython-313.pyc b/python3/__pycache__/mbnotes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c20436df45b36af0f8996364fa910b49bd1ca557 GIT binary patch literal 1715 zcmb7F&2QXP5FhVHvbCEwBmvZ}gdUqk$|81yC>7L7f>a_93F;x6pojp;+x6IMYQML7 zZyea3mLt7HLPBuh4}cqgN}?P90|Kd)5VuN!3n%pK{V3vq_#Nzd^JeCaXMXd1F*j!e z0KELcC&6DOfCpwVMsXxu+y;Qpp$%+qqaW&*1*`@2!!X;^7x7vp}!2^)p9;&DO>V@xlZr-u=7{+k8RY&LBQ zDMFXkHhCBg*d0k&vw1>2sa#g8=Tgl!5zN}WYql>{%*nMGW2{GUluHH}chGAp)G|?B z)Gi~HU}iVAZN{8>%^0aCJ(u0(gdb1#V;%}$3Ub-ooa9t3h0N7&iGD&8J@qig>DdNR z81%?xe#9vnObiV?uI{?obF|U-{Hd=eyGe|Kx)ImfWn59%ji!nJ&%^?hF_Icg;~O*? z$^QC{7vuOJi8Wb-KCQp>Z+#EV*C?3?550+xNa7Bvrp7LXQjAM%TDi=!{@C{NGMl#3 zw!0&;WGDkR6g$f3aPO!uRZ%-K;W zaO@j}niv-z<64J-K+hN-<)IGT_m5h3!%AmIncce{xN*mnQ6FYi$ghRNNQ&Uz{B$qB zLmGv&WUSJe(Ym5a0FaimVk$$gw0IoGX$g;clvXq;KfAN8WbHbn(S4TMI_YQ%DIsi> zQs=SyE5pH&CeKA(0sw8!F$0)Bzxs}hk{I>s>(ay3<1lV!>96KByRka@oK}C3E{=I{ zn4fP9)gk~qYeK#Y07Lj=2`(?4Y~0^CcdB27-#TZ{terV)!_A)-p8R6vvz628E9VQZ z4!6H|uAZ!ay8dPL+<9)e`EYLjWdHvD=~FA`bB*B}51nh@IrTHA{&oG-sh>I%}0A~Dk4FDb$ kEz9~5?4P0f3#|O`#I;9It(GpJx=