commit 4e06bec5ef6e154a5b2970efdad131d048664808 Author: Ämin Baumeler Date: Tue Feb 17 16:18:18 2026 +0100 Initial commit diff --git a/after/ftplugin/markdown.vim b/after/ftplugin/markdown.vim new file mode 100644 index 0000000..25229ea --- /dev/null +++ b/after/ftplugin/markdown.vim @@ -0,0 +1,16 @@ +" Go to file command |gf| adjustments {{{1 +" This resolves denote links in markdown and org files. The function has access +" to the variable v:fname, which corresponds to the filename under the cursor. +function s:DenoteGotoFile() + return v:fname !~ "^denote:" + \ ? v:fname + \ : denote#meta#fileFromNoteId(v:fname[7:]) ?? v:fname +endfunction + +" Denote links are of the form 'denote:'; we require the column. +setlocal isfname+=: +" Set the function to resolve the filename under the cursor (see |gf|). +setlocal includeexpr=s:DenoteGotoFile() + +" Back references command {{{1 +command! DenoteBackReferences :call denote#loclist#references(denote#meta#noteIdFromFile(expand("%"))) diff --git a/after/ftplugin/org.vim b/after/ftplugin/org.vim new file mode 120000 index 0000000..4d7d231 --- /dev/null +++ b/after/ftplugin/org.vim @@ -0,0 +1 @@ +markdown.vim \ No newline at end of file diff --git a/after/ftplugin/qf.vim b/after/ftplugin/qf.vim new file mode 100644 index 0000000..cb40974 --- /dev/null +++ b/after/ftplugin/qf.vim @@ -0,0 +1,8 @@ +" Disable spell checking and set up custom mappings. +setlocal nospell + +if getwininfo(win_getid())[0].loclist == 0 + finish +endif +nnoremap q :lclose +nnoremap dd :lremove diff --git a/autoload/denote/loclist.vim b/autoload/denote/loclist.vim new file mode 100644 index 0000000..6e6172f --- /dev/null +++ b/autoload/denote/loclist.vim @@ -0,0 +1,62 @@ +" Clear location list +function denote#loclist#clear() + call setloclist(0, [], ' ') +endfunction + +" Local helper function to retrieve and format the title. +function s:titleFromBuf(buf) + let name=denote#meta#noteTitleFromFile(bufname(a:buf)) + return strchars(name, 1) <= g:denote_loc_title_columns + \ ? printf('%' .. g:denote_loc_title_columns .. 's', name) + \ : printf('%.' .. (g:denote_loc_title_columns - 1) .. 's', name) .. '…' +endfunction + +" Local helper function to truncate text with match at the given column as a +" string of at most `width` columns. +function s:formatText(text, col, width) + return a:col <= a:width + \ ? a:text + \ : a:text[a:col-float2nr(a:width*0.3):] +endfunction + +" This modifies the location list for pretty display. +function denote#loclist#textReferences(info) + let items=getloclist(a:info.winid) + let l=[] + let width=winwidth(0) - g:denote_loc_title_columns - 19 + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + let e=items[idx] + let name=s:titleFromBuf(e.bufnr) + let lnum=printf('%5d', e.lnum) + let col=printf('%3d', e.col) + call add(l, name .. + \ ' | ' .. lnum .. ' col ' .. col .. + \ ' | ' .. s:formatText(items[idx].text, col, width)) + endfor + return l +endfunction + +" This modifies the location list for pretty display. +function denote#loclist#textNoteList(info) + let items=getloclist(a:info.winid) + let l=[] + for idx in range(a:info.start_idx - 1, a:info.end_idx - 1) + let e=items[idx] + let name=s:titleFromBuf(e.bufnr) + let ntags=denote#meta#noteTagsFromFile(bufname(e.bufnr))->join() + call add(l, name .. ' | ' .. ntags) +endfor + return l +endfunction + +" Load all references to the given note into the location list. +function denote#loclist#references(noteId) + " Populate location list + execute "lvimgrep /\\/gj *" + " Adjust location list: set title and specify display function + let file=denote#meta#fileFromNoteId(a:noteId) + let noteTitle=denote#meta#noteTitleFromFile(file) + call setloclist(0, [], 'r', + \ {'title': 'References to ' .. noteTitle .. ' (' .. a:noteId .. ')', + \ 'quickfixtextfunc' : 'denote#loclist#textReferences'}) +endfunction diff --git a/autoload/denote/meta.vim b/autoload/denote/meta.vim new file mode 100644 index 0000000..07a2d7a --- /dev/null +++ b/autoload/denote/meta.vim @@ -0,0 +1,36 @@ +" 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 files = glob("*" .. a:noteId .. "*", 0, v:true) + \ ->filter('v:val =~ "' .. a:noteId .. '\\(==\\|--\\|__\\|\\.\\)"') + \ ->filter('v:val =~ "^' .. a:noteId .. '\\|@@' .. a:noteId .. '"') + return empty(files) ? v:false : files[0] +endfunction + +" Return the note id from the filename. On failure, v:false is returned. +function denote#meta#noteIdFromFile(filename) + return a:filename->matchstr("@@\\zs.\\{-\\}\\ze\\(==\\|--\\|__\\|\\..\\)") + \ ?? a:filename->matchstr("^.\\{-\\}\\ze\\(==\\|--\\|__\\|\\..\\)") + \ ?? v:false +endfunction + +" Return the note title from the filename. On failure, v:false is returned. +function denote#meta#noteTitleFromFile(filename) + return a:filename->matchstr("--\\zs.\\{-\\}\\ze\\(==\\|@@\\|__\\|\\..\\)")->substitute("-", " ", "g") + \ ?? v:false +endfunction + +" Return the note tags from the filename as a list. +function denote#meta#noteTagsFromFile(filename) + return a:filename->matchstr("__\\zs.\\{-\\}\\ze\\(==\\|@@\\|--\\|\\..\\)")->split("_") + \ ?? [] +endfunction diff --git a/plugin/denote.vim b/plugin/denote.vim new file mode 100644 index 0000000..ad4bcb4 --- /dev/null +++ b/plugin/denote.vim @@ -0,0 +1,92 @@ +" Global configurations {{{1 + +" Restrict basic operations to these files: +if !exists('g:denote_note_file_extensions') + let g:denote_note_file_extensions=['md', 'org'] +endif + +" Number of columns used for the title in the location window. +if !exists('g:denote_loc_title_columns') + let g:denote_loc_title_columns=40 +endif + +" Local functions {{{1 +" Put all notes of the given tag to the location list. The tag argument is +" mandatory. +function s:DenoteNotesByTag(tag) + " Clear location list + call denote#loclist#clear() + " Find files + let files = glob("*_" .. a:tag .. "*", 0, v:true)->filter('v:val =~ "_' .. a:tag .. '\\(==\\|@@\\|__\\|_\\|\\.\\)"') + " Populate location list + let locTitle="Denote notes: " .. a:tag + call setloclist(0, [], 'r', + \ {'title': locTitle, + \ 'quickfixtextfunc' : 'denote#loclist#textNoteList'}) + let notes=[] + for f in files + call add(notes, { + \ 'filename' : f, + \ 'lnum' : 1 + \ }) + endfor + call setloclist(0, notes, 'a') +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 s:DenoteNotes(search) + let s = substitute(" " .. a:search .. " ", " ", "*", "g") + " Clear location list + call denote#loclist#clear() + " Find files + let files = glob(s, 0, v:true) + " Populate location list + let locTitle="Denote notes search:" .. a:search + call setloclist(0, [], 'r', + \ {'title': locTitle, + \ 'quickfixtextfunc' : 'denote#loclist#textNoteList'}) + let notes=[] + for f in files + call add(notes, { + \ 'filename' : f, + \ 'lnum' : 1 + \ }) + endfor + call setloclist(0, notes, 'a') +endfunction + +" This function implements the similar functionality of :vimgrep, but for +" denote notes. +function s:DenoteGrep(search) + " Clear location list + call denote#loclist#clear() + " Grep all greppable files + let fpat=map(copy(g:denote_note_file_extensions), {_, e -> "*." .. e})->join() + execute "lvimgrep " .. a:search .. " " .. fpat + " Adjust location list: set title and specify display function + call setloclist(0, [], 'r', + \ {'title': 'Denote grep: ' .. a:search, + \ 'quickfixtextfunc' : 'denote#loclist#textReferences'}) + lclose + lopen +endfunction + +" This function is used to autocomplete the tags in user commands. +function s:tagList(ArgLead, cmdLine, CursorPos) + let files=glob("*", 0, v:true) + let tags=[] + for f in files + let tags=extend(tags, denote#meta#noteTagsFromFile(f)) + endfor + return uniq(sort(tags))->join("\n") +endfunction + +" Public commands and key mappings {{{1 +command -nargs=* Denote :call DenoteNotes() +command -nargs=1 -complete=custom,tagList DenoteTag :call DenoteNotesByTag() +command -nargs=+ DenoteGrep :call DenoteGrep() + +" Useful key mappings +nnoremap DenoteList :Denote:lclose:lopen:resize 20 +nnoremap DenoteBackReferences :DenoteBackReferences:lclose:lopen:resize 20