Compare commits
7 Commits
c3ed08e42f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 270be048dc | |||
| e1c3ea09da | |||
| 5df2b79938 | |||
| 320bc0b6c0 | |||
| 5b7825d358 | |||
| 66e032ee38 | |||
| 2270ab9069 |
11
README.md
11
README.md
@@ -23,15 +23,16 @@ Also, the present package aims at remaining flexible. Again, both
|
||||
above-mentioned plugins require the denote note identifiers to follow the rigid
|
||||
format `YYYYMMDDTHHMMSS`. Per denote manual, however, the identify may be [any
|
||||
string](https://protesilaos.com/emacs/denote#h:3048f558-7d84-45d6-9ef2-53055483e801)
|
||||
(free of field delimiters, of course).
|
||||
(free of field delimiters, of course). This package offers great flexibility in
|
||||
configuration (see `:help denote-settings`).
|
||||
|
||||
### Installation
|
||||
You may use any of your favorite plugin managers, or, manually place a copy of
|
||||
this package in, e.g., `~/.vim/pack/tools/start`. For such a manual
|
||||
installation, you may want to run `:helptags
|
||||
~/.vim/pack/tools/start/vim-denote` to regenerate the help files. This allows
|
||||
you to display the package documentation using `:help vim-denote` and similar
|
||||
commands.
|
||||
~/.vim/pack/tools/start/vim-denote/doc` to regenerate the help files. This
|
||||
allows you to display the package documentation using `:help vim-denote` and
|
||||
similar commands.
|
||||
|
||||
### Usage
|
||||
vim needs to know where your denote entries are stored. To do so, run the
|
||||
@@ -54,7 +55,7 @@ completion](https://vimhelp.org/insert.txt.html#compl-omni) by pressing
|
||||
`denote:<str>`, then the link is completed for denote entries that have `<str>`
|
||||
as part of the title.
|
||||
|
||||
_Browsing_ and searching notes:_ Browsing and searching is done using vim's
|
||||
_Browsing and searching notes:_ Browsing and searching is done using vim's
|
||||
[location list](https://vimhelp.org/quickfix.txt.html#location-list). The
|
||||
benefit of doing so is that commands like `:lnext`, `:lprev` etc. become
|
||||
available.
|
||||
|
||||
87
after/ftplugin/markdown.vim
Normal file
87
after/ftplugin/markdown.vim
Normal file
@@ -0,0 +1,87 @@
|
||||
" Run this plugin only if the denote package has been setup
|
||||
if !exists('g:denote_directory')
|
||||
finish
|
||||
endif
|
||||
|
||||
" ... and if this filetype is specified as denote-note file
|
||||
if index(g:denote_note_file_extensions, expand('%:e')) == -1
|
||||
finish
|
||||
endif
|
||||
|
||||
" ... and if the file is in the denote directory
|
||||
if g:denote_directory != expand('%:p:h')
|
||||
finish
|
||||
endif
|
||||
|
||||
" Load only once per buffer
|
||||
if exists('b:loaded_denote_ftplugin_notes')
|
||||
finish
|
||||
endif
|
||||
let b:loaded_denote_ftplugin_notes = 1
|
||||
|
||||
" Link completion
|
||||
" This works by using the functions s:column(), s:suggestions(),
|
||||
" s:complete_link(), and by setting 'omnifunc'.
|
||||
|
||||
" Find the column where the completion starts. This must be between 1 and
|
||||
" col('.'). Denote links are of this form: `denote:<identifier>`.
|
||||
function DenoteNoteCompleteLinkColumn()
|
||||
" Get the substring from the start of the line until col('.')
|
||||
let l:x = max([1, col('.')-2])
|
||||
let l:l = getline('.')[:l:x]
|
||||
" Take the shortest prefix of a denote link. This may be any of
|
||||
" \<d$
|
||||
" ..
|
||||
" \<denote:$
|
||||
" \<denote:\f$
|
||||
" \<denote:\f\f$
|
||||
" etc.
|
||||
let l:res = l:l->matchstrpos('\<denote:\f*$')
|
||||
if l:res[1] >= 0
|
||||
return l:res[1]
|
||||
endif
|
||||
let l:res = l:l->matchstrpos('\<d\f*$')
|
||||
if l:res[1] == -1
|
||||
return -3
|
||||
endif
|
||||
return 'denote:' =~ '^' .. l:res[0] ? l:res[1] : -3
|
||||
endfunction
|
||||
|
||||
" Return completion items given by the base
|
||||
function DenoteNoteCompleteLinkSuggestions(base)
|
||||
let l:prefix = a:base->matchstr('^denote:\zs.*$')
|
||||
let l:flist = glob(g:denote_directory .. '/' .. (l:prefix ? '*' .. l:prefix .. '*' : '*'), 0, v:true)
|
||||
let l:res = []
|
||||
for filename in l:flist
|
||||
let l:meta = libdenote#scheme_metadata(filename)
|
||||
if l:meta.id == v:false || (l:meta.id !~ '^' .. l:prefix && l:meta.title !~ l:prefix)
|
||||
continue
|
||||
endif
|
||||
let l:meta.title = l:meta.title ?? '(no title)'
|
||||
call add(l:res, {
|
||||
\ 'word' : 'denote:' .. l:meta.id,
|
||||
\ 'abbr' : l:meta.title,
|
||||
\ 'menu' : l:meta.tags->join(', ')
|
||||
\ })
|
||||
endfor
|
||||
return l:res
|
||||
endfunction
|
||||
|
||||
" Completion function for denote links
|
||||
function DenoteNoteCompleteLink(findstart, base)
|
||||
return a:findstart == 1 ? DenoteNoteCompleteLinkColumn() : DenoteNoteCompleteLinkSuggestions(a:base)
|
||||
endfunction
|
||||
setlocal omnifunc=DenoteNoteCompleteLink
|
||||
" Denote links are of the form 'denote:<note id>'; we require the column.
|
||||
setlocal isfname+=:
|
||||
" Set the function to resolve the filename under the cursor (see |gf|).
|
||||
function DenoteNoteGotoFile()
|
||||
return v:fname !~ '^denote:'
|
||||
\ ? v:fname
|
||||
\ : libdenote#scheme_find(g:denote_directory, v:fname[7:]) ?? v:fname
|
||||
endfunction
|
||||
setlocal includeexpr=DenoteNoteGotoFile()
|
||||
|
||||
" Back references command
|
||||
let b:meta = libdenote#scheme_metadata(expand('%:t'))
|
||||
exe 'command! -buffer DenoteBackReferences DenoteGrep /\<denote:' .. b:meta.id .. '\>/gj'
|
||||
1
after/ftplugin/org.vim
Symbolic link
1
after/ftplugin/org.vim
Symbolic link
@@ -0,0 +1 @@
|
||||
markdown.vim
|
||||
60
after/ftplugin/qf.vim
Normal file
60
after/ftplugin/qf.vim
Normal file
@@ -0,0 +1,60 @@
|
||||
" Run this plugin only if the denote package has been setup
|
||||
if !exists('g:denote_directory')
|
||||
finish
|
||||
endif
|
||||
|
||||
" Load only once per buffer
|
||||
if exists('b:loaded_denote_ftplugin_qf')
|
||||
finish
|
||||
endif
|
||||
let b:loaded_denote_ftplugin_qf = 1
|
||||
|
||||
" This will be called for every location and quickfix, when data is loaded into
|
||||
" the list. This does nothing for such lists that are note 'denote' lists.
|
||||
|
||||
let b:context = getloclist(0, {'context': 1})['context']
|
||||
if type(b:context) != v:t_dict || !has_key(b:context, 'denote')
|
||||
" Clear settings
|
||||
set spell<
|
||||
nmapclear <buffer>
|
||||
finish
|
||||
endif
|
||||
|
||||
setlocal nospell
|
||||
nnoremap <buffer> q :lclose<CR>
|
||||
|
||||
" Reload capability
|
||||
if has_key(b:context, 'gfun')
|
||||
function DenoteLocListReload()
|
||||
let curl = line('.')
|
||||
let Gfun = b:context['gfun']
|
||||
call denote#loclist#jumptowindow()
|
||||
exe 'lclose'
|
||||
call Gfun()
|
||||
exe 'lwindow'
|
||||
exe curl
|
||||
endfunction
|
||||
nnoremap <buffer> <silent> r :call DenoteLocListReload()<CR>
|
||||
endif
|
||||
|
||||
" Denote-list specific configuration
|
||||
if b:context['denote'] == 'list'
|
||||
function OpenDenoteEntry()
|
||||
let l:item = getloclist(0, {'items': 1})['items'][line('.')-1]
|
||||
let l:bufnr = l:item['bufnr']
|
||||
let l:filename = bufname(l:bufnr)
|
||||
call system('open ' .. shellescape(l:filename))
|
||||
endfunction
|
||||
command! -nargs=1 -range -buffer DenoteSetTitle :call denote#notes#settitle(<line1>, <q-args>) | :normal r
|
||||
command! -nargs=1 -range -buffer -complete=custom,denote#completion#tags DenoteTagAdd :call denote#notes#tagmod(<line1>, <line2>, <q-args>, v:true) | :normal r
|
||||
command! -nargs=1 -range -buffer -complete=custom,denote#completion#tags DenoteTagRm :call denote#notes#tagmod(<line1>, <line2>, <q-args>, v:false) | :normal r
|
||||
command! -range -buffer -bang DenoteDelete :call denote#notes#rm(<line1>, <line2>, <bang>0) | :normal r
|
||||
nnoremap <buffer> C :DenoteSetTitle
|
||||
nnoremap <buffer> + :DenoteTagAdd
|
||||
nnoremap <buffer> - :DenoteTagRm
|
||||
nnoremap <buffer> dd :DenoteDelete<CR>r
|
||||
nnoremap <buffer> o :call OpenDenoteEntry()<CR>
|
||||
xnoremap <buffer> + :DenoteTagAdd
|
||||
xnoremap <buffer> - :DenoteTagRm
|
||||
xnoremap <buffer> d :DenoteDelete<CR>
|
||||
endif
|
||||
1
after/ftplugin/text.vim
Symbolic link
1
after/ftplugin/text.vim
Symbolic link
@@ -0,0 +1 @@
|
||||
markdown.vim
|
||||
@@ -3,26 +3,11 @@ function denote#commands#load()
|
||||
if exists('g:denote_commands_loaded') && g:denote_commands_loaded
|
||||
return
|
||||
endif
|
||||
let g:denote_commands_loaded = 1
|
||||
" Register user commands
|
||||
command -nargs=* Denote call denote#notes#list(<q-args>) | lcl | lopen
|
||||
command -nargs=1 -complete=custom,denote#completion#tags DenoteByTag call denote#notes#bytag(<q-args>) | lcl | lopen
|
||||
command -nargs=+ DenoteGrep call denote#notes#grep(<q-args>) | lcl | lopen
|
||||
command -nargs=1 DenoteNew call denote#notes#new(<q-args>)
|
||||
command -nargs=1 -complete=file DenoteCopy call denote#notes#copy(<q-args>)
|
||||
|
||||
" Register auto commands
|
||||
autocmd BufReadPost quickfix call denote#ft#qf()
|
||||
let l:aupat = map(copy(g:denote_note_file_extensions), {_, v -> '*.' .. v})->join(',')
|
||||
exe 'autocmd BufReadPost,BufNewFile ' .. l:aupat .. ' call denote#ft#denote()'
|
||||
|
||||
" Useful key mappings
|
||||
" nnoremap <silent> <Plug>DenoteList :DenoteSearch<CR>:lclose<CR>:lopen<CR>:resize 20<CR>
|
||||
" nnoremap <silent> <Plug>DenoteBackReferences :DenoteBackReferences<CR>:lclose<CR>:lopen<CR>:resize 20<CR>
|
||||
let g:denote_commands_loaded = v:true
|
||||
endfunction
|
||||
|
||||
" Public commands for the denote-list location list
|
||||
function denote#commands#loadll()
|
||||
command DenoteReload call denote#
|
||||
" command -nargs=1 DenoteSetTitle call denote#notes#settitle(<q-args>)
|
||||
endfunction
|
||||
|
||||
@@ -1,59 +1,9 @@
|
||||
" Find the column where the completion starts. This must be between 1 and
|
||||
" col('.'). Denote links are of this form: `denote:<identifier>`.
|
||||
function s:column()
|
||||
" Get the substring from the start of the line until col('.')
|
||||
let l:l = getline('.')[:col('.')]
|
||||
" Take the shortest prefix of a denote link. This may be any of
|
||||
" \<d$
|
||||
" ..
|
||||
" \<denote:$
|
||||
" \<denote:\f$
|
||||
" \<denote:\f\f$
|
||||
" etc.
|
||||
let l:res = l:l->matchstrpos('\<denote:\f*$')
|
||||
if l:res[1] >= 0
|
||||
return l:res[1]
|
||||
endif
|
||||
let l:res = l:l->matchstrpos('\<d\f*$')
|
||||
if l:res[1] == -1
|
||||
return -3
|
||||
endif
|
||||
return 'denote:' =~ '^' .. l:res[0] ? l:res[1] : -3
|
||||
endfunction
|
||||
|
||||
" Return completion items given by the base
|
||||
function s:suggestions(base)
|
||||
let l:prefix = a:base->matchstr('^denote:\zs.*$')
|
||||
let l:flist = glob(g:denote_directory .. '/' .. (l:prefix ? '*' .. l:prefix .. '*' : '*'), 0, v:true)
|
||||
let l:res = []
|
||||
for filename in l:flist
|
||||
let l:noteId = denote#meta#noteIdFromFile(filename)
|
||||
let l:noteTitle = denote#meta#noteTitleFromFile(filename)
|
||||
if l:noteId == v:false || (l:noteId !~ '^' .. l:prefix && l:noteTitle !~ l:prefix)
|
||||
continue
|
||||
endif
|
||||
let l:noteTitle = l:noteTitle ?? '(no title)'
|
||||
let l:noteTags = denote#meta#noteTagsFromFile(filename)
|
||||
call add(l:res, {
|
||||
\ 'word' : 'denote:' .. l:noteId,
|
||||
\ 'abbr' : l:noteTitle,
|
||||
\ 'menu' : l:noteTags->join(', ')
|
||||
\ })
|
||||
endfor
|
||||
return l:res
|
||||
endfunction
|
||||
|
||||
" Completion function for denote links
|
||||
function denote#completion#get(findstart, base)
|
||||
return a:findstart == 1 ? s:column() : s:suggestions(a:base)
|
||||
endfunction
|
||||
|
||||
" Completion function for denote tags
|
||||
function denote#completion#tags(ArgLead, cmdLine, CursorPos)
|
||||
function denote#completion#tags(ArgLead, CmdLine, CursorPos)
|
||||
let l:files=glob(g:denote_directory .. '/*', 0, v:true)
|
||||
let l:tags=[]
|
||||
for f in l:files
|
||||
let l:tags=extend(l:tags, denote#meta#noteTagsFromFile(f))
|
||||
let l:tags=extend(l:tags, libdenote#scheme_metadata(f).tags)
|
||||
endfor
|
||||
return uniq(sort(l:tags))->join("\n")
|
||||
endfunction
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
" Functions to manipulate and query the front matter
|
||||
|
||||
" Helper function to 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
|
||||
|
||||
" Create front matter
|
||||
function denote#frontmatter#new(ft, id, title, tags=[])
|
||||
return a:ft == 'org' ? s:org_new(a:id, a:title, a:tags)
|
||||
\ : a:ft == 'plain' ? s:plain_new(a:id, a:title, a:tags)
|
||||
\ : g:denote_fm_md_type == 'toml' ? s:md_new_toml(a:id, a:title, a:tags)
|
||||
\ : s:md_new_yaml(a:id, a:title, a:tags)
|
||||
endfunction
|
||||
|
||||
" Return the frontmatter of the specified file (as a list)
|
||||
function s:getFrontmatter(filename)
|
||||
let l:ft = fnamemodify(a:filename, ':e')
|
||||
let l:cnt = l:ft == 'org' ? 4
|
||||
\ : l:ft == 'txt' ? 5
|
||||
\ : 6
|
||||
call bufload(a:filename)
|
||||
return getbufline(a:filename, 1, 1 + l:cnt)
|
||||
endfunction
|
||||
|
||||
" Get title from front matter
|
||||
function denote#frontmatter#setTitle(filename, title)
|
||||
" Retrieve front matter (number of lines depends on ft), and replace title
|
||||
let l:frontmatter = s:getFrontmatter(a:filename)
|
||||
call map(l:frontmatter, { _, v -> substitute(v, '^#\?+\?title:\?\s*"\?\zs.\{-\}\ze\"\?$', a:title, '')})
|
||||
call setbufline(a:filename, 1, l:frontmatter)
|
||||
endfunction
|
||||
|
||||
" Set tags in front matter
|
||||
function denote#frontmatter#setTags(filename, tags)
|
||||
let l:frontmatter = s:getFrontmatter(a:filename)
|
||||
let l:tagline = denote#frontmatter#new(fnamemodify(a:filename, ':t'), '', '', a:tags)
|
||||
\ ->filter('v:val =~ "^\\(#+file\\)\\?tags"')[0]
|
||||
call map(l:frontmatter, { _, v -> v =~ '^\(#+file\)\?tags' ? l:tagline : v})
|
||||
call setbufline(a:filename, 1, l:frontmatter)
|
||||
endfunction
|
||||
|
||||
" Prepend frontmatter to file
|
||||
function denote#frontmatter#prepend(filename, id, title, tags=[])
|
||||
let l:frontmatter = denote#frontmatter#new(fnamemodify(a:filename, ':t'), a:id, a:title, a:tags)
|
||||
call appendbufline(a:filename, 0, l:frontmatter)
|
||||
endfunction
|
||||
@@ -1,57 +0,0 @@
|
||||
" Go to file command |gf| adjustments
|
||||
" This resolves denote links. The function has access to the variable v:fname,
|
||||
" which corresponds to the filename under the cursor.
|
||||
function s:gotofile()
|
||||
return v:fname !~ '^denote:'
|
||||
\ ? v:fname
|
||||
\ : denote#meta#fileFromNoteId(v:fname[7:]) ?? v:fname
|
||||
endfunction
|
||||
|
||||
" Denote note specifics
|
||||
function denote#ft#denote()
|
||||
if expand('%:p:h') != g:denote_directory
|
||||
return
|
||||
endif
|
||||
" Link completion
|
||||
setlocal omnifunc=denote#completion#get
|
||||
" Denote links are of the form 'denote:<note id>'; we require the column.
|
||||
setlocal isfname+=:
|
||||
" Set the function to resolve the filename under the cursor (see |gf|).
|
||||
setlocal includeexpr=s:gotofile()
|
||||
" Back references command
|
||||
let l:noteid = denote#meta#noteIdFromFile(expand('%:t'))
|
||||
exe 'command! -buffer DenoteBackReferences DenoteGrep /\<denote:' .. l:noteid .. '\>/gj'
|
||||
endfunction
|
||||
|
||||
" Location-list specifics
|
||||
"
|
||||
" This will be called for every location and quickfix, when data is loaded into
|
||||
" the list. This does nothing for such lists that are note 'denote' lists.
|
||||
function denote#ft#qf()
|
||||
let l:context = getloclist(0, {'context': 1})['context']
|
||||
if type(l:context) != v:t_dict || !has_key(l:context, 'denote')
|
||||
" Clear settings
|
||||
set spell<
|
||||
nmapclear <buffer>
|
||||
return
|
||||
endif
|
||||
setlocal nospell
|
||||
nnoremap <buffer> q :lclose<CR>
|
||||
if has_key(l:context, 'gfun')
|
||||
nnoremap <buffer> <silent> r :call denote#loclist#reload()<CR>
|
||||
endif
|
||||
" Denote-list specific configuration
|
||||
if l:context['denote'] == 'list'
|
||||
command! -nargs=1 -range -buffer DenoteSetTitle :call denote#notes#settitle(<line1>, <q-args>)
|
||||
command! -nargs=1 -range -buffer -complete=custom,denote#completion#tags DenoteTagAdd :call denote#notes#tagmod(<line1>, <line2>, <q-args>, v:true)
|
||||
command! -nargs=1 -range -buffer -complete=custom,denote#completion#tags DenoteTagRm :call denote#notes#tagmod(<line1>, <line2>, <q-args>, v:false)
|
||||
command! -range -buffer -bang DenoteDelete :call denote#notes#rm(<line1>, <line2>, <bang>0)
|
||||
nnoremap <buffer> C :DenoteSetTitle
|
||||
nnoremap <buffer> + :DenoteTagAdd
|
||||
nnoremap <buffer> - :DenoteTagRm
|
||||
nnoremap <buffer> dd :DenoteDelete<CR>r
|
||||
xnoremap <buffer> + :DenoteTagAdd
|
||||
xnoremap <buffer> - :DenoteTagRm
|
||||
xnoremap <buffer> d :DenoteDelete<CR>
|
||||
endif
|
||||
endfunction
|
||||
@@ -5,7 +5,7 @@ endfunction
|
||||
|
||||
" Local helper function to retrieve and format the title.
|
||||
function s:titleFromBuf(buf)
|
||||
let l:name=denote#meta#noteTitleFromFile(bufname(a:buf))
|
||||
let l:name = libdenote#scheme_metadata(bufname(a:buf)).title
|
||||
return strchars(l:name, 1) <= g:denote_loc_title_columns
|
||||
\ ? printf('%' .. g:denote_loc_title_columns .. 's', l:name)
|
||||
\ : printf('%.' .. (g:denote_loc_title_columns - 1) .. 's', l:name) .. '…'
|
||||
@@ -20,7 +20,7 @@ function s:formatText(text, col, width)
|
||||
endfunction
|
||||
|
||||
" This modifies the location list for pretty display.
|
||||
function denote#loclist#textReferences(info)
|
||||
function s:textReferences(info)
|
||||
let l:items=getloclist(a:info.winid)
|
||||
let l:l=[]
|
||||
let l:width=winwidth(0) - g:denote_loc_title_columns - 19
|
||||
@@ -37,13 +37,13 @@ function denote#loclist#textReferences(info)
|
||||
endfunction
|
||||
|
||||
" This modifies the location list for pretty display.
|
||||
function denote#loclist#textNoteList(info)
|
||||
function s:textNoteList(info)
|
||||
let l:items=getloclist(a:info.winid)
|
||||
let l:l=[]
|
||||
for idx in range(a:info.start_idx - 1, a:info.end_idx - 1)
|
||||
let l:e=l:items[idx]
|
||||
let l:name=s:titleFromBuf(l:e.bufnr)
|
||||
let l:ntags=denote#meta#noteTagsFromFile(bufname(l:e.bufnr))->join()
|
||||
let l:ntags = libdenote#scheme_metadata(bufname(l:e.bufnr)).tags->join()
|
||||
call add(l:l, l:name .. ' | ' .. l:ntags)
|
||||
endfor
|
||||
return l:l
|
||||
@@ -56,7 +56,7 @@ function denote#loclist#fill(title, files, Gfun)
|
||||
" Set properties
|
||||
call setloclist(0, [], 'r',
|
||||
\ {'title': a:title,
|
||||
\ 'quickfixtextfunc' : 'denote#loclist#textNoteList',
|
||||
\ 'quickfixtextfunc' : 's:textNoteList',
|
||||
\ 'context' : {'denote': 'list', 'gfun': a:Gfun}})
|
||||
" Populate
|
||||
let l:notes=[]
|
||||
@@ -73,7 +73,7 @@ endfunction
|
||||
function denote#loclist#setgrep(title, Gfun)
|
||||
call setloclist(0, [], 'r',
|
||||
\ {'title': a:title,
|
||||
\ 'quickfixtextfunc' : 'denote#loclist#textReferences',
|
||||
\ 'quickfixtextfunc' : 's:textReferences',
|
||||
\ 'context' : {'denote': 'grep', 'gfun': a:Gfun}})
|
||||
endfunction
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
" Return the filename of the note with id `noteId`. If the file is note found,
|
||||
" then v:false is returned.
|
||||
function denote#meta#fileFromNoteId(noteId)
|
||||
" According to the file-naming scheme, the note id is prefixed with '@@'. If
|
||||
" the note id appears at the beginning of the filename, 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(g:denote_directory .. '/*' .. a:noteId .. '*', 0, v:true)
|
||||
\ ->filter('v:val->split("/")[-1] =~ "' .. a:noteId .. '\\(==\\|--\\|__\\|\\.\\)"')
|
||||
\ ->filter('v:val->split("/")[-1] =~ "^' .. a:noteId .. '\\|@@' .. a:noteId .. '"')
|
||||
return empty(l:files) ? v:false : l:files[0]
|
||||
endfunction
|
||||
|
||||
" Return the note id from the filename. On failure, v:false is returned.
|
||||
function denote#meta#noteIdFromFile(file)
|
||||
return a:file->fnamemodify(':t')->matchstr('@@\zs.\{-\}\ze\(==\|--\|__\|\..\)')
|
||||
\ ?? a:file->fnamemodify(':t')->matchstr('^.\{-\}\ze\(==\|--\|__\|\..\)')
|
||||
\ ?? v:false
|
||||
endfunction
|
||||
|
||||
" Return the note title from the filename.
|
||||
function denote#meta#noteTitleFromFile(file)
|
||||
return a:file->fnamemodify(':t')
|
||||
\ ->matchstr('--\zs.\{-\}\ze\(==\|@@\|__\|\..\)')
|
||||
\ ->substitute('-', ' ', 'g')
|
||||
endfunction
|
||||
|
||||
" Return the note tags from the filename as a list.
|
||||
function denote#meta#noteTagsFromFile(file)
|
||||
return a:file->fnamemodify(':t')
|
||||
\ ->matchstr('__\zs.\{-\}\ze\(==\|@@\|--\|\..\)')
|
||||
\ ->split('_')
|
||||
endfunction
|
||||
|
||||
" Return the note signature from the filename.
|
||||
function denote#meta#noteSignatureFromFile(file)
|
||||
return a:file->fnamemodify(':t')
|
||||
\ ->matchstr('==\zs.\{-\}\ze\(==\|@@\|__\|\..\)')
|
||||
endfunction
|
||||
|
||||
" Identifier creation
|
||||
function denote#meta#identifier_generate()
|
||||
return exists('*strftime')
|
||||
\ ? strftime('%Y%m%dT%H%M%S')
|
||||
\ : rand()
|
||||
endfunction
|
||||
|
||||
" 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
|
||||
|
||||
" Function that returns the filename give all metadata. The filename is
|
||||
" returned with the path to the denote directory.
|
||||
function denote#meta#filename(ext, identifier, title='', tags=[], signature='')
|
||||
let l:f = g:denote_directory .. '/' .. s:santizefnpart(a:identifier)
|
||||
let l:f ..= len(a:signature) > 0 ? ('==' .. s:santizefnpart(a:signature)) : ''
|
||||
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
|
||||
@@ -1,3 +1,90 @@
|
||||
" Script-local function that retrieves the front-matter of a file.
|
||||
function s:getfrontmatter(filename)
|
||||
let l:ext = fnamemodify(a:filename, ':e')
|
||||
if index(g:denote_note_file_extensions, l:ext) == -1
|
||||
return []
|
||||
endif
|
||||
" The following code aims to be as robust as possible. This is achieved by
|
||||
" parsing each file-type (extension) separately. A common signature of the
|
||||
" front matter is the empty line that separates the front matter from the
|
||||
" rest of the text. As fail safe, we assume that the front matter is shorter
|
||||
" than l:max lines.
|
||||
" Markdown:
|
||||
" - The first line is either '---' or '+++'.
|
||||
" - The last line is the same as the first line.
|
||||
" - All entries (apart first and last lines) are of the form '^\w\+:\?'.
|
||||
" - Contains the entry '^identifier'.
|
||||
" Org:
|
||||
" - All lines start with '^#+\w\+:\s*'.
|
||||
" - Contains the entry '^#identifier:
|
||||
" Text:
|
||||
" - The last line is '^-\+$.
|
||||
" - All lines start with '^\w\+:\s*.
|
||||
" - Contains the entry '^identifier:'
|
||||
"
|
||||
" Note: getbufline() returns an empty list if no more lines are available.
|
||||
let l:max = 50
|
||||
call bufload(a:filename)
|
||||
let l:fmt = []
|
||||
let l:lnr = 1
|
||||
let l:identifier_seen = v:false
|
||||
if l:ext == 'md'
|
||||
let l:separator = getbufline(a:filename, l:lnr)[0]
|
||||
if index(['---', '+++'], l:separator) == -1
|
||||
return []
|
||||
endif
|
||||
call add(l:fmt, l:separator)
|
||||
let l:lnr += 1
|
||||
while l:lnr < l:max
|
||||
let l:line = getbufline(a:filename, l:lnr)[0]
|
||||
if l:line == l:separator
|
||||
call add(l:fmt, l:line)
|
||||
return l:identifier_seen && getbufline(a:filename, l:lnr + 1)[0] == '' ? l:fmt : []
|
||||
endif
|
||||
if l:line !~ '^\w\+:\?'
|
||||
return []
|
||||
endif
|
||||
call add(l:fmt, l:line)
|
||||
if l:line =~ '^identifier'
|
||||
let l:identifier_seen = v:true
|
||||
endif
|
||||
let l:lnr += 1
|
||||
endwhile
|
||||
elseif l:ext == 'org'
|
||||
while l:lnr < l:max
|
||||
let l:line = getbufline(a:filename, l:lnr)[0]
|
||||
if l:line == ''
|
||||
return l:identifier_seen ? l:fmt : []
|
||||
endif
|
||||
if l:line !~ '^#+\w\+:'
|
||||
return []
|
||||
endif
|
||||
call add(l:fmt, l:line)
|
||||
if l:line =~ '^#+identifier:'
|
||||
let l:identifier_seen = v:true
|
||||
endif
|
||||
let l:lnr += 1
|
||||
endwhile
|
||||
elseif l:ext == 'txt'
|
||||
while l:lnr < l:max
|
||||
let l:line = getbufline(a:filename, l:lnr)[0]
|
||||
if l:line =~ '^-\+$'
|
||||
call add(l:fmt, l:line)
|
||||
return l:identifier_seen && getbufline(a:filename, l:lnr + 1)[0] == '' ? l:fmt : []
|
||||
endif
|
||||
if l:line !~ '^\w\+:'
|
||||
return []
|
||||
endif
|
||||
call add(l:fmt, l:line)
|
||||
if l:line =~ '^identifier:'
|
||||
let l:identifier_seen = v:true
|
||||
endif
|
||||
let l:lnr += 1
|
||||
endwhile
|
||||
endif
|
||||
return []
|
||||
endfunction
|
||||
|
||||
" Put all notes of the given tag to the location list. The search argument may be
|
||||
" empty. For improving search, white spaces are replaced by the * |wildcard|.
|
||||
function denote#notes#list(search)
|
||||
@@ -27,15 +114,15 @@ function denote#notes#grep(re)
|
||||
endfunction
|
||||
|
||||
" This creates a new denote entry with the given title and of the given
|
||||
" filetype. The title may be empty.
|
||||
function denote#notes#new(title, ft=g:denote_new_ft)
|
||||
" extension. The title may be empty.
|
||||
function denote#notes#new(title, ext=g:denote_new_ext)
|
||||
let l:identifier = g:Denote_identifier_fun()
|
||||
let l:fn = denote#meta#filename(a:ft, l:identifier, a:title)
|
||||
let l:fn = g:denote_directory .. '/' .. libdenote#scheme_filename(a:ext, l:identifier, a:title)
|
||||
" Jump to window this location list belongs to
|
||||
call denote#loclist#jumptowindow()
|
||||
" Open file and write front matter
|
||||
exe 'edit ' l:fn
|
||||
call setline(1, denote#frontmatter#new(a:ft, l:identifier, a:title))
|
||||
call setline(1, libdenote#fm_gen(a:ext, l:identifier, a:title, [], g:denote_fm_md_type))
|
||||
endfunction
|
||||
|
||||
" Function to set the title of the selected entry
|
||||
@@ -48,22 +135,32 @@ function denote#notes#settitle(linenr, title)
|
||||
let l:item = l:items[a:linenr-1]
|
||||
let l:bufnr = l:item['bufnr']
|
||||
let l:filename = bufname(l:bufnr)
|
||||
let l:noteid = denote#meta#noteIdFromFile(l:filename)
|
||||
let l:notetags = denote#meta#noteTagsFromFile(l:filename)
|
||||
let l:notesignature = denote#meta#noteSignatureFromFile(l:filename)
|
||||
let l:oldtitle = denote#meta#noteTitleFromFile(l:filename)
|
||||
let l:meta = libdenote#scheme_metadata(l:filename)
|
||||
let l:ext = fnamemodify(l:filename, ':e')
|
||||
let l:newfilename = denote#meta#filename(l:ext, l:noteid, a:title, l:notetags, l:notesignature)
|
||||
let l:newfilename = g:denote_directory .. '/' .. libdenote#scheme_filename(l:ext, l:meta.id, a:title, l:meta.tags, l:meta.sig)
|
||||
" If this note has a front matter, we rewrite the front matter and rename the
|
||||
" file. Otherwise, we rename the file only.
|
||||
if index(g:denote_note_file_extensions, l:ext) >= 0
|
||||
" Handle front matter
|
||||
call denote#frontmatter#setTitle(l:filename, a:title)
|
||||
call bufload(l:filename)
|
||||
let l:frontmatter = s:getfrontmatter(l:filename)
|
||||
if empty(l:frontmatter)
|
||||
" Write fresh front matter
|
||||
let l:frontmatter = libdenote#fm_gen(l:ext, l:meta.id, l:meta.title, l:meta.tags, g:denote_fm_md_type)
|
||||
call add(l:frontmatter, '')
|
||||
call appendbufline(l:filename, 0, l:frontmatter)
|
||||
else
|
||||
" Modify front matter
|
||||
let l:frontmatter = libdenote#fm_alter(l:frontmatter, {'title': a:title})
|
||||
call setbufline(l:filename, 1, l:frontmatter)
|
||||
endif
|
||||
let curl = line('.')
|
||||
call denote#loclist#jumptowindow()
|
||||
exe l:bufnr .. 'buf'
|
||||
exe 'silent buf ' .. l:bufnr
|
||||
exe 'silent file ' .. l:newfilename
|
||||
exe 'silent w'
|
||||
exe 'lopen'
|
||||
exe curl
|
||||
if fnamemodify(l:filename, ':t') != fnamemodify(l:newfilename, ':t')
|
||||
call delete(l:filename)
|
||||
endif
|
||||
@@ -87,32 +184,42 @@ function denote#notes#tagmod(line1, line2, tag, add)
|
||||
let l:item = l:items[i-1]
|
||||
let l:bufnr = l:item['bufnr']
|
||||
let l:filename = bufname(l:bufnr)
|
||||
let l:noteid = denote#meta#noteIdFromFile(l:filename)
|
||||
let l:notetags = denote#meta#noteTagsFromFile(l:filename)
|
||||
let l:idx = index(l:notetags, a:tag)
|
||||
let l:meta = libdenote#scheme_metadata(l:filename)
|
||||
let l:idx = index(l:meta.tags, a:tag)
|
||||
if a:add
|
||||
if l:idx >= 0
|
||||
continue
|
||||
endif
|
||||
call add(l:notetags, a:tag)
|
||||
call add(l:meta.tags, a:tag)
|
||||
else
|
||||
if l:idx == -1
|
||||
continue
|
||||
endif
|
||||
call remove(l:notetags, l:idx)
|
||||
call remove(l:meta.tags, l:idx)
|
||||
endif
|
||||
let l:notesignature = denote#meta#noteSignatureFromFile(l:filename)
|
||||
let l:title = denote#meta#noteTitleFromFile(l:filename)
|
||||
let l:ext = fnamemodify(l:filename, ':e')
|
||||
let l:newfilename = denote#meta#filename(l:ext, l:noteid, l:title, l:notetags, l:notesignature)
|
||||
let l:newfilename = g:denote_directory .. '/' .. libdenote#scheme_filename(l:ext, l:meta.id, l:meta.title, l:meta.tags, l:meta.sig)
|
||||
if index(g:denote_note_file_extensions, l:ext) >= 0
|
||||
" Handle front matter
|
||||
call denote#frontmatter#setTags(l:filename, l:notetags)
|
||||
call bufload(l:filename)
|
||||
let l:frontmatter = s:getfrontmatter(l:filename)
|
||||
if empty(l:frontmatter)
|
||||
" Write fresh front matter
|
||||
let l:frontmatter = libdenote#fm_gen(l:ext, l:meta.id, l:meta.title, l:meta.tags, g:denote_fm_md_type)
|
||||
call add(l:frontmatter, '')
|
||||
call appendbufline(l:filename, 0, l:frontmatter)
|
||||
else
|
||||
" Modify front matter
|
||||
let l:frontmatter = libdenote#fm_alter(l:frontmatter, {'tags': l:meta.tags})
|
||||
call setbufline(l:filename, 1, l:frontmatter)
|
||||
endif
|
||||
let curl = line('.')
|
||||
call denote#loclist#jumptowindow()
|
||||
exe l:bufnr .. 'buf'
|
||||
exe 'silent buf ' .. l:bufnr
|
||||
exe 'silent file ' .. l:newfilename
|
||||
exe 'silent w'
|
||||
exe 'lopen'
|
||||
exe curl
|
||||
call delete(l:filename)
|
||||
else
|
||||
if fnamemodify(l:filename, ':t') == fnamemodify(l:newfilename, ':t')
|
||||
@@ -134,20 +241,19 @@ function denote#notes#copy(origfile)
|
||||
let l:title = fnamemodify(a:origfile, ':t:r')
|
||||
let l:ext = fnamemodify(a:origfile, ':e')
|
||||
let l:identifier = g:Denote_identifier_fun()
|
||||
let l:filename = denote#meta#filename(l:ext, l:identifier, l:title)
|
||||
let l:filename = g:denote_directory .. '/' .. libdenote#scheme_filename(l:ext, l:identifier, l:title)
|
||||
call system('cp ' .. shellescape(a:origfile) .. ' ' .. shellescape(l:filename))
|
||||
" Write front matter, if this is supported
|
||||
if index(g:denote_note_file_extensions, l:ext) >= 0
|
||||
call denote#loclist#jumptowindow()
|
||||
exe 'edit ' .. l:filename
|
||||
call denote#frontmatter#prepend(l:filename, l:identifier, l:title)
|
||||
exe 'edit ' .. fnameescape(l:filename)
|
||||
call appendbufline(l:filename, 0, libdenote#fm_gen(l:ext, l:identifier, l:title), [], g:denote_fm_md_type)
|
||||
exe 'w'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Delete notes from denote directory
|
||||
function denote#notes#rm(line1, line2, bang)
|
||||
echom "bang set to:"..a:bang
|
||||
let l:items = getloclist(0, {'items': 1})['items']
|
||||
if empty(l:items)
|
||||
return
|
||||
@@ -156,7 +262,7 @@ function denote#notes#rm(line1, line2, bang)
|
||||
let l:item = l:items[i-1]
|
||||
let l:bufnr = l:item['bufnr']
|
||||
let l:filename = bufname(l:bufnr)
|
||||
let l:title = denote#meta#noteTitleFromFile(l:filename)
|
||||
let l:title = libdenote#scheme_metadata(l:filename).title
|
||||
if a:bang == v:false
|
||||
let l:answer = confirm('Are you sure you want to delete the note "' .. l:title .. '"?', "&Yes\n&No\n", 2, 'Question')
|
||||
if l:answer != 1
|
||||
|
||||
202
autoload/libdenote.vim
Normal file
202
autoload/libdenote.vim
Normal file
@@ -0,0 +1,202 @@
|
||||
" 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. The argument a:mod
|
||||
" of type dict specifies the fields to be updated. If a:mod contains the key
|
||||
" 'date', then the date field will be updated. If it contains the key 'id',
|
||||
" then the identifier will be updated with the value a:mod.id. Similar for the
|
||||
" title (key 'title'), and the tags (key 'tags'). For the tags, the value type
|
||||
" is a list of strings.
|
||||
"
|
||||
" @argument a:fm list[string] List of strings describes a frontmatter
|
||||
" @argument a:mod dict Dictionary to control updates
|
||||
" @return: List of strings with a line-per item that describes the front matter
|
||||
function libdenote#fm_alter(fm, mod)
|
||||
let l:md_type = a:fm[0] == '+++' ? 'toml' : 'yaml'
|
||||
let l:ext = { n -> n == 4 ? 'org' : n == 5 ? 'txt' : 'md'}(len(a:fm))
|
||||
let l:new = copy(a:fm)
|
||||
let l:repl = libdenote#fm_gen(l:ext,
|
||||
\ has_key(a:mod, 'id') ? a:mod.id : '',
|
||||
\ has_key(a:mod, 'title') ? a:mod.title : '',
|
||||
\ has_key(a:mod, 'tags') ? a:mod.tags : [],
|
||||
\ g:denote_fm_md_type)
|
||||
if has_key(a:mod, 'date')
|
||||
let l:ididx = indexof(l:repl, 'v:val =~ "^\\(#+\\)\\?date"')
|
||||
call map(l:new, {_, v -> v =~ "^\\(#+\\)\\?date" ? l:repl[l:ididx] : v})
|
||||
endif
|
||||
if has_key(a:mod, 'id')
|
||||
let l:ididx = indexof(l:repl, 'v:val =~ "^\\(#+\\)\\?identifier"')
|
||||
call map(l:new, {_, v -> v =~ "^\\(#+\\)\\?identifier" ? l:repl[l:ididx] : v})
|
||||
endif
|
||||
if has_key(a:mod, 'title')
|
||||
let l:titleidx = indexof(l:repl, 'v:val =~ "^\\(#+\\)\\?title"')
|
||||
call map(l:new, {_, v -> v =~ "^\\(#+\\)\\?title" ? l:repl[l:titleidx] : v})
|
||||
endif
|
||||
if has_key(a:mod, 'tags')
|
||||
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
|
||||
177
doc/denote.txt
177
doc/denote.txt
@@ -1,8 +1,14 @@
|
||||
*denote.txt* Handling denote entries – the vim way
|
||||
|
||||
Last change: 2026 Feb 28
|
||||
Last change: 2026 Mar 4
|
||||
|
||||
This is the documentation for the denote plugin.
|
||||
This is the documentation for the denote package. This package also introduces
|
||||
the |libdenote| plugin in the file autoload/libdenote.vim, which may be of
|
||||
independent interest. If this package is loaded automatically, but you prefer
|
||||
to opt-out, then add this line to your vimrc:
|
||||
>
|
||||
let g:loaded_denote = 1
|
||||
<
|
||||
|
||||
==============================================================================
|
||||
CONTENTS *vim-denote* *denote*
|
||||
@@ -15,6 +21,12 @@ CONTENTS *vim-denote* *denote*
|
||||
3. Settings |denote-settings|
|
||||
4. Keys |denote-keys|
|
||||
|
||||
Appendices
|
||||
A. The libdenote plugin |libdenote|
|
||||
A.1 File-naming functions |libdentoe-filenaming|
|
||||
A.2 Front-matter functions |libdenote-frontmatter|
|
||||
B. Package API |denote-api|
|
||||
|
||||
==============================================================================
|
||||
INTRODUCTION *denote-intro*
|
||||
|
||||
@@ -41,6 +53,7 @@ prefix of a denote link. This completion also works with titles: by invoking
|
||||
omni completion after typing "denote:foo", denote links are completed for
|
||||
entries containing "foo" in the title.
|
||||
|
||||
|
||||
==============================================================================
|
||||
COMMANDS *denote-commands*
|
||||
|
||||
@@ -244,5 +257,165 @@ C Change the title of the selected entry.
|
||||
dd Delete the selected entry.
|
||||
{Visual}d Delete the visually selected entries.
|
||||
|
||||
*denote-list-o*
|
||||
o Open the file using the system "open" command.
|
||||
|
||||
|
||||
==============================================================================
|
||||
APPENDIX A - THE LIBDENOTE PLUGIN *libdenote*
|
||||
|
||||
The present package has as constituent the libdenote plugin for basic
|
||||
denote-centered operations. That plugin is given by the file
|
||||
autoload/libdenote.vim. Each function in this plugin is pure, i.e., produces
|
||||
no side effects. There are two classes of functions: functions concerning the
|
||||
file-naming scheme (prefixed with scheme_, see |libdenote-filenaming|) and
|
||||
functions concerning the front matter (prefixed with fm_, see
|
||||
|libdenote-frontmatter|).
|
||||
|
||||
*libdenote-filenaming*
|
||||
File-naming functions~
|
||||
*libdenote#scheme_idgen()*
|
||||
libdenote#scheme_idgen()
|
||||
This function generates a new identifier. If the function |strftime()|
|
||||
is available, than strftime(%Y%m%dT%H%M%S) is used, otherwise a random
|
||||
identifier is generated using |rand()|.
|
||||
|
||||
*libdenote#scheme_filename()*
|
||||
libdenote#scheme_filename({ext}, {id}, {title}, {tags}, {sig})
|
||||
With this, the file name of the denote entry with the given metadata
|
||||
is returned. The parameter {ext} describes the extension of the file
|
||||
is is one of 'md', 'txt', or 'org'. The parameter {id} is the denote
|
||||
identifier, possibly generated via |libdenote#scheme_idgen()|. The
|
||||
{title} parameter is optional and describes the title of the note. By
|
||||
default, it is set to the empty string ''. The {tags} parameter is
|
||||
optional as well, is a list of tags associated to the note. The
|
||||
default value is the empty list []. Also, the {sig} parameter is
|
||||
optional, and used to describe the signature of the entry.
|
||||
|
||||
*libdenote#scheme_metadata()*
|
||||
libdenote#scheme_metadata({filename})
|
||||
This function returns the metadata of the file given by {filename}.
|
||||
This parameter may describe the path to the file, or the tail only.
|
||||
The returned |dict| has the keys 'id' for the identifier, 'title' for
|
||||
the title, 'tags' for the list of tags, and 'sig' for the signature.
|
||||
|
||||
*libdenote-frontmatter*
|
||||
Front-matter functions~
|
||||
*libdenote#fm_gen()*
|
||||
libdenote#fm_gen({ext}, {id}, {title}, {tags}, {mdtype})
|
||||
This function returns the list of lines for the front matter that
|
||||
stores the given metadata. The paramter {ext} is the extension of the
|
||||
file, the parameter {id} the identifier, the parameter {title}, the
|
||||
title of the note, and {tags} the optional parameter as a list of tags
|
||||
associated to the note. The argument {mdtype} descries the format to
|
||||
be used in markdown. For markdown files (extension 'md'), two formats
|
||||
are possible: 'yaml' and 'toml'. The former format is used per
|
||||
default.
|
||||
|
||||
*libdenote#fm_alter()*
|
||||
libdenote#fm_alter({fm}, {mod})
|
||||
Similar to |libdenote#fm_gen()|, this function returns a list
|
||||
containing the lines of a front matter. In contrast to the above
|
||||
function, this functions takes a front matter {fm} as base (again,
|
||||
given as list of lines), and updates the fields specified by the
|
||||
|dict| {mod}. If {mod} contains the key "date", then the date string
|
||||
will be updated. If {mod} contains the key "id", then the identifier
|
||||
will be updated with the value a:mod.id. Similarly, the keys "title"
|
||||
and "tags" are used to update the title and tag list of the front
|
||||
matter.
|
||||
|
||||
|
||||
==============================================================================
|
||||
APPENDIX B - PACKAGE API *denote-api*
|
||||
|
||||
Here, we list and briefly describe all functions that come with this package.
|
||||
The aim of providing this information is to keep vim-denote hackable.
|
||||
|
||||
*denote#commands#load()*
|
||||
denote#commands#load()
|
||||
This function initializes the user commands (see, |denote-commands|)
|
||||
that are globally available.
|
||||
|
||||
*denote#completion#tags()*
|
||||
denote#completion#tags({ArgLead}, {CmdLine}, {CursorPos})
|
||||
This is the autocompletion function for tag arguments in the user
|
||||
commands |:DenoteByTag|, |:DenoteTagAdd|, and |:DenoteTagRm|.
|
||||
|
||||
*denote#loclist#clear()*
|
||||
denote#loclist#clear()
|
||||
With this, the location list is cleared.
|
||||
|
||||
*denote#loclist*fill()*
|
||||
denote#loclist*fill({title}, {files}, {Gfun})
|
||||
This function populates the location list with denote entries. The
|
||||
{title} argument specifies the title of the location list. The {files}
|
||||
argument is a list of filenames of denote entries. Finally, the {Gfun}
|
||||
argument is a Funcref to the function that reloads the location list —
|
||||
this is the function (with all arguments set) that invoked this call
|
||||
to |denote#loclist#fill()|.
|
||||
|
||||
*denote#loclist#setgrep()*
|
||||
denote#loclist#setgrep({title}, {Gfun})
|
||||
This function makes the location list fit for showing the result of
|
||||
|:DenoteGrep|. The {title} and {Gfun} arguments are as in
|
||||
|denote#loclist#fill()|.
|
||||
|
||||
*denote#loclist#reload()*
|
||||
denote#loclist#reload()
|
||||
This function reruns the location-list generating function (see {Gfun}
|
||||
in |denote#loclist#fill()|.
|
||||
|
||||
*denote#loclist#jumptowindow()*
|
||||
denote#loclist#jumptowindow()
|
||||
With this, the cursor is moved from the location list to the window
|
||||
the location list belongs to.
|
||||
|
||||
*denote#notes#list()*
|
||||
denote#notes#list({search})
|
||||
This function searches for denote entries that contain {search} in the
|
||||
path, and displays the results in the location list. The command
|
||||
|:Denote| is bound to this function.
|
||||
|
||||
*denote#notes#bytag()*
|
||||
denote#notes#bytag({tag})
|
||||
This is similar to |denote#notes#list()|, but only entries with the
|
||||
tag {tag} are put to the location list. This is used by the command
|
||||
|:DenoteByTag|.
|
||||
|
||||
*denote#notes#grep()*
|
||||
denote#notes#grep({re})
|
||||
This is the function used by |:DenoteGrep| and |:DenoteBackReferences|
|
||||
to search for patterns within the denote files.
|
||||
|
||||
*denote#notes#new()*
|
||||
denote#notes#new({title}, {ext})
|
||||
This function is used by |:DenoteNew|. Here, {title} is the title of
|
||||
the new note, and {ext} the file extension. This second argument is
|
||||
optional and has the default value |g:denote_new_ext|.
|
||||
|
||||
*denote#notes#settitle()*
|
||||
denote#notes#settitle({linenr}, {title})
|
||||
With this, the title of the entry on line {linenr} of the location
|
||||
list is set to {title}. This is used by |:DenoteSetTitle|.
|
||||
|
||||
*denote#notes#tagmod()*
|
||||
denote#notes#tagmod({line1}, {line2}, {tag}, {add})
|
||||
This function modifies the tags of the denote entries from line
|
||||
{line1} to line {line2} of the location list. In each of these
|
||||
entries, the tag {tag} is added if {add} is 1, and removed if {add} is
|
||||
0.
|
||||
|
||||
*denote#notes#copy()*
|
||||
denote#notes#copy({origfile})
|
||||
This copies the file {origfile} to the denote directory, and renames
|
||||
the copy to make it denote compatible. This function is used by
|
||||
|:DenoteCopy|.
|
||||
|
||||
*denote#notes#rm()*
|
||||
denote#notes#rm({line1}, {line2}, {bang})
|
||||
With this, the files that correspond to the entries from line {line1}
|
||||
to line {line2} of the location list are deleted. If the {bang} is set
|
||||
to 1, then no confirmation will be asked. Otherwise, the user is asked
|
||||
to confirm every deletion. This function is used by |:DenoteDelete|.
|
||||
|
||||
vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
" Global configurations
|
||||
if exists('g:loaded_denote')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_denote = 1
|
||||
|
||||
" List of denote directories
|
||||
if !exists('g:denote_directories')
|
||||
let g:denote_directories = []
|
||||
endif
|
||||
call map(g:denote_directories, {_, d -> fnamemodify(d, ':p')})
|
||||
" If only one directory has been specified, use that as denote directory
|
||||
" if len(g:denote_directories) == 1
|
||||
" let g:denote_directory = g:denote_directories[0]
|
||||
" endif
|
||||
|
||||
" Restrict basic operations to these files
|
||||
if !exists('g:denote_note_file_extensions')
|
||||
@@ -21,8 +21,8 @@ if !exists('g:denote_loc_title_columns')
|
||||
endif
|
||||
|
||||
" Default filetype for newly created denote entries
|
||||
if !exists('g:denote_new_ft')
|
||||
let g:denote_new_ft = 'md'
|
||||
if !exists('g:denote_new_ext')
|
||||
let g:denote_new_ext = 'md'
|
||||
endif
|
||||
|
||||
" Default front-matter type for markdown notes, may be one of 'yaml' or 'toml'
|
||||
@@ -33,7 +33,7 @@ endif
|
||||
" By using the following global variable, the user may specify a custom
|
||||
" function for creating identifiers.
|
||||
if !exists('g:Denote_identifier_fun')
|
||||
let g:Denote_identifier_fun = function('denote#meta#identifier_generate')
|
||||
let g:Denote_identifier_fun = function('libdenote#scheme_idgen')
|
||||
endif
|
||||
|
||||
" Transform full path into canonical form WITH trailing '/'
|
||||
|
||||
Reference in New Issue
Block a user