196 lines
7.4 KiB
VimL
196 lines
7.4 KiB
VimL
" libdenote plugin for vim
|
|
" This plugin describes basic denote functions. Each one of these functions
|
|
" deals wither with the file-naming scheme (prefixed with scheme_), or with the
|
|
" front matter (prefixed with fm_). All functions are pure, i.e., have no side
|
|
" affects.
|
|
"
|
|
" FILE-NAMING SCHEME {{{1
|
|
"
|
|
" Script-local functions {{{2
|
|
"
|
|
" This function removes illegal characters in parts of the filename.
|
|
function s:santizefnpart(part)
|
|
return a:part->tolower()
|
|
\ ->substitute('[^[:alnum:]]', '-', 'g')
|
|
\ ->substitute('-\+', '-', 'g')
|
|
\ ->substitute('_\+', '_', 'g')
|
|
\ ->substitute('=\+', '=', 'g')
|
|
\ ->substitute('@\+', '@', 'g')
|
|
\ ->trim('-_@=')
|
|
endfunction
|
|
|
|
" API {{{2
|
|
|
|
" Identifier creation
|
|
" This unction generates a fresh denote identifier
|
|
function libdenote#scheme_idgen()
|
|
return exists('*strftime')
|
|
\ ? strftime('%Y%m%dT%H%M%S')
|
|
\ : rand()
|
|
endfunction
|
|
|
|
" Generate file name
|
|
" This function returns the Filename given all components in the file-naming
|
|
" scheme
|
|
"
|
|
" @argument a:ext string: File extension
|
|
" @argument a:id string: Identifier of denote entry
|
|
" @argument a:title string: Title of denote entry
|
|
" @argument a:tags list[string]: List of strings that specify the tags
|
|
" @argument a:sig string: Signature string of denote entry
|
|
function libdenote#scheme_filename(ext, identifier, title='', tags=[], sig='')
|
|
let l:f = s:santizefnpart(a:identifier)
|
|
let l:f ..= len(a:sig) > 0 ? ('==' .. s:santizefnpart(a:sig)) : ''
|
|
let l:f ..= len(a:title) > 0 ? ('--' .. s:santizefnpart(a:title)) : ''
|
|
let l:f ..= len(a:tags) > 0 ? ('__' .. map(copy(a:tags), {_, v -> s:santizefnpart(v)})->join('_')) : ''
|
|
return l:f .. '.' .. a:ext
|
|
endfunction
|
|
|
|
" Retrieve metadata from file name
|
|
" This function returns a dict with the entries 'id', 'title', 'tags', and
|
|
" 'sig.' The value type of 'tags' is a list, the other entries hold strings.
|
|
"
|
|
" @argument a:filename string: Filename or path to file
|
|
function libdenote#scheme_metadata(filename)
|
|
return {
|
|
\ 'id': a:filename->fnamemodify(':t')->matchstr('@@\zs.\{-\}\ze\(==\|--\|__\|\..\)')
|
|
\ ?? a:filename->fnamemodify(':t')->matchstr('^.\{-\}\ze\(==\|--\|__\|\..\)'),
|
|
\ 'title': a:filename->fnamemodify(':t')->matchstr('--\zs.\{-\}\ze\(==\|@@\|__\|\..\)')
|
|
\ ->substitute('-', ' ', 'g'),
|
|
\ 'tags': a:filename->fnamemodify(':t')->matchstr('__\zs.\{-\}\ze\(==\|@@\|--\|\..\)')
|
|
\ ->split('_'),
|
|
\ 'sig': a:filename->fnamemodify(':t')->matchstr('==\zs.\{-\}\ze\(==\|@@\|__\|\..\)')
|
|
\}
|
|
endfunction
|
|
|
|
|
|
" Get path to file from denote identifier
|
|
" This function returns the path to the file the note corresponds to, if it
|
|
" exists, and v:false otherwise.
|
|
"
|
|
" @argument a:dir string: Path to denote directory
|
|
" @argument a:id string: Identifier of denote entry
|
|
function libdenote#scheme_find(dir, id)
|
|
" According to the file-naming scheme, the note id is prefixed with '@@'. If
|
|
" the note id appears at the beginning of the file name, then this prefix is
|
|
" optional. There may exist another field (such as the signature, title, or
|
|
" keywords field) after the note id. These are prefixed with '==', '--', or
|
|
" '__'. If the note id is the last field, then the id is followed by the file
|
|
" extension, e.g., '.md'.
|
|
" (A) First, we get all files that contain the note id as substring.
|
|
" (B) Then we ensure that the note id is followed by another field or by the
|
|
" file extension.
|
|
let l:files = glob(a:dir .. '/*' .. a:id .. '*', 0, v:true)
|
|
\ ->filter('v:val->split("/")[-1] =~ "' .. a:id .. '\\(==\\|--\\|__\\|\\.\\)"')
|
|
\ ->filter('v:val->split("/")[-1] =~ "^' .. a:id .. '\\|@@' .. a:id .. '"')
|
|
return empty(l:files) ? v:false : l:files[0]
|
|
endfunction
|
|
|
|
|
|
" FRONT-MATTER HANDLING {{{1
|
|
"
|
|
" Script-local functions {{{2
|
|
"
|
|
" Put string in double quotes, and remove inside double quotes.
|
|
function s:escapeDQ(s)
|
|
return '"' .. substitute(a:s, '"', '', 'g') .. '"'
|
|
endfunction
|
|
|
|
" Create front matter (yaml)
|
|
function s:md_new_yaml(id, title, tags)
|
|
return [
|
|
\ '---',
|
|
\ 'title: ' .. s:escapeDQ(a:title),
|
|
\ 'date: ' .. strftime('%FT%T%z'),
|
|
\ 'tags: [' .. map(copy(a:tags), {_, t -> s:escapeDQ(t) })->join(', ') .. ']',
|
|
\ 'identifier: ' .. s:escapeDQ(a:id),
|
|
\ '---'
|
|
\ ]
|
|
endfunction
|
|
|
|
" Create front matter (toml)
|
|
function s:md_new_toml(id, title, tags)
|
|
return [
|
|
\ '+++',
|
|
\ 'title ' .. s:escapeDQ(a:title),
|
|
\ 'date ' .. strftime('%FT%T%z'),
|
|
\ 'tags [' .. map(copy(a:tags), {_, t -> s:escapeDQ(t) })->join(', ') .. ']',
|
|
\ 'identifier ' .. s:escapeDQ(a:id),
|
|
\ '+++'
|
|
\ ]
|
|
endfunction
|
|
|
|
" Create front matter (plain)
|
|
function s:plain_new(id, title, tags)
|
|
return [
|
|
\ 'title: ' .. a:title,
|
|
\ 'date: ' .. strftime('%F'),
|
|
\ 'tags: ' .. a:tags->join(' '),
|
|
\ 'identifier: ' .. a:id,
|
|
\ '---------------------------',
|
|
\ ]
|
|
endfunction
|
|
|
|
" Create front matter (org)
|
|
function s:org_new(id, title, tags)
|
|
return [
|
|
\ '#+title: ' .. a:title,
|
|
\ '#+date: [' .. strftime('%F %a %R') .. ']',
|
|
\ '#+filetags: :' .. map(copy(a:tags), {_, t -> substitute(t, ':', '', 'g') })->join(':') .. ':',
|
|
\ '#+identifier: ' .. a:id,
|
|
\ ]
|
|
endfunction
|
|
|
|
" API {{{2
|
|
|
|
" Create front matter
|
|
" This function generates a front matter. It may use the global variable
|
|
" g:denote_fm_md_type.
|
|
"
|
|
" @argument a:ext string: Generate front matter for a file of this
|
|
" extension
|
|
" @argument a:id string: Identifier of the denote note
|
|
" @argument a:title string: Title of the denote note
|
|
" @argument a:tags list[string]: List of strings that specify the tags
|
|
" @argument a:md_type string: Any of 'yaml' (default) or 'toml', for
|
|
" markdown front matter
|
|
" @return: List of strings with a line-per item that describes the front matter
|
|
function libdenote#fm_gen(ext, id, title, tags=[], md_type='yaml')
|
|
return a:ext == 'org' ? s:org_new(a:id, a:title, a:tags)
|
|
\ : a:ext == 'txt' ? s:plain_new(a:id, a:title, a:tags)
|
|
\ : a:md_type == 'toml' ? s:md_new_toml(a:id, a:title, a:tags)
|
|
\ : s:md_new_yaml(a:id, a:title, a:tags)
|
|
endfunction
|
|
|
|
" Alter front matter
|
|
" This function returns the (modified) a:fm front matter. For any of the
|
|
" arguments a:id, a:title, and a:tags, if the argument is specified (for the
|
|
" tag, it may also be an empty list), then the value of the argument is used as
|
|
" a replacement in the input front matter a:fm.
|
|
"
|
|
" @argument a:fm list[string] List of strings describes a frontmatter
|
|
" @argument a:ext string: Generate front matter for a file of this
|
|
" extension
|
|
" @argument a:id string: Identifier of the denote note
|
|
" @argument a:title string: Title of the denote note
|
|
" @argument a:tags list[string] or v:false:
|
|
" List of strings that specify the tags
|
|
" @return: List of strings with a line-per item that describes the front matter
|
|
function libdenote#fm_alter(fm, ext, id='', title='', tags=v:false)
|
|
let l:new = copy(a:fm)
|
|
let l:repl = libdenote#fm(a:ext, a:id ?? '', a:title ?? '', a:tags ?? [])
|
|
if a:id
|
|
let l:ididx = indexof(l:repl, 'v:val =~ "^\\(#+\\)\\?identifier"')
|
|
call map(l:new, {_, v -> v =~ "^\\(#+\\)\\?identifier" ? l:repl[l:ididx] : v})
|
|
endif
|
|
if a:title
|
|
let l:titleidx = indexof(l:repl, 'v:val =~ "^\\(#+\\)\\?title"')
|
|
call map(l:new, {_, v -> v =~ "^\\(#+\\)\\?title" ? l:repl[l:titleidx] : v})
|
|
endif
|
|
if type(a:tags) == v:t_list
|
|
let l:tagsidx = indexof(l:repl, 'v:val =~ "^\\(#+file\\)\\?tags"')
|
|
call map(l:new, {_, v -> v =~ "^\\(#+file\\)\\?tags" ? l:repl[l:tagsidx] : v})
|
|
endif
|
|
return l:new
|
|
endfunction
|