" vim:tabstop=2:shiftwidth=2:expandtab:foldmethod=marker:textwidth=79 " Vimwiki autoload plugin file " Author: Maxim Kim " Home: http://code.google.com/p/vimwiki/ if exists("g:loaded_vimwiki_auto") || &cp finish endif let g:loaded_vimwiki_auto = 1 " MISC helper functions {{{ " s:normalize_path function! s:normalize_path(path) "{{{ let g:VimwikiLog.normalize_path += 1 "XXX " resolve doesn't work quite right with symlinks ended with / or \ return resolve(expand(substitute(a:path, '[/\\]\+$', '', ''))).'/' endfunction "}}} " s:path_html function! s:path_html(idx) "{{{ let path_html = VimwikiGet('path_html', a:idx) if !empty(path_html) return path_html else let g:VimwikiLog.path_html += 1 "XXX let path = VimwikiGet('path', a:idx) return substitute(path, '[/\\]\+$', '', '').'_html/' endif endfunction "}}} function! vimwiki#base#get_known_extensions() " {{{ " Getting all extensions that different wikis could have let extensions = {} for wiki in g:vimwiki_list if has_key(wiki, 'ext') let extensions[wiki.ext] = 1 else let extensions['.wiki'] = 1 endif endfor " append map g:vimwiki_ext2syntax for ext in keys(g:vimwiki_ext2syntax) let extensions[ext] = 1 endfor return keys(extensions) endfunction " }}} function! vimwiki#base#get_known_syntaxes() " {{{ " Getting all syntaxes that different wikis could have let syntaxes = {} let syntaxes['default'] = 1 for wiki in g:vimwiki_list if has_key(wiki, 'syntax') let syntaxes[wiki.syntax] = 1 endif endfor " append map g:vimwiki_ext2syntax for syn in values(g:vimwiki_ext2syntax) let syntaxes[syn] = 1 endfor return keys(syntaxes) endfunction " }}} " }}} " vimwiki#base#apply_wiki_options function! vimwiki#base#apply_wiki_options(options) " {{{ Update the current " wiki using the options dictionary for kk in keys(a:options) let g:vimwiki_list[g:vimwiki_current_idx][kk] = a:options[kk] endfor call vimwiki#base#validate_wiki_options(g:vimwiki_current_idx) call vimwiki#base#setup_buffer_state(g:vimwiki_current_idx) endfunction " }}} " vimwiki#base#read_wiki_options function! vimwiki#base#read_wiki_options(check) " {{{ Attempt to read wiki " options from the current page's directory, or its ancesters. If a file " named vimwiki.vimrc is found, which declares a wiki-options dictionary " named g:local_wiki, a message alerts the user that an update has been " found and may be applied. If the argument check=1, the user is queried " before applying the update to the current wiki's option. " Save global vimwiki options ... after all, the global list is often " initialized for the first time in vimrc files, and we don't want to " overwrite !! (not to mention all the other globals ...) let l:vimwiki_list = deepcopy(g:vimwiki_list, 1) " if a:check > 1 call vimwiki#base#print_wiki_state() echo " \n" endif " let g:local_wiki = {} let done = 0 " ... start the wild-goose chase! for invsubdir in ['.', '..', '../..', '../../..'] " other names are possible, but most vimrc files will cause grief! for nm in ['vimwiki.vimrc'] " TODO: use an alternate strategy, instead of source, to read options if done continue endif " let local_wiki_options_filename = expand('%:p:h').'/'.invsubdir.'/'.nm if !filereadable(local_wiki_options_filename) continue endif " echo "\nFound file : ".local_wiki_options_filename let query = "Vimwiki: Check for options in this file [Y]es/[n]o? " if a:check > 0 && (tolower(input(query)) !~ "y") continue endif " try execute 'source '.local_wiki_options_filename catch endtry if empty(g:local_wiki) continue endif " if a:check > 0 echo "\n\nFound wiki options\n g:local_wiki = ".string(g:local_wiki) let query = "Vimwiki: Apply these options [Y]es/[n]o? " if tolower(input(query)) !~ "y" let g:local_wiki = {} continue endif endif " " restore global list " - this prevents corruption by g:vimwiki_list in options_file let g:vimwiki_list = deepcopy(l:vimwiki_list, 1) " call vimwiki#base#apply_wiki_options(g:local_wiki) let done = 1 endfor endfor if !done " " restore global list, if no local options were found " - this prevents corruption by g:vimwiki_list in options_file let g:vimwiki_list = deepcopy(l:vimwiki_list, 1) " endif if a:check > 1 echo " \n " if done call vimwiki#base#print_wiki_state() else echo "Vimwiki: No options were applied." endif endif endfunction " }}} " vimwiki#base#validate_wiki_options function! vimwiki#base#validate_wiki_options(idx) " {{{ Validate wiki options " Only call this function *before* opening a wiki page. " " XXX: It's too early to update global / buffer variables, because they are " still needed in their existing state for s:setup_buffer_leave() "" let g:vimwiki_current_idx = a:idx " update normalized path & path_html call VimwikiSet('path', s:normalize_path(VimwikiGet('path', a:idx)), a:idx) call VimwikiSet('path_html', s:normalize_path(s:path_html(a:idx)), a:idx) call VimwikiSet('template_path', \ s:normalize_path(VimwikiGet('template_path', a:idx)), a:idx) call VimwikiSet('diary_rel_path', \ s:normalize_path(VimwikiGet('diary_rel_path', a:idx)), a:idx) " XXX: It's too early to update global / buffer variables, because they are " still needed in their existing state for s:setup_buffer_leave() "" call vimwiki#base#cache_buffer_state() endfunction " }}} " vimwiki#base#setup_buffer_state function! vimwiki#base#setup_buffer_state(idx) " {{{ Init page-specific variables " Only call this function *after* opening a wiki page. if a:idx < 0 return endif let g:vimwiki_current_idx = a:idx " The following state depends on the current active wiki page let subdir = vimwiki#base#current_subdir(a:idx) call VimwikiSet('subdir', subdir, a:idx) call VimwikiSet('invsubdir', vimwiki#base#invsubdir(subdir), a:idx) call VimwikiSet('url', vimwiki#html#get_wikifile_url(expand('%:p')), a:idx) " update cache call vimwiki#base#cache_buffer_state() endfunction " }}} " vimwiki#base#cache_buffer_state function! vimwiki#base#cache_buffer_state() "{{{ if !exists('g:vimwiki_current_idx') && g:vimwiki_debug echo "[Vimwiki Internal Error]: Missing global state variable: 'g:vimwiki_current_idx'" endif let b:vimwiki_idx = g:vimwiki_current_idx endfunction "}}} " vimwiki#base#recall_buffer_state function! vimwiki#base#recall_buffer_state() "{{{ if !exists('b:vimwiki_idx') if g:vimwiki_debug echo "[Vimwiki Internal Error]: Missing buffer state variable: 'b:vimwiki_idx'" endif return 0 else let g:vimwiki_current_idx = b:vimwiki_idx return 1 endif endfunction " }}} " vimwiki#base#print_wiki_state function! vimwiki#base#print_wiki_state() "{{{ print wiki options " and buffer state variables let g_width = 18 let b_width = 18 echo "- Wiki Options (idx=".g:vimwiki_current_idx.") -" for kk in VimwikiGetOptionNames() echo " '".kk."': ".repeat(' ', g_width-len(kk)).string(VimwikiGet(kk)) endfor if !exists('b:vimwiki_list') return endif echo "- Cached Variables -" for kk in keys(b:vimwiki_list) echo " '".kk."': ".repeat(' ', b_width-len(kk)).string(b:vimwiki_list[kk]) endfor endfunction "}}} " vimwiki#base#mkdir " If the optional argument 'confirm' == 1 is provided, " vimwiki#base#mkdir will ask before creating a directory function! vimwiki#base#mkdir(path, ...) "{{{ let path = expand(a:path) if !isdirectory(path) && exists("*mkdir") let path = vimwiki#u#chomp_slash(path) if vimwiki#u#is_windows() && !empty(g:vimwiki_w32_dir_enc) let path = iconv(path, &enc, g:vimwiki_w32_dir_enc) endif if a:0 && a:1 && tolower(input("Vimwiki: Make new directory: ".path."\n [Y]es/[n]o? ")) !~ "y" return 0 endif call mkdir(path, "p") endif return 1 endfunction " }}} " vimwiki#base#file_pattern function! vimwiki#base#file_pattern(files) "{{{ Get search regex from glob() " string. Aim to support *all* special characters, forcing the user to choose " names that are compatible with any external restrictions that they " encounter (e.g. filesystem, wiki conventions, other syntaxes, ...). " See: http://code.google.com/p/vimwiki/issues/detail?id=316 " Change / to [/\\] to allow "Windows paths" " TODO: boundary cases ... " e.g. "File$", "^File", "Fi]le", "Fi[le", "Fi\le", "Fi/le" " XXX: (remove my comment if agreed) Maxim: with \V (very nomagic) boundary " cases works for 1 and 2. " 3, 4, 5 is not highlighted as links thus wouldn't be highlighted. " 6 is a regular vimwiki link with subdirectory... " let pattern = vimwiki#base#branched_pattern(a:files,"\n") return '\V'.pattern.'\m' endfunction "}}} " vimwiki#base#branched_pattern function! vimwiki#base#branched_pattern(string,separator) "{{{ get search regex " from a string-list; separators assumed at start and end as well let pattern = substitute(a:string, a:separator, '\\|','g') let pattern = substitute(pattern, '\%^\\|', '\\%(','') let pattern = substitute(pattern,'\\|\%$', '\\)','') return pattern endfunction "}}} " vimwiki#base#subdir "FIXME TODO slow and faulty function! vimwiki#base#subdir(path, filename)"{{{ let g:VimwikiLog.subdir += 1 "XXX let path = a:path " ensure that we are not fooled by a symbolic link "FIXME if we are not "fooled", we end up in a completely different wiki? let filename = resolve(a:filename) let idx = 0 "FIXME this can terminate in the middle of a path component! while path[idx] ==? filename[idx] let idx = idx + 1 endwhile let p = split(strpart(filename, idx), '[/\\]') let res = join(p[:-2], '/') if len(res) > 0 let res = res.'/' endif return res endfunction "}}} " vimwiki#base#current_subdir function! vimwiki#base#current_subdir(idx)"{{{ return vimwiki#base#subdir(VimwikiGet('path', a:idx), expand('%:p')) endfunction"}}} " vimwiki#base#invsubdir function! vimwiki#base#invsubdir(subdir) " {{{ return substitute(a:subdir, '[^/\.]\+/', '../', 'g') endfunction " }}} " vimwiki#base#resolve_scheme function! vimwiki#base#resolve_scheme(lnk, as_html) " {{{ Resolve scheme " if link is schemeless add wikiN: scheme let lnk = a:lnk let is_schemeless = lnk !~ g:vimwiki_rxSchemeUrl let lnk = (is_schemeless ? 'wiki'.g:vimwiki_current_idx.':'.lnk : lnk) " Get scheme let scheme = matchstr(lnk, g:vimwiki_rxSchemeUrlMatchScheme) " Get link (without scheme) let lnk = matchstr(lnk, g:vimwiki_rxSchemeUrlMatchUrl) let path = '' let subdir = '' let ext = '' let idx = -1 " do nothing if scheme is unknown to vimwiki if !(scheme =~ 'wiki.*' || scheme =~ 'diary' || scheme =~ 'local' \ || scheme =~ 'file') return [idx, scheme, path, subdir, lnk, ext, scheme.':'.lnk] endif " scheme behaviors if scheme =~ 'wiki\d\+' let idx = eval(matchstr(scheme, '\D\+\zs\d\+\ze')) if idx < 0 || idx >= len(g:vimwiki_list) echom 'Vimwiki Error: Numbered scheme refers to a non-existent wiki!' return [idx,'','','','','',''] else if idx != g:vimwiki_current_idx call vimwiki#base#validate_wiki_options(idx) endif endif if a:as_html if idx == g:vimwiki_current_idx let path = VimwikiGet('path_html') else let path = VimwikiGet('path_html', idx) endif else if idx == g:vimwiki_current_idx let path = VimwikiGet('path') else let path = VimwikiGet('path', idx) endif endif " For Issue 310. Otherwise current subdir is used for another wiki. if idx == g:vimwiki_current_idx let subdir = VimwikiGet('subdir') else let subdir = "" endif if a:as_html let ext = '.html' else if idx == g:vimwiki_current_idx let ext = VimwikiGet('ext') else let ext = VimwikiGet('ext', idx) endif endif " default link for directories if vimwiki#u#is_link_to_dir(lnk) let ext = (g:vimwiki_dir_link != '' ? g:vimwiki_dir_link. ext : '') endif elseif scheme =~ 'diary' if a:as_html " use cached value (save time when converting diary index!) let path = VimwikiGet('invsubdir') let ext = '.html' else let path = VimwikiGet('path') let ext = VimwikiGet('ext') endif let subdir = VimwikiGet('diary_rel_path') elseif scheme =~ 'local' " revisiting the 'lcd'-bug ... let path = VimwikiGet('path') let subdir = VimwikiGet('subdir') if a:as_html " prepend browser-specific file: scheme let path = 'file://'.fnamemodify(path, ":p") endif elseif scheme =~ 'file' " RM repeated leading "/"'s within a link let lnk = substitute(lnk, '^/*', '/', '') " convert "/~..." into "~..." for fnamemodify let lnk = substitute(lnk, '^/\~', '\~', '') " convert /C: to C: (or fnamemodify(...":p:h") interpret it as C:\C: if vimwiki#u#is_windows() let lnk = substitute(lnk, '^/\ze[[:alpha:]]:', '', '') endif if a:as_html " prepend browser-specific file: scheme let path = 'file://'.fnamemodify(lnk, ":p:h").'/' else let path = fnamemodify(lnk, ":p:h").'/' endif let lnk = fnamemodify(lnk, ":p:t") let subdir = '' endif " construct url from parts if is_schemeless && a:as_html let scheme = '' let url = lnk.ext else let url = path.subdir.lnk.ext endif " result return [idx, scheme, path, subdir, lnk, ext, url] endfunction "}}} " vimwiki#base#system_open_link function! vimwiki#base#system_open_link(url) "{{{ " handlers function! s:win32_handler(url) "http://vim.wikia.com/wiki/Opening_current_Vim_file_in_your_Windows_browser execute 'silent ! start "Title" /B ' . shellescape(a:url, 1) endfunction function! s:macunix_handler(url) execute '!open ' . shellescape(a:url, 1) endfunction function! s:linux_handler(url) call system('xdg-open ' . shellescape(a:url, 1).' &') endfunction let success = 0 try if vimwiki#u#is_windows() call s:win32_handler(a:url) return elseif has("macunix") call s:macunix_handler(a:url) return else call s:linux_handler(a:url) return endif endtry echomsg 'Default Vimwiki link handler was unable to open the HTML file!' endfunction "}}} " vimwiki#base#open_link function! vimwiki#base#open_link(cmd, link, ...) "{{{ let [idx, scheme, path, subdir, lnk, ext, url] = \ vimwiki#base#resolve_scheme(a:link, 0) if url == '' if g:vimwiki_debug echom 'open_link: idx='.idx.', scheme='.scheme.', path='.path.', subdir='.subdir.', lnk='.lnk.', ext='.ext.', url='.url endif echom 'Vimwiki Error: Unable to resolve link!' return endif let update_prev_link = ( \ scheme == '' || \ scheme =~ 'wiki' || \ scheme =~ 'diary' ? 1 : 0) let use_system_open = ( \ scheme == '' || \ scheme =~ 'wiki' || \ scheme =~ 'diary' ? 0 : 1) let vimwiki_prev_link = [] " update previous link for wiki pages if update_prev_link if a:0 let vimwiki_prev_link = [a:1, []] elseif &ft == 'vimwiki' let vimwiki_prev_link = [expand('%:p'), getpos('.')] endif endif " open/edit if g:vimwiki_debug echom 'open_link: idx='.idx.', scheme='.scheme.', path='.path.', subdir='.subdir.', lnk='.lnk.', ext='.ext.', url='.url endif if use_system_open call vimwiki#base#system_open_link(url) else call vimwiki#base#edit_file(a:cmd, url, \ vimwiki_prev_link, update_prev_link) if idx != g:vimwiki_current_idx " this call to setup_buffer_state may not be necessary call vimwiki#base#setup_buffer_state(idx) endif endif endfunction " }}} " vimwiki#base#generate_links function! vimwiki#base#generate_links() "{{{only get links from the current dir " change to the directory of the current file let orig_pwd = getcwd() lcd! %:h " all path are relative to the current file's location let globlinks = glob('*'.VimwikiGet('ext'),1)."\n" " remove extensions let globlinks = substitute(globlinks, '\'.VimwikiGet('ext').'\ze\n', '', 'g') " restore the original working directory exe 'lcd! '.orig_pwd " We don't want link to itself. XXX Why ??? " let cur_link = expand('%:t:r') " call filter(links, 'v:val != cur_link') let links = split(globlinks,"\n") call append(line('$'), substitute(g:vimwiki_rxH1_Template, '__Header__', 'Generated Links', '')) call sort(links) let bullet = repeat(' ', vimwiki#lst#get_list_margin()). \ vimwiki#lst#default_symbol().' ' for link in links call append(line('$'), bullet. \ substitute(g:vimwiki_WikiLinkTemplate1, '__LinkUrl__', '\='."'".link."'", '')) endfor endfunction " }}} " vimwiki#base#goto function! vimwiki#base#goto(key) "{{{ call vimwiki#base#edit_file(':e', \ VimwikiGet('path'). \ a:key. \ VimwikiGet('ext')) endfunction "}}} " vimwiki#base#backlinks function! vimwiki#base#backlinks() "{{{ execute 'lvimgrep "\%(^\|[[:blank:][:punct:]]\)'. \ expand("%:t:r"). \ '\([[:blank:][:punct:]]\|$\)\C" '. \ escape(VimwikiGet('path').'**/*'.VimwikiGet('ext'), ' ') endfunction "}}} " vimwiki#base#get_links function! vimwiki#base#get_links(pat) "{{{ return string-list for files " in the current wiki matching the pattern "pat" " search all wiki files (or directories) in wiki 'path' and its subdirs. let time1 = reltime() " start the clock " XXX: " if maxhi = 1 and ww before loading any vimwiki file " cached 'subdir' is not set up try let subdir = VimwikiGet('subdir') " FIXED: was previously converting './' to '../' let invsubdir = VimwikiGet('invsubdir') catch let subdir = '' let invsubdir = '' endtry " if current wiki is temporary -- was added by an arbitrary wiki file then do " not search wiki files in subdirectories. Or it would hang the system if " wiki file was created in $HOME or C:/ dirs. if VimwikiGet('temp') let search_dirs = '' else let search_dirs = '**/' endif " let globlinks = "\n".glob(VimwikiGet('path').search_dirs.a:pat,1)."\n" "save pwd, do lcd %:h, restore old pwd; getcwd() " change to the directory of the current file let orig_pwd = getcwd() " calling from other than vimwiki file let path_base = vimwiki#u#path_norm(vimwiki#u#chomp_slash(VimwikiGet('path'))) let path_file = vimwiki#u#path_norm(vimwiki#u#chomp_slash(expand('%:p:h'))) if vimwiki#u#path_common_pfx(path_file, path_base) != path_base exe 'lcd! '.path_base else lcd! %:p:h endif " all path are relative to the current file's location let globlinks = "\n".glob(invsubdir.search_dirs.a:pat,1)."\n" " remove extensions let globlinks = substitute(globlinks,'\'.VimwikiGet('ext').'\ze\n', '', 'g') " standardize path separators on Windows let globlinks = substitute(globlinks,'\\', '/', 'g') " shortening those paths ../../dir1/dir2/ that can be shortened " first for the current directory, then for parent etc. let sp_rx = '\n\zs' . invsubdir . subdir . '\ze' for i in range(len(invsubdir)/3) "XXX multibyte? let globlinks = substitute(globlinks, sp_rx, '', 'g') let sp_rx = substitute(sp_rx,'\\zs../','../\\zs','') let sp_rx = substitute(sp_rx,'[^/]\+/\\ze','\\ze','') endfor " for directories: add ./ (instead of now empty) and invsubdir (if distinct) if a:pat == '*/' let globlinks = substitute(globlinks, "\n\n", "\n./\n",'') if invsubdir != '' let globlinks .= invsubdir."\n" else let globlinks .= "./\n" endif endif " restore the original working directory exe 'lcd! '.orig_pwd let time2 = vimwiki#u#time(time1) call VimwikiLog_extend('timing',['base:afterglob('.len(split(globlinks, '\n')).')',time2]) return globlinks endfunction "}}} " vimwiki#base#edit_file function! vimwiki#base#edit_file(command, filename, ...) "{{{ " XXX: Should we allow * in filenames!? " Maxim: It is allowed, escaping here is for vim to be able to open files " which have that symbols. " Try to remove * from escaping and open&save : " [[testBLAfile]]... " then " [[test*file]]... " you'll have E77: Too many file names let fname = escape(a:filename, '% *|#') let dir = fnamemodify(a:filename, ":p:h") if vimwiki#base#mkdir(dir, 1) execute a:command.' '.fname else echom ' ' echom 'Vimwiki: Unable to edit file in non-existent directory: '.dir endif " save previous link " a:1 -- previous vimwiki link to save " a:2 -- should we update previous link if a:0 && a:2 && len(a:1) > 0 let b:vimwiki_prev_link = a:1 endif endfunction " }}} " vimwiki#base#search_word function! vimwiki#base#search_word(wikiRx, cmd) "{{{ let match_line = search(a:wikiRx, 's'.a:cmd) if match_line == 0 echomsg 'vimwiki: Wiki link not found.' endif endfunction " }}} " vimwiki#base#matchstr_at_cursor " Returns part of the line that matches wikiRX at cursor function! vimwiki#base#matchstr_at_cursor(wikiRX) "{{{ let col = col('.') - 1 let line = getline('.') let ebeg = -1 let cont = match(line, a:wikiRX, 0) while (ebeg >= 0 || (0 <= cont) && (cont <= col)) let contn = matchend(line, a:wikiRX, cont) if (cont <= col) && (col < contn) let ebeg = match(line, a:wikiRX, cont) let elen = contn - ebeg break else let cont = match(line, a:wikiRX, contn) endif endwh if ebeg >= 0 return strpart(line, ebeg, elen) else return "" endif endf "}}} " vimwiki#base#replacestr_at_cursor function! vimwiki#base#replacestr_at_cursor(wikiRX, sub) "{{{ let col = col('.') - 1 let line = getline('.') let ebeg = -1 let cont = match(line, a:wikiRX, 0) while (ebeg >= 0 || (0 <= cont) && (cont <= col)) let contn = matchend(line, a:wikiRX, cont) if (cont <= col) && (col < contn) let ebeg = match(line, a:wikiRX, cont) let elen = contn - ebeg break else let cont = match(line, a:wikiRX, contn) endif endwh if ebeg >= 0 " TODO: There might be problems with Unicode chars... let newline = strpart(line, 0, ebeg).a:sub.strpart(line, ebeg+elen) call setline(line('.'), newline) endif endf "}}} " s:print_wiki_list function! s:print_wiki_list() "{{{ let idx = 0 while idx < len(g:vimwiki_list) if idx == g:vimwiki_current_idx let sep = ' * ' echohl PmenuSel else let sep = ' ' echohl None endif echo (idx + 1).sep.VimwikiGet('path', idx) let idx += 1 endwhile echohl None endfunction " }}} " s:update_wiki_link function! s:update_wiki_link(fname, old, new) " {{{ echo "Updating links in ".a:fname let has_updates = 0 let dest = [] for line in readfile(a:fname) if !has_updates && match(line, a:old) != -1 let has_updates = 1 endif " XXX: any other characters to escape!? call add(dest, substitute(line, a:old, escape(a:new, "&"), "g")) endfor " add exception handling... if has_updates call rename(a:fname, a:fname.'#vimwiki_upd#') call writefile(dest, a:fname) call delete(a:fname.'#vimwiki_upd#') endif endfunction " }}} " s:update_wiki_links_dir function! s:update_wiki_links_dir(dir, old_fname, new_fname) " {{{ let old_fname = substitute(a:old_fname, '[/\\]', '[/\\\\]', 'g') let new_fname = a:new_fname let old_fname_r = old_fname let new_fname_r = new_fname let old_fname_r = vimwiki#base#apply_template(g:vimwiki_WikiLinkTemplate1, \ '\zs'.old_fname.'\ze', '.*', ''). \ '\|'. vimwiki#base#apply_template(g:vimwiki_WikiLinkTemplate2, \ '\zs'.old_fname.'\ze', '.*', '') let files = split(glob(VimwikiGet('path').a:dir.'*'.VimwikiGet('ext')), '\n') for fname in files call s:update_wiki_link(fname, old_fname_r, new_fname_r) endfor endfunction " }}} " s:tail_name function! s:tail_name(fname) "{{{ let result = substitute(a:fname, ":", "__colon__", "g") let result = fnamemodify(result, ":t:r") let result = substitute(result, "__colon__", ":", "g") return result endfunction "}}} " s:update_wiki_links function! s:update_wiki_links(old_fname, new_fname) " {{{ let old_fname = s:tail_name(a:old_fname) let new_fname = s:tail_name(a:new_fname) let subdirs = split(a:old_fname, '[/\\]')[: -2] " TODO: Use Dictionary here... let dirs_keys = [''] let dirs_vals = [''] if len(subdirs) > 0 let dirs_keys = [''] let dirs_vals = [join(subdirs, '/').'/'] let idx = 0 while idx < len(subdirs) - 1 call add(dirs_keys, join(subdirs[: idx], '/').'/') call add(dirs_vals, join(subdirs[idx+1 :], '/').'/') let idx = idx + 1 endwhile call add(dirs_keys,join(subdirs, '/').'/') call add(dirs_vals, '') endif let idx = 0 while idx < len(dirs_keys) let dir = dirs_keys[idx] let new_dir = dirs_vals[idx] call s:update_wiki_links_dir(dir, \ new_dir.old_fname, new_dir.new_fname) let idx = idx + 1 endwhile endfunction " }}} " s:get_wiki_buffers function! s:get_wiki_buffers() "{{{ let blist = [] let bcount = 1 while bcount<=bufnr("$") if bufexists(bcount) let bname = fnamemodify(bufname(bcount), ":p") if bname =~ VimwikiGet('ext')."$" let bitem = [bname, getbufvar(bname, "vimwiki_prev_link")] call add(blist, bitem) endif endif let bcount = bcount + 1 endwhile return blist endfunction " }}} " s:open_wiki_buffer function! s:open_wiki_buffer(item) "{{{ call vimwiki#base#edit_file(':e', a:item[0]) if !empty(a:item[1]) call setbufvar(a:item[0], "vimwiki_prev_link", a:item[1]) endif endfunction " }}} " vimwiki#base#nested_syntax function! vimwiki#base#nested_syntax(filetype, start, end, textSnipHl) abort "{{{ " From http://vim.wikia.com/wiki/VimTip857 let ft=toupper(a:filetype) let group='textGroup'.ft if exists('b:current_syntax') let s:current_syntax=b:current_syntax " Remove current syntax definition, as some syntax files (e.g. cpp.vim) " do nothing if b:current_syntax is defined. unlet b:current_syntax endif " Some syntax files set up iskeyword which might scratch vimwiki a bit. " Let us save and restore it later. " let b:skip_set_iskeyword = 1 let is_keyword = &iskeyword try " keep going even if syntax file is not found execute 'syntax include @'.group.' syntax/'.a:filetype.'.vim' execute 'syntax include @'.group.' after/syntax/'.a:filetype.'.vim' catch endtry let &iskeyword = is_keyword if exists('s:current_syntax') let b:current_syntax=s:current_syntax else unlet b:current_syntax endif execute 'syntax region textSnip'.ft. \ ' matchgroup='.a:textSnipHl. \ ' start="'.a:start.'" end="'.a:end.'"'. \ ' contains=@'.group.' keepend' " A workaround to Issue 115: Nested Perl syntax highlighting differs from " regular one. " Perl syntax file has perlFunctionName which is usually has no effect due to " 'contained' flag. Now we have 'syntax include' that makes all the groups " included as 'contained' into specific group. " Here perlFunctionName (with quite an angry regexp "\h\w*[^:]") clashes with " the rest syntax rules as now it has effect being really 'contained'. " Clear it! if ft =~ 'perl' syntax clear perlFunctionName endif endfunction "}}} " }}} " WIKI link following functions {{{ " vimwiki#base#find_next_link function! vimwiki#base#find_next_link() "{{{ call vimwiki#base#search_word(g:vimwiki_rxAnyLink, '') endfunction " }}} " vimwiki#base#find_prev_link function! vimwiki#base#find_prev_link() "{{{ call vimwiki#base#search_word(g:vimwiki_rxAnyLink, 'b') endfunction " }}} " vimwiki#base#follow_link function! vimwiki#base#follow_link(split, ...) "{{{ Parse link at cursor and pass " to VimwikiLinkHandler, or failing that, the default open_link handler if exists('*vimwiki#'.VimwikiGet('syntax').'_base#follow_link') " Syntax-specific links " XXX: @Stuart: do we still need it? " XXX: @Maxim: most likely! I am still working on a seemless way to " integrate regexp's without complicating syntax/vimwiki.vim if a:0 call vimwiki#{VimwikiGet('syntax')}_base#follow_link(a:split, a:1) else call vimwiki#{VimwikiGet('syntax')}_base#follow_link(a:split) endif else if a:split == "split" let cmd = ":split " elseif a:split == "vsplit" let cmd = ":vsplit " elseif a:split == "tabnew" let cmd = ":tabnew " else let cmd = ":e " endif " try WikiLink let lnk = matchstr(vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWikiLink), \ g:vimwiki_rxWikiLinkMatchUrl) " try WikiIncl if lnk == "" let lnk = matchstr(vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWikiIncl), \ g:vimwiki_rxWikiInclMatchUrl) endif " try Weblink if lnk == "" let lnk = matchstr(vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWeblink), \ g:vimwiki_rxWeblinkMatchUrl) endif if lnk != "" if !VimwikiLinkHandler(lnk) call vimwiki#base#open_link(cmd, lnk) endif return endif if a:0 > 0 execute "normal! ".a:1 else call vimwiki#base#normalize_link(0) endif endif endfunction " }}} " vimwiki#base#go_back_link function! vimwiki#base#go_back_link() "{{{ if exists("b:vimwiki_prev_link") " go back to saved wiki link let prev_word = b:vimwiki_prev_link execute ":e ".substitute(prev_word[0], '\s', '\\\0', 'g') call setpos('.', prev_word[1]) endif endfunction " }}} " vimwiki#base#goto_index function! vimwiki#base#goto_index(wnum, ...) "{{{ if a:wnum > len(g:vimwiki_list) echom "vimwiki: Wiki ".a:wnum." is not registered in g:vimwiki_list!" return endif " usually a:wnum is greater then 0 but with the following command it is == 0: " vim -n -c "exe 'VimwikiIndex' | echo g:vimwiki_current_idx" if a:wnum > 0 let idx = a:wnum - 1 else let idx = 0 endif if a:0 let cmd = 'tabedit' else let cmd = 'edit' endif if g:vimwiki_debug == 3 echom "--- Goto_index g:curr_idx=".g:vimwiki_current_idx." ww_idx=".idx."" endif call vimwiki#base#validate_wiki_options(idx) call vimwiki#base#edit_file(cmd, \ VimwikiGet('path', idx).VimwikiGet('index', idx). \ VimwikiGet('ext', idx)) call vimwiki#base#setup_buffer_state(idx) endfunction "}}} " vimwiki#base#delete_link function! vimwiki#base#delete_link() "{{{ "" file system funcs "" Delete wiki link you are in from filesystem let val = input('Delete ['.expand('%').'] (y/n)? ', "") if val != 'y' return endif let fname = expand('%:p') try call delete(fname) catch /.*/ echomsg 'vimwiki: Cannot delete "'.expand('%:t:r').'"!' return endtry call vimwiki#base#go_back_link() execute "bdelete! ".escape(fname, " ") " reread buffer => deleted wiki link should appear as non-existent if expand('%:p') != "" execute "e" endif endfunction "}}} " vimwiki#base#rename_link function! vimwiki#base#rename_link() "{{{ "" Rename wiki link, update all links to renamed WikiWord let subdir = VimwikiGet('subdir') let old_fname = subdir.expand('%:t') " there is no file (new one maybe) if glob(expand('%:p')) == '' echomsg 'vimwiki: Cannot rename "'.expand('%:p'). \'". It does not exist! (New file? Save it before renaming.)' return endif let val = input('Rename "'.expand('%:t:r').'" (y/n)? ', "") if val!='y' return endif let new_link = input('Enter new name: ', "") if new_link =~ '[/\\]' " It is actually doable but I do not have free time to do it. echomsg 'vimwiki: Cannot rename to a filename with path!' return endif " check new_fname - it should be 'good', not empty if substitute(new_link, '\s', '', 'g') == '' echomsg 'vimwiki: Cannot rename to an empty filename!' return endif let url = matchstr(new_link, g:vimwiki_rxWikiLinkMatchUrl) if url != '' let new_link = url endif let new_link = subdir.new_link let new_fname = VimwikiGet('path').new_link.VimwikiGet('ext') " do not rename if file with such name exists let fname = glob(new_fname) if fname != '' echomsg 'vimwiki: Cannot rename to "'.new_fname. \ '". File with that name exist!' return endif " rename wiki link file try echomsg "Renaming ".VimwikiGet('path').old_fname." to ".new_fname let res = rename(expand('%:p'), expand(new_fname)) if res != 0 throw "Cannot rename!" end catch /.*/ echomsg 'vimwiki: Cannot rename "'.expand('%:t:r').'" to "'.new_fname.'"' return endtry let &buftype="nofile" let cur_buffer = [expand('%:p'), \getbufvar(expand('%:p'), "vimwiki_prev_link")] let blist = s:get_wiki_buffers() " save wiki buffers for bitem in blist execute ':b '.escape(bitem[0], ' ') execute ':update' endfor execute ':b '.escape(cur_buffer[0], ' ') " remove wiki buffers for bitem in blist execute 'bwipeout '.escape(bitem[0], ' ') endfor let setting_more = &more setlocal nomore " update links call s:update_wiki_links(old_fname, new_link) " restore wiki buffers for bitem in blist if bitem[0] != cur_buffer[0] call s:open_wiki_buffer(bitem) endif endfor call s:open_wiki_buffer([new_fname, \ cur_buffer[1]]) " execute 'bwipeout '.escape(cur_buffer[0], ' ') echomsg old_fname." is renamed to ".new_fname let &more = setting_more endfunction " }}} " vimwiki#base#ui_select function! vimwiki#base#ui_select() "{{{ call s:print_wiki_list() let idx = input("Select Wiki (specify number): ") if idx == "" return endif call vimwiki#base#goto_index(idx) endfunction "}}} " }}} " TEXT OBJECTS functions {{{ " vimwiki#base#TO_header function! vimwiki#base#TO_header(inner, visual) "{{{ if !search('^\(=\+\).\+\1\s*$', 'bcW') return endif let sel_start = line("'<") let sel_end = line("'>") let block_start = line(".") let advance = 0 let level = vimwiki#u#count_first_sym(getline('.')) let is_header_selected = sel_start == block_start \ && sel_start != sel_end if a:visual && is_header_selected if level > 1 let level -= 1 call search('^\(=\{'.level.'\}\).\+\1\s*$', 'bcW') else let advance = 1 endif endif normal! V if a:visual && is_header_selected call cursor(sel_end + advance, 0) endif if search('^\(=\{1,'.level.'}\).\+\1\s*$', 'W') call cursor(line('.') - 1, 0) else call cursor(line('$'), 0) endif if a:inner && getline(line('.')) =~ '^\s*$' let lnum = prevnonblank(line('.') - 1) call cursor(lnum, 0) endif endfunction "}}} " vimwiki#base#TO_table_cell function! vimwiki#base#TO_table_cell(inner, visual) "{{{ if col('.') == col('$')-1 return endif if a:visual normal! `> let sel_end = getpos('.') normal! `< let sel_start = getpos('.') let firsttime = sel_start == sel_end if firsttime if !search('|\|\(-+-\)', 'cb', line('.')) return endif if getline('.')[virtcol('.')] == '+' normal! l endif if a:inner normal! 2l endif let sel_start = getpos('.') endif normal! `> call search('|\|\(-+-\)', '', line('.')) if getline('.')[virtcol('.')] == '+' normal! l endif if a:inner if firsttime || abs(sel_end[2] - getpos('.')[2]) != 2 normal! 2h endif endif let sel_end = getpos('.') call setpos('.', sel_start) exe "normal! \" call setpos('.', sel_end) " XXX: WORKAROUND. " if blockwise selection is ended at | character then pressing j to extend " selection furhter fails. But if we shake the cursor left and right then " it works. normal! hl else if !search('|\|\(-+-\)', 'cb', line('.')) return endif if a:inner normal! 2l endif normal! v call search('|\|\(-+-\)', '', line('.')) if !a:inner && getline('.')[virtcol('.')-1] == '|' normal! h elseif a:inner normal! 2h endif endif endfunction "}}} " vimwiki#base#TO_table_col function! vimwiki#base#TO_table_col(inner, visual) "{{{ let t_rows = vimwiki#tbl#get_rows(line('.')) if empty(t_rows) return endif " TODO: refactor it! if a:visual normal! `> let sel_end = getpos('.') normal! `< let sel_start = getpos('.') let firsttime = sel_start == sel_end if firsttime " place cursor to the top row of the table call vimwiki#u#cursor(t_rows[0][0], virtcol('.')) " do not accept the match at cursor position if cursor is next to column " separator of the table separator (^ is a cursor): " |-----^-+-------| " | bla | bla | " |-------+-------| " or it will select wrong column. if strpart(getline('.'), virtcol('.')-1) =~ '^-+' let s_flag = 'b' else let s_flag = 'cb' endif " search the column separator backwards if !search('|\|\(-+-\)', s_flag, line('.')) return endif " -+- column separator is matched --> move cursor to the + sign if getline('.')[virtcol('.')] == '+' normal! l endif " inner selection --> reduce selection if a:inner normal! 2l endif let sel_start = getpos('.') endif normal! `> if !firsttime && getline('.')[virtcol('.')] == '|' normal! l elseif a:inner && getline('.')[virtcol('.')+1] =~ '[|+]' normal! 2l endif " search for the next column separator call search('|\|\(-+-\)', '', line('.')) " Outer selection selects a column without border on the right. So we move " our cursor left if the previous search finds | border, not -+-. if getline('.')[virtcol('.')] != '+' normal! h endif if a:inner " reduce selection a bit more if inner. normal! h endif " expand selection to the bottom line of the table call vimwiki#u#cursor(t_rows[-1][0], virtcol('.')) let sel_end = getpos('.') call setpos('.', sel_start) exe "normal! \" call setpos('.', sel_end) else " place cursor to the top row of the table call vimwiki#u#cursor(t_rows[0][0], virtcol('.')) " do not accept the match at cursor position if cursor is next to column " separator of the table separator (^ is a cursor): " |-----^-+-------| " | bla | bla | " |-------+-------| " or it will select wrong column. if strpart(getline('.'), virtcol('.')-1) =~ '^-+' let s_flag = 'b' else let s_flag = 'cb' endif " search the column separator backwards if !search('|\|\(-+-\)', s_flag, line('.')) return endif " -+- column separator is matched --> move cursor to the + sign if getline('.')[virtcol('.')] == '+' normal! l endif " inner selection --> reduce selection if a:inner normal! 2l endif exe "normal! \" " search for the next column separator call search('|\|\(-+-\)', '', line('.')) " Outer selection selects a column without border on the right. So we move " our cursor left if the previous search finds | border, not -+-. if getline('.')[virtcol('.')] != '+' normal! h endif " reduce selection a bit more if inner. if a:inner normal! h endif " expand selection to the bottom line of the table call vimwiki#u#cursor(t_rows[-1][0], virtcol('.')) endif endfunction "}}} " }}} " HEADER functions {{{ " vimwiki#base#AddHeaderLevel function! vimwiki#base#AddHeaderLevel() "{{{ let lnum = line('.') let line = getline(lnum) let rxHdr = g:vimwiki_rxH if line =~ '^\s*$' return endif if line =~ g:vimwiki_rxHeader let level = vimwiki#u#count_first_sym(line) if level < 6 if g:vimwiki_symH let line = substitute(line, '\('.rxHdr.'\+\).\+\1', rxHdr.'&'.rxHdr, '') else let line = substitute(line, '\('.rxHdr.'\+\).\+', rxHdr.'&', '') endif call setline(lnum, line) endif else let line = substitute(line, '^\s*', '&'.rxHdr.' ', '') if g:vimwiki_symH let line = substitute(line, '\s*$', ' '.rxHdr.'&', '') endif call setline(lnum, line) endif endfunction "}}} " vimwiki#base#RemoveHeaderLevel function! vimwiki#base#RemoveHeaderLevel() "{{{ let lnum = line('.') let line = getline(lnum) let rxHdr = g:vimwiki_rxH if line =~ '^\s*$' return endif if line =~ g:vimwiki_rxHeader let level = vimwiki#u#count_first_sym(line) let old = repeat(rxHdr, level) let new = repeat(rxHdr, level - 1) let chomp = line =~ rxHdr.'\s' if g:vimwiki_symH let line = substitute(line, old, new, 'g') else let line = substitute(line, old, new, '') endif if level == 1 && chomp let line = substitute(line, '^\s', '', 'g') let line = substitute(line, '\s$', '', 'g') endif let line = substitute(line, '\s*$', '', '') call setline(lnum, line) endif endfunction " }}} "}}} " LINK functions {{{ " vimwiki#base#apply_template " Construct a regular expression matching from template (with special " characters properly escaped), by substituting rxUrl for __LinkUrl__, rxDesc " for __LinkDescription__, and rxStyle for __LinkStyle__. The three " arguments rxUrl, rxDesc, and rxStyle are copied verbatim, without any " special character escapes or substitutions. function! vimwiki#base#apply_template(template, rxUrl, rxDesc, rxStyle) "{{{ let magic_chars = '.*[\^$' let lnk = escape(a:template, magic_chars) if a:rxUrl != "" let lnk = substitute(lnk, '__LinkUrl__', '\='."'".a:rxUrl."'", '') endif if a:rxDesc != "" let lnk = substitute(lnk, '__LinkDescription__', '\='."'".a:rxDesc."'", '') endif if a:rxStyle != "" let lnk = substitute(lnk, '__LinkStyle__', '\='."'".a:rxStyle."'", '') endif return lnk endfunction " }}} " s:clean_url function! s:clean_url(url) " {{{ let url = split(a:url, '/\|=\|-\|&\|?\|\.') let url = filter(url, 'v:val != ""') let url = filter(url, 'v:val != "www"') let url = filter(url, 'v:val != "com"') let url = filter(url, 'v:val != "org"') let url = filter(url, 'v:val != "net"') let url = filter(url, 'v:val != "edu"') let url = filter(url, 'v:val != "http\:"') let url = filter(url, 'v:val != "https\:"') let url = filter(url, 'v:val != "file\:"') let url = filter(url, 'v:val != "xml\:"') return join(url, " ") endfunction " }}} " vimwiki#base#normalize_link_helper function! vimwiki#base#normalize_link_helper(str, rxUrl, rxDesc, template) " {{{ let str = a:str let url = matchstr(str, a:rxUrl) let descr = matchstr(str, a:rxDesc) let template = a:template if descr == "" let descr = s:clean_url(url) endif let lnk = substitute(template, '__LinkDescription__', '\="'.descr.'"', '') let lnk = substitute(lnk, '__LinkUrl__', '\="'.url.'"', '') return lnk endfunction " }}} " vimwiki#base#normalize_imagelink_helper function! vimwiki#base#normalize_imagelink_helper(str, rxUrl, rxDesc, rxStyle, template) "{{{ let lnk = vimwiki#base#normalize_link_helper(a:str, a:rxUrl, a:rxDesc, a:template) let style = matchstr(str, a:rxStyle) let lnk = substitute(lnk, '__LinkStyle__', '\="'.style.'"', '') return lnk endfunction " }}} " s:normalize_link_syntax_n function! s:normalize_link_syntax_n() " {{{ let lnum = line('.') " try WikiLink let lnk = vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWikiLink) if !empty(lnk) let sub = vimwiki#base#normalize_link_helper(lnk, \ g:vimwiki_rxWikiLinkMatchUrl, g:vimwiki_rxWikiLinkMatchDescr, \ g:vimwiki_WikiLinkTemplate2) call vimwiki#base#replacestr_at_cursor(g:vimwiki_rxWikiLink, sub) if g:vimwiki_debug > 1 echomsg "WikiLink: ".lnk." Sub: ".sub endif return endif " try WikiIncl let lnk = vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWikiIncl) if !empty(lnk) " NO-OP !! if g:vimwiki_debug > 1 echomsg "WikiIncl: ".lnk." Sub: ".lnk endif return endif " try Word (any characters except separators) " rxWord is less permissive than rxWikiLinkUrl which is used in " normalize_link_syntax_v let lnk = vimwiki#base#matchstr_at_cursor(g:vimwiki_rxWord) if !empty(lnk) let sub = vimwiki#base#normalize_link_helper(lnk, \ g:vimwiki_rxWord, '', \ g:vimwiki_WikiLinkTemplate1) call vimwiki#base#replacestr_at_cursor('\V'.lnk, sub) if g:vimwiki_debug > 1 echomsg "Word: ".lnk." Sub: ".sub endif return endif endfunction " }}} " s:normalize_link_syntax_v function! s:normalize_link_syntax_v() " {{{ let lnum = line('.') let sel_save = &selection let &selection = "old" let rv = @" let rt = getregtype('"') let done = 0 try norm! gvy let visual_selection = @" let visual_selection = substitute(g:vimwiki_WikiLinkTemplate1, '__LinkUrl__', '\='."'".visual_selection."'", '') call setreg('"', visual_selection, 'v') " paste result norm! `>pgvd finally call setreg('"', rv, rt) let &selection = sel_save endtry endfunction " }}} " vimwiki#base#normalize_link function! vimwiki#base#normalize_link(is_visual_mode) "{{{ if exists('*vimwiki#'.VimwikiGet('syntax').'_base#normalize_link') " Syntax-specific links call vimwiki#{VimwikiGet('syntax')}_base#normalize_link(a:is_visual_mode) else if !a:is_visual_mode call s:normalize_link_syntax_n() elseif visualmode() ==# 'v' && line("'<") == line("'>") " action undefined for 'line-wise' or 'multi-line' visual mode selections call s:normalize_link_syntax_v() endif endif endfunction "}}} " }}} " ------------------------------------------------------------------------- " Load syntax-specific Wiki functionality for syn in vimwiki#base#get_known_syntaxes() execute 'runtime! autoload/vimwiki/'.syn.'_base.vim' endfor " -------------------------------------------------------------------------