" Global configurations " 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') let g:denote_note_file_extensions=['md', 'org', 'txt'] 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 " Default filetype for newly created denote entries if !exists('g:denote_new_ft') let g:denote_new_ft='md' endif " Default front-matter type for markdown notes, may be one of 'yaml' or 'toml' if !exists('g:denote_fm_md_type') let g:denote_fm_md_type='yaml' 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='' endif " Transform full path into canonical form WITH trailing '/' function s:canonicalFullPath(path) let l:parts = split(a:path, '/') let l:result = [] for part in l:parts if part == '.' || part == '' continue elseif part == '..' if len(l:result) > 0 call remove(l:result, len(l:result)-1) endif else call add(l:result, part) endif endfor return empty(l:result) ? '/' : '/' .. join(l:result, '/') .. '/' endfunction " Compute the relative path from start to target. Both arguments are given as " full paths. function s:relativePath(start, target) let l:a = s:canonicalFullPath(a:start) let l:b = s:canonicalFullPath(a:target) " Simple cases first: both paths are the same, or the target is l:a subpath " from the start. if l:a == l:b[:strlen(l:a)-1] return l:b[strlen(l:a):] endif " Now, we need to go back. If the following match fails, then we need to go " back all the way let l:l = matchstrpos(l:a .. l:b, '^\(/.*\)/\zs.*/\ze\1') return l:l[1] == -1 \ ? substitute(l:a[1:-2], '[^/]\+', '..', 'g') .. l:b \ : substitute(l:l[0], '[^/]\+', '..', 'g') .. l:b[l:l[1]:] endfunction " Complete all paths to the denote directories. This functions completes the " paths to the configured directories. function s:denoteDirectoryCompletion(ArgLead, CmdLine, CursorPos) let l:res = [] let l:bang = match(a:CmdLine, '^\S\+!') >= 0 let l:argleaddir = isdirectory(expand(a:ArgLead)) ? a:ArgLead : matchstr(a:ArgLead, '^.*/') if strlen(l:argleaddir) == 0 let l:argleaddir = '~' endif if l:argleaddir !~ '/$' let l:argleaddir = l:argleaddir .. '/' endif if l:bang for ldir in (glob(expand(l:argleaddir) .. '/*', v:true, v:true)->filter('isdirectory(v:val)')) call add(l:res, l:argleaddir .. fnamemodify(ldir, ':t')) endfor else let l:leaddir = s:canonicalFullPath(fnamemodify(l:argleaddir, ':p')) for ldir in g:denote_directories if l:leaddir == ldir[:strlen(l:leaddir)-1] call add(l:res, l:argleaddir .. s:relativePath(l:leaddir, ldir)) endif endfor endif return l:res->join("\n") endfunction " Setup denote plugin, i.e., " - set the denote directory " - register user commands " - register auto commands function s:setup(dir) " Set the denote directory let l:tmp = s:canonicalFullPath(fnamemodify(a:dir, ':p'))[:-2] if !isdirectory(l:tmp) echohl WarningMsg echom "The specified argument is not a directory (" .. a:dir .. ")" return endif let g:denote_directory = l:tmp " Register user commands and auto commands call denote#commands#load() endfunction command -nargs=1 -bang -complete=custom,s:denoteDirectoryCompletion DenoteDirectory call s:setup()