Initial commit
This commit is contained in:
commit
a9ed31593f
10 changed files with 553 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/doc/tags
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -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)
|
69
Ultisnips/mbnotes.snippets
Normal file
69
Ultisnips/mbnotes.snippets
Normal file
|
@ -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
|
||||
|
109
autoload/mbnotes.vim
Normal file
109
autoload/mbnotes.vim
Normal file
|
@ -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
|
206
doc/mbnotes.txt
Normal file
206
doc/mbnotes.txt
Normal file
|
@ -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:
|
11
ftdetect/mbnotes.vim
Normal file
11
ftdetect/mbnotes.vim
Normal file
|
@ -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()
|
79
plugin/mbnotes.vim
Normal file
79
plugin/mbnotes.vim
Normal file
|
@ -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(<args>)
|
||||
command -nargs=? MBNotesOpenDailySplit {
|
||||
execute "<mods> new"
|
||||
mbnotes.OpenDailyNote(<args>)
|
||||
}
|
||||
|
||||
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 "<mods> new"
|
||||
mbnotes.NewNote()
|
||||
}
|
||||
|
||||
g:mbnotes_loaded = 1
|
BIN
python3/__pycache__/mbnotes.cpython-313.pyc
Normal file
BIN
python3/__pycache__/mbnotes.cpython-313.pyc
Normal file
Binary file not shown.
32
python3/mbnotes.py
Normal file
32
python3/mbnotes.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import vim
|
||||
from string import Template
|
||||
from datetime import datetime, timedelta
|
||||
daily_template = Template("""---
|
||||
title: "Daily Note"
|
||||
date: "$date"
|
||||
---
|
||||
|
||||
# $date
|
||||
|
||||
## Daily Note
|
||||
|
||||
|
||||
""")
|
||||
|
||||
def generate_daily_note(date_offset = 0):
|
||||
"""Generate a new daily note.
|
||||
|
||||
By default, this generates a daily note starter for today. The
|
||||
`date_offset` argument changes this by offsetting the current date by the
|
||||
given number of seconds.
|
||||
|
||||
This function returns a string. To put into a python-buffer, this output
|
||||
needs to be split by line into a list and assigned to `buffer[:]`.
|
||||
"""
|
||||
date = datetime.today() + timedelta(seconds=date_offset)
|
||||
date_format = vim.eval("g:mbnotes_date_format_long")
|
||||
date = date.strftime(date_format)
|
||||
|
||||
author = vim.eval("g:mbnotes_author")
|
||||
|
||||
return daily_template.substitute(date=date, author=author)
|
25
syntax/mbnotes.vim
Normal file
25
syntax/mbnotes.vim
Normal file
|
@ -0,0 +1,25 @@
|
|||
vim9s
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
runtime! syntax/quarto.vim
|
||||
unlet b:current_syntax
|
||||
|
||||
syn region mbnCalloutNote start=":::\s\+{\.callout-note\s*" end=":::$" keepend
|
||||
hi link mbnCalloutNote Question
|
||||
|
||||
syn region mbnCalloutWarning start=":::\s\+{\.callout-warning\s*" end=":::$" keepend
|
||||
hi link mbnCalloutWarning SpellCap
|
||||
|
||||
syn region mbnCalloutImportant start=":::\s\+{\.callout-important\s*" end=":::$" keepend
|
||||
hi link mbnCalloutImportant SpellBad
|
||||
|
||||
syn region mbnCalloutTip start=":::\s\+{\.callout-tip\s*" end=":::$" keepend
|
||||
hi link mbnCalloutNote Question
|
||||
|
||||
syn region mbnCalloutCaution start=":::\s\+{\.callout-caution\s*" end=":::$" keepend
|
||||
hi link mbnCalloutWarning SpellCap
|
||||
|
||||
b:current_syntax = "mbnotes"
|
Loading…
Add table
Reference in a new issue