" Vimball Archiver by Charles E. Campbell, Jr., Ph.D. UseVimball finish doc/autolinker.txt [[[1 422 *autolinker.txt* Automatic links for any filetype (e.g. for a markdown/latex based wiki) Author: Thomas Link, micathom AT gmail com?subject=vim Features: - Call |:Autolinkbuffer| to enable autolinker for the current buffer or check the definitions of |g:autolinker_filetypes| and |g:autolinker_patterns|. - Jump to files in the current directory - Jump to files in 'path' - Jump to tags - Jump to file URLs - Jump to line numbers (syntax: filename#123 where '123' is the line number) - Jump to the first occurrence of a string (syntax: filename#q=STRING) - Invoke URLs - Highlight potential hyperlinks (doesn't work with all filetypes) - Use a fallback command (e.g. create inexistent files) - Special support for markdown (incl. link maps), viki files - file URLs (an encoded URL starting with "file://") can be used to circumvent encoding issues etc. The default map is `gz` in normal and visual mode (see |g:autolinker#nmap| and |g:autolinker#xmap|). In insert mode, it is if the key is yet unmapped (see |g:autolinker#imap|). Differences to |gf|: - Customizable & extensible - Supports several methods - Highlight some potential hyperlinks if possible - When there are multiple potential matches, users can select the right file from a list ----------------------------------------------------------------------- Install~ To install the vimball, edit the vba file and type: > :so % See :help vimball for details. To install from github, please use a plugin manager like enable_vim, pathogen, VAM, Vundle, NeoBundle, vim-plug, vim-unbundle etc. This script requires tlib (vimscript #1863) to be installed -- see https://github.com/tomtom/tlib_vim. Also available on https://github.com/tomtom/autolinker_vim ======================================================================== Contents~ g:autolinker_filetypes ................ |g:autolinker_filetypes| g:autolinker_patterns ................. |g:autolinker_patterns| g:autolinker_exclude_filetypes_rx ..... |g:autolinker_exclude_filetypes_rx| :Albuffer ............................. |:Albuffer| :Aledit ............................... |:Aledit| :Algrep ............................... |:Algrep| :Alfind ............................... |:Alfind| prototype.CleanCFile prototype.IsInternalLink autolinker#ft_viki#GetInstance ........ |autolinker#ft_viki#GetInstance()| prototype.Dispatch prototype.BaseNameLinks prototype.Dirname prototype.Rootname prototype.Highlight prototype.Jump_system prototype.Jump_def prototype.Jump_path prototype.Jump_tag prototype.Jump_fallback prototype.CleanCFile prototype.JumpInternalLink prototype.IsInternalLink autolinker#ft_markdown#GetInstance .... |autolinker#ft_markdown#GetInstance()| g:autolinker#use_highlight ............ |g:autolinker#use_highlight| g:autolinker#url_rx ................... |g:autolinker#url_rx| g:autolinker#types .................... |g:autolinker#types| g:autolinker#nmap ..................... |g:autolinker#nmap| g:autolinker#imap ..................... |g:autolinker#imap| g:autolinker#xmap ..................... |g:autolinker#xmap| g:autolinker#map_forward .............. |g:autolinker#map_forward| g:autolinker#map_backward ............. |g:autolinker#map_backward| g:autolinker#map_options .............. |g:autolinker#map_options| g:autolinker#layout ................... |g:autolinker#layout| g:autolinker#edit_file ................ |g:autolinker#edit_file| g:autolinker#edit_dir ................. |g:autolinker#edit_dir| g:autolinker#tag ...................... |g:autolinker#tag| g:autolinker#fallback ................. |g:autolinker#fallback| g:autolinker#index .................... |g:autolinker#index| g:autolinker#cfile_gsub ............... |g:autolinker#cfile_gsub| g:autolinker#cfile_rstrip_rx .......... |g:autolinker#cfile_rstrip_rx| g:autolinker#find_ignore_rx ........... |g:autolinker#find_ignore_rx| g:autolinker#find_ignore_subst ........ |g:autolinker#find_ignore_subst| g:autolinker#fragment_rx .............. |g:autolinker#fragment_rx| prototype.WordLinks prototype.Dirname prototype.Rootname prototype.UninstallHotkey prototype.InstallHotkey prototype.ClearHighlight prototype.CfileGsubRx prototype.Highlight prototype.Edit prototype.SplitFilename prototype.GetCFile prototype.GetCWord prototype.CleanCWord prototype.CleanCFile prototype.IsInternalLink prototype.JumpInternalLink prototype.Jump_internal prototype.Jump_system prototype.Jump_def prototype.Jump_path prototype.Jump_tag prototype.Jump_fallback autolinker#Ensure ..................... |autolinker#Ensure()| autolinker#EnableBuffer ............... |autolinker#EnableBuffer()| autolinker#DisableBuffer .............. |autolinker#DisableBuffer()| autolinker#Balloon .................... |autolinker#Balloon()| autolinker#Jump ....................... |autolinker#Jump()| autolinker#Edit ....................... |autolinker#Edit()| autolinker#EditInPath ................. |autolinker#EditInPath()| autolinker#NextLink ................... |autolinker#NextLink()| autolinker#FileSources ................ |autolinker#FileSources()| autolinker#CompleteFilename ........... |autolinker#CompleteFilename()| ======================================================================== plugin/autolinker.vim~ *g:autolinker_filetypes* g:autolinker_filetypes (default: ['text', 'txt', 'todo', 'ttodo', 'todotxt', 'md', 'markdown', 'markdown.pandoc', 'tex', 'latex']) *g:autolinker_patterns* g:autolinker_patterns (default: ['*.txt', '*.TXT', '*.md', '*.markdown']) *g:autolinker_exclude_filetypes_rx* g:autolinker_exclude_filetypes_rx (default: '') Don't enable autolinker for filetypes matching this |regexp| even when the filename matches |g:autolinker_patterns|. *:Albuffer* :Albuffer Enable the autolinker plugin for the current buffer. *:Aledit* :Aledit " Edit/create a file in path. *:Algrep* :Algrep Grep all files with prefixes defined in |g:autolinker#cfile_gsub|. This requires the trag_vim plugin to be installed. See also |:Tragsearch|. *:Alfind* :Alfind Find a file via |:Tragfiles|. ======================================================================== autoload/autolinker/ft_viki.vim~ prototype.CleanCFile prototype.IsInternalLink *autolinker#ft_viki#GetInstance()* autolinker#ft_viki#GetInstance(prototype) ======================================================================== autoload/autolinker/prototype.vim~ prototype.Dispatch prototype.BaseNameLinks prototype.Dirname prototype.Rootname prototype.Highlight prototype.Jump_system prototype.Jump_def prototype.Jump_path prototype.Jump_tag prototype.Jump_fallback ======================================================================== autoload/autolinker/ft_markdown.vim~ prototype.CleanCFile prototype.JumpInternalLink prototype.IsInternalLink *autolinker#ft_markdown#GetInstance()* autolinker#ft_markdown#GetInstance(prototype) ======================================================================== autoload/autolinker.vim~ *g:autolinker#use_highlight* g:autolinker#use_highlight (default: ['word', 'url', 'cfile_gsub']) Items that should be highlighted as hyperlinks: - word - url - cfile_gsub *g:autolinker#url_rx* g:autolinker#url_rx (default: '\l\{2,6}://[-./[:alnum:]_+~=%#?&]\+') *g:autolinker#types* g:autolinker#types (default: ['internal', 'system', 'path', 'def', 'tag', 'fallback']) Possible values (the order is significant): - internal (a document-internal reference) - system (URLs, non-text files etc. matching |g:autolinker#system_rx|) - def (files in the current directory) - path (files in 'path') - tag (tags) - fallback (see |g:autolinker#fallback|) *g:autolinker#nmap* g:autolinker#nmap (default: 'gz') Normal mode map. *g:autolinker#imap* g:autolinker#imap (default: ' ') Insert mode map. *g:autolinker#xmap* g:autolinker#xmap (default: g:autolinker#nmap) Visual mode map. *g:autolinker#map_forward* g:autolinker#map_forward (default: ']'. g:autolinker#nmap) *g:autolinker#map_backward* g:autolinker#map_backward (default: '['. g:autolinker#nmap) *g:autolinker#map_options* g:autolinker#map_options (default: 'a%s') Call this map with a count and a char (w = window, t = tab, s = split, v = vertical split) to define where the destination should be displayed. Let's assume |maplocalleader| is '\'. Then, e.g., \asgz .... Open the destination in a split buffer 2\awgz ... Open the destination in the second window *g:autolinker#layout* g:autolinker#layout (default: {'*': ['tab drop', 'fnameescape'], 'w': ['wincmd w'], 't': ['tabn'], 's': ['split'], 'v': ['vert split']}) Command for working with layouts. *g:autolinker#edit_file* g:autolinker#edit_file (default: ['edit', 'fnameescape']) Command for opening files. *g:autolinker#edit_dir* g:autolinker#edit_dir (default: ['Explore']) Command for opening directories *g:autolinker#tag* g:autolinker#tag (default: 'tselect') *g:autolinker#fallback* g:autolinker#fallback (default: ':call autolinker#EditInPath("%s"),gf,gx') A comma-separated list of fallback procedures. Normal command to run when everything else fails. If the command starts with ':', it is an ex command. branch to use. The arguments are format strings for |printf|. Any `%s` will be replaced with ||. A `%` must be written as `%%`. Possible values are: - gf - ':'. g:autolinker#edit_file[0] (if you want to create new files) *g:autolinker#index* g:autolinker#index (default: '"index.". expand("%:e")') The value is an expression that evaluates to the filename. When opening directories, check whether a file with that name exists. If so, open that file instead of the directory. *g:autolinker#cfile_gsub* g:autolinker#cfile_gsub (default: []) A list of lists [RX, SUB, optional: {OPT => VALUE}] that are applied to the || under the cursor. This can be used to rewrite filenames and URLs, in order to implement e.g. interwikis. RX must be a |magic| |regexp|. Options: flags = 'g' ... flags for |substitute()| stop = 0 ...... Don't process other gsubs when this |regexp| matches Examples: ["^WIKI/", "~/MyWiki/"] .............. Redirect to wiki ["^todo://", "~/data/simpletask/"] ... Use todo pseudo-protocol as used by the simpletasks app *g:autolinker#cfile_rstrip_rx* g:autolinker#cfile_rstrip_rx (default: '[\])},;:.]\s*') Strip a suffix matching this |regexp| from cfile. *g:autolinker#find_ignore_rx* g:autolinker#find_ignore_rx (default: '') |autolinker#Find()| ignores substitutions from |g:autolinker#cfile_gsub| that match this |regexp|. *g:autolinker#find_ignore_subst* g:autolinker#find_ignore_subst (default: '^\a\{2,}:') |autolinker#Find()| ignores substitutions from |g:autolinker#cfile_gsub| that match this |regexp|. *g:autolinker#fragment_rx* g:autolinker#fragment_rx (default: '\%(\d\+\|lnum=\d\+\|q=\S\+\)$') prototype.WordLinks prototype.Dirname prototype.Rootname prototype.UninstallHotkey prototype.InstallHotkey prototype.ClearHighlight prototype.CfileGsubRx prototype.Highlight prototype.Edit prototype.SplitFilename prototype.GetCFile prototype.GetCWord prototype.CleanCWord prototype.CleanCFile prototype.IsInternalLink prototype.JumpInternalLink prototype.Jump_internal prototype.Jump_system prototype.Jump_def prototype.Jump_path prototype.Jump_tag prototype.Jump_fallback *autolinker#Ensure()* autolinker#Ensure() *autolinker#EnableBuffer()* autolinker#EnableBuffer() *autolinker#DisableBuffer()* autolinker#DisableBuffer() *autolinker#Balloon()* autolinker#Balloon() *autolinker#Jump()* autolinker#Jump(mode) Jump to the destination descibed by the word under the cursor. Use one of the following methods (see |g:autolinker#types|): 1. Jump by definition (depends on filetype; by default all files in the current directory) 2. Jump to special URLs matching |g:autolinker#system_rx| 3. Jump to a file in 'path' mathing the word under cursor 4. Jump to a tag As last resort use |g:autolinker#fallback| *autolinker#Edit()* autolinker#Edit(cfile) *autolinker#EditInPath()* autolinker#EditInPath(cfile) *autolinker#NextLink()* autolinker#NextLink(n) *autolinker#FileSources()* autolinker#FileSources(opts) *autolinker#CompleteFilename()* autolinker#CompleteFilename(ArgLead, CmdLine, CursorPos) vim:tw=78:fo=w2croql:isk=!-~,^*,^|,^":ts=8:ft=help:norl: plugin/autolinker.vim [[[1 81 " @Author: Thomas Link (micathom AT gmail.com) " @GIT: http://github.com/tomtom/autolinker_vim/ " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-26. " @Revision: 92 " GetLatestVimScripts: 5253 0 :AutoInstall: autolinker.vim " Automatic hyperlinks for any filetype if &cp || exists("loaded_autolinker") finish endif let loaded_autolinker = 2 let s:save_cpo = &cpo set cpo&vim if !exists('g:autolinker_filetypes') let g:autolinker_filetypes = ['text', 'txt', 'todo', 'ttodo', 'todotxt', 'md', 'markdown', 'markdown.pandoc', 'tex', 'latex'] "{{{2 endif if exists('g:autolinker_filetypes_user') let g:autolinker_filetypes += g:autolinker_filetypes_user endif if !exists('g:autolinker_patterns') let g:autolinker_patterns = ['*.txt', '*.TXT', '*.md', '*.markdown'] "{{{2 endif if exists('g:autolinker_patterns_user') let g:autolinker_patterns += g:autolinker_patterns_user endif if !exists('g:autolinker_exclude_filetypes') " Don't enable autolinker for filetypes matching this |regexp| " even when the filename matches |g:autolinker_patterns|. let g:autolinker_exclude_filetypes_rx = '' "{{{2 endif " :nodoc: let g:autolinker_glob_cache = {} " :nodoc: command! -bar Alcachereset let g:autolinker_glob_cache = {} augroup AutoLinker augroup! AutoLinker autocmd FocusLost * Alcachereset autocmd BufWritePre,FileWritePre * if index(g:autolinker_filetypes, &ft, 0, 0) != -1 && !filereadable(expand("")) | Alcachereset | endif autocmd BufWinEnter * if index(g:autolinker_filetypes, &ft, 0, 0) != -1 | call autolinker#Ensure() | endif for s:item in g:autolinker_patterns exec 'autocmd BufWinEnter' s:item 'if empty(g:autolinker_exclude_filetypes_rx) || &filetype !~? g:autolinker_exclude_filetypes_rx | call autolinker#Ensure() | endif' exec 'autocmd BufWritePre,FileWritePre' s:item 'if (empty(g:autolinker_exclude_filetypes_rx) || &filetype !~? g:autolinker_exclude_filetypes_rx) && !filereadable(expand("")) | Alcachereset | endif' endfor unlet! s:item augroup end " Enable the autolinker plugin for the current buffer. command! -bar Albuffer call autolinker#EnableBuffer() " " Edit/create a file in path. command! -bar -nargs=1 -complete=customlist,autolinker#CompleteFilename Aledit call autolinker#Edit() " Grep all files with prefixes defined in |g:autolinker#cfile_gsub|. " This requires the trag_vim plugin to be installed. " See also |:Tragsearch|. command! -bar -bang -nargs=+ Algrep if exists(':Trag') == 2 | Trag --filenames --file_sources=*autolinker#FileSources | else | echom ':Algrep requires the trag_vim plugin to be installed!' | endif " Find a file via |:Tragfiles|. command! -bar -bang -nargs=* -complete=customlist,trag#CComplete Alfind if exists(':Tragfiles') == 2 | Tragfiles --filenames --file_sources=*autolinker#FileSources | else | echom ':Alfind requires the trag_vim plugin to be installed!' | endif let &cpo = s:save_cpo unlet s:save_cpo autoload/autolinker/ft_viki.vim [[[1 28 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: http://github.com/tomtom/ " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2015-10-13 " @Revision: 19 let s:prototype = {} function! s:prototype.CleanCFile(cfile) abort dict "{{{3 let cfile = self.Viki_Super_CleanCFile(a:cfile) let cfile = substitute(cfile, '\]\[.\{-}\].\?\]$', ']]', 'g') let cfile = substitute(cfile, '\(^\[\[\|\].\?\]\+$\)', '', 'g') return cfile endf function! s:prototype.IsInternalLink(cfile) abort dict "{{{3 return a:cfile =~ '^#\S\+$' endf function! autolinker#ft_viki#GetInstance(prototype) abort "{{{3 let a:prototype.Viki_Super_CleanCFile = a:prototype.CleanCFile return extend(a:prototype, s:prototype) endf autoload/autolinker/prototype.vim [[[1 191 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: http://github.com/tomtom/ " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2015-09-28 " @Revision: 2 let s:prototype = {'fallback': g:autolinker#fallback \ , 'use_highlight': g:autolinker#use_highlight \ , 'cfile_gsub': g:autolinker#cfile_gsub \ } function! s:prototype.Dispatch(method, default, ...) abort dict "{{{3 " TLogVAR a:method, has_key(self, a:method) if has_key(self, a:method) let rv = call(self[a:method], a:000, self) else let fn = 'autolinker#'. a:method " TLogVAR fn, exists('*'. fn), a:000 if exists('*'. fn) let rv = call(fn, [self] + a:000) else let rv = a:default endif endif " TLogVAR rv return rv endf function! s:prototype.BaseNameLinks() dict abort "{{{3 let files = s:Globpath(self.Dirname(), get(self, 'pattern', '*')) " TLogVAR self.Dirname(), files let defs = {} let rootname = self.Rootname() for filename in files let rname = fnamemodify(filename, ':t:r') " TLogVAR rootname, rname if !empty(rname) && rootname != rname let vrx = '\V\<'. escape(rname, '\') .'\>' let defs[rname] = {'rx': vrx, 'filename': filename} endif endfor return defs endf function! s:prototype.Dirname() abort dict "{{{3 return expand('%:p:h') endf function! s:prototype.Rootname() abort dict "{{{3 return expand('%:t:r') endf function! s:prototype.Highlight() abort dict "{{{3 if self.use_highlight silent! syn clear AutoHyperlink let rx = join(map(values(self.defs), 'v:val.rx'), '\|') if !empty(rx) exec 'syn match AutoHyperlink /'. escape(rx, '/') .'/' let col = &background == 'dark' ? 'Cyan' : 'DarkBlue' exec 'hi AutoHyperlink term=underline cterm=underline gui=underline ctermfg='. col 'guifg='. col endif endif endf function! s:prototype.Jump_system(mode, cword, cfile) abort dict "{{{3 " TLogVAR a:mode, a:cword, a:cfile if a:cfile =~ g:autolinker#system_rx let cmd = printf(g:autolinker#system_browser, a:cfile) exec cmd return 1 else return 0 endif endf function! s:prototype.Jump_def(mode, cword, cfile) abort dict "{{{3 " TLogVAR a:mode, a:cword, a:cfile let exact = [] let partly = [] for [rname, def] in items(self.defs) if rname ==# a:cword call add(exact, def.filename) elseif stridx(rname, a:cword) call add(partly, def.filename) endif endfor let filename = '' if !empty(exact) let matches = exact if len(matches) == 1 let filename = matches[0] endif else let matches = partly endif if !empty(filename) let filename = tlib#input#List('s', 'Select file:', matches) endif " TLogVAR filename, matches try if !empty(filename) return self.Dispatch('Edit', '', filename) endif catch echohl ErrorMsg echom v:exception echom v:throwpoint echohl NONE endtry return 0 endf function! s:prototype.Jump_path(mode, cword, cfile) abort dict "{{{3 " TLogVAR a:mode, a:cword, a:cfile if has_key(self.globpath, a:cfile) let matches = self.globpath[a:cfile] else let matches = s:Globpath(&path, a:cfile .'*') let brx = '\V\%(\^\|\[\/]\)'. substitute(a:cfile, '[\/]', '\\[\\/]', 'g') .'.\[^.]\+\$' let bmatches = filter(copy(matches), 'v:val =~ brx') " TLogVAR brx, bmatches, matches if !empty(bmatches) let matches = bmatches endif let matches = map(matches, 'fnamemodify(v:val, ":p")') let matches = tlib#list#Uniq(matches) let self.globpath[a:cfile] = matches endif let nmatches = len(matches) if nmatches == 1 let filename = matches[0] else let filename = tlib#input#List('s', 'Select file:', matches) endif if !empty(filename) return self.Dispatch('Edit', '', filename) else return 0 endif endf function! s:prototype.Jump_tag(mode, cword, cfile) abort dict "{{{3 " TLogVAR a:mode, a:cword, a:cfile try exec 'tab' g:autolinker#tag a:cword return 1 catch /^Vim\%((\a\+)\)\=:E426/ catch /^Vim\%((\a\+)\)\=:E433/ endtry return 0 endf function! s:prototype.Jump_fallback(mode, cword, cfile) abort dict "{{{3 " TLogVAR a:mode, a:cword, a:cfile try let fallback = self.fallback if stridx(fallback, ',') != -1 let fallbacks = split(fallback, ',') let fallback = tlib#input#List('s', 'Use', fallbacks) endif if !empty(fallback) let fallback = tlib#string#Printf1(fallback, a:cfile) if fallback =~# '^:' exec fallback else let restore = a:mode ==# 'v' ? 'gv' : '' exec 'norm!' restore . fallback endif return 1 endif catch echohl ErrorMsg echom v:exception echom v:throwpoint echohl NONE endtry return 0 endf autoload/autolinker/ft_markdown.vim [[[1 47 " @Author: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: http://github.com/tomtom/ " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2015-10-13 " @Revision: 40 let s:prototype = {} function! s:prototype.CleanCFile(cfile) abort dict "{{{3 let cfile = self.Markdown_Super_CleanCFile(a:cfile) let cfile = s:GetLinkMapLink(cfile) return cfile endf function! s:prototype.JumpInternalLink(cfile) abort dict "{{{3 let rx = printf('\V\%(\^\s\*%s\>\|\\)', \ tlib#rx#Escape(a:cfile, 'V'), \ tlib#rx#Escape(substitute(a:cfile, '^#', '', ''), 'V')) return search(rx, 'cw') > 0 endf function! s:prototype.IsInternalLink(cfile) abort dict "{{{3 return a:cfile =~ '^#\S\+$' endf function! s:GetLinkMapLink(cfile) abort "{{{3 let lines = getline(line('.'), '$') let rx = printf('^\s\+\[%s\]:\s\+\zs\S\+', a:cfile) let lines = filter(lines, 'v:val =~ rx') if !empty(lines) return matchstr(lines[0], rx) else return a:cfile endif endf function! autolinker#ft_markdown#GetInstance(prototype) abort "{{{3 let a:prototype.Markdown_Super_CleanCFile = a:prototype.CleanCFile return extend(a:prototype, s:prototype) endf autoload/autolinker.vim [[[1 868 " @thor: Tom Link (mailto:micathom AT gmail com?subject=[vim]) " @Website: http://github.com/tomtom/ " @License: GPL (see http://www.gnu.org/licenses/gpl.txt) " @Last Change: 2016-01-25 " @Revision: 892 if !exists('g:loaded_tlib') || g:loaded_tlib < 115 runtime plugin/tlib.vim if !exists('g:loaded_tlib') || g:loaded_tlib < 115 echoerr 'tlib >= 1.15 is required' finish endif endif if !exists('g:autolinker#use_highlight') " Items that should be highlighted as hyperlinks: " - word " - url " - cfile_gsub let g:autolinker#use_highlight = ['word', 'url', 'cfile_gsub'] "{{{2 endif if !exists('g:autolinker#url_rx') let g:autolinker#url_rx = '\l\{2,6}://[-./[:alnum:]_+~=%#?&]\+' "{{{2 " let g:autolinker#url_rx = '\<\%(ht\|f\)tps\?:\/\/\f\+' "{{{2 endif if !exists('g:autolinker#types') " Possible values (the order is significant): " - internal (a document-internal reference) " - system (URLs, non-text files etc. matching " |g:autolinker#system_rx|) " - def (files in the current directory) " - path (files in 'path') " - tag (tags) " - fallback (see |g:autolinker#fallback|) let g:autolinker#types = ['internal', 'system', 'path', 'def', 'tag', 'fallback'] "{{{2 endif if !exists('g:autolinker#nmap') " Normal mode map. let g:autolinker#nmap = 'gz' "{{{2 endif if !exists('g:autolinker#imap') " Insert mode map. let g:autolinker#imap = ' ' "{{{2 endif if !exists('g:autolinker#xmap') " Visual mode map. let g:autolinker#xmap = g:autolinker#nmap "{{{2 endif if !exists('g:autolinker#map_forward') let g:autolinker#map_forward = ']'. g:autolinker#nmap "{{{2 endif if !exists('g:autolinker#map_backward') let g:autolinker#map_backward = '['. g:autolinker#nmap "{{{2 endif if !exists('g:autolinker#map_options') " Call this map with a count and a char (w = window, t = tab, s = " split, v = vertical split) to define where the destination should " be displayed. " " Let's assume |maplocalleader| is '\'. Then, e.g., " \asgz .... Open the destination in a split buffer " 2\awgz ... Open the destination in the second window let g:autolinker#map_options = 'a%s' "{{{2 endif if !exists('g:autolinker#layout') " Command for working with layouts. let g:autolinker#layout = {'*': ['tab drop', 'fnameescape'], 'w': ['wincmd w'], 't': ['tabn'], 's': ['split'], 'v': ['vert split']} "{{{2 endif if !exists('g:autolinker#edit_file') " Command for opening files. let g:autolinker#edit_file = ['edit', 'fnameescape'] "{{{2 endif if !exists('g:autolinker#edit_dir') " Command for opening directories let g:autolinker#edit_dir = ['Explore'] "{{{2 endif if !exists('g:autolinker#tag') let g:autolinker#tag = 'tselect' "{{{2 endif if !exists('g:autolinker#fallback') " A comma-separated list of fallback procedures. " Normal command to run when everything else fails. " If the command starts with ':', it is an ex command. " branch to use. " " The arguments are format strings for |printf|. Any `%s` will be " replaced with ||. A `%` must be written as `%%`. " " Possible values are: " - gf " - ':'. g:autolinker#edit_file[0] (if you want to create new files) let g:autolinker#fallback = ':call autolinker#EditInPath("%s"),gf,gx' "{{{2 endif if !exists('g:autolinker#index') " The value is an expression that evaluates to the filename. " When opening directories, check whether a file with that name " exists. If so, open that file instead of the directory. let g:autolinker#index = '"index.". expand("%:e")' "{{{2 endif if !exists('g:autolinker#cfile_gsub') " A list of lists [RX, SUB, optional: {OPT => VALUE}] that are " applied to the || under the cursor. This can be used to " rewrite filenames and URLs, in order to implement e.g. interwikis. " " RX must be a |magic| |regexp|. " " Options: " flags = 'g' ... flags for |substitute()| " stop = 0 ...... Don't process other gsubs when this |regexp| " matches " " Examples: " ["^WIKI/", "~/MyWiki/"] .............. Redirect to wiki " ["^todo://", "~/data/simpletask/"] ... Use todo pseudo-protocol as " used by the simpletasks app let g:autolinker#cfile_gsub = [] "{{{2 endif if !exists('g:autolinker#cfile_rstrip_rx') " Strip a suffix matching this |regexp| from cfile. let g:autolinker#cfile_rstrip_rx = '[\])},;:.]\s*' "{{{2 endif if !exists('g:autolinker#find_ignore_rx') " |autolinker#Find()| ignores substitutions from " |g:autolinker#cfile_gsub| that match this |regexp|. let g:autolinker#find_ignore_rx = '' "{{{2 endif if !exists('g:autolinker#find_ignore_subst') " |autolinker#Find()| ignores substitutions from " |g:autolinker#cfile_gsub| that match this |regexp|. let g:autolinker#find_ignore_subst = '^\a\{2,}:' "{{{2 endif if !exists('g:autolinker#fragment_rx') let g:autolinker#fragment_rx = '\%(\d\+\|lnum=\d\+\|q=\S\+\)$' "{{{2 endif let s:prototype = {'mode': 'n' \ , 'fallback': g:autolinker#fallback \ , 'types': g:autolinker#types \ , 'use_highlight': g:autolinker#use_highlight \ , 'cfile_gsub': g:autolinker#cfile_gsub \ , 'cfile_rstrip_rx': g:autolinker#cfile_rstrip_rx \ } if v:version > 704 || (v:version == 704 && has('patch279')) function! s:DoGlobPath(path, pattern) abort "{{{3 return globpath(a:path, a:pattern, 0, 1) endf else function! s:DoGlobPath(path, pattern) abort "{{{3 return split(globpath(a:path, a:pattern), '\n') endf endif function! s:IsValidCache(id) abort "{{{3 return has_key(g:autolinker_glob_cache, a:id) endf function! s:Globpath(path, pattern) abort "{{{3 let id = join([getcwd(), a:path, a:pattern], '|') if s:IsValidCache(id) let matches = g:autolinker_glob_cache[id].files else let matches = s:DoGlobPath(a:path, a:pattern) let matches = map(matches, 'fnamemodify(v:val, ":p")') let matches = tlib#list#Uniq(matches) let g:autolinker_glob_cache[id] = {'files': matches} endif return matches endf function! s:prototype.WordLinks() dict abort "{{{3 let files = s:Globpath(self.Dirname(), get(self, 'pattern', '*')) " TLogVAR self.Dirname(), files let defs = {} let rootname = self.Rootname() for filename in files let rname = fnamemodify(filename, ':t:r') " TLogVAR rootname, rname if !empty(rname) && rootname != rname let vrx = '\V\<'. escape(rname, '\') .'\>' let defs[rname] = {'rx': vrx, 'filename': filename} endif endfor return defs endf function! s:prototype.Dirname() abort dict "{{{3 return expand('%:p:h') endf function! s:prototype.Rootname() abort dict "{{{3 return expand('%:t:r') endf function! s:prototype.UninstallHotkey() abort dict "{{{3 if !empty(g:autolinker#nmap) exec 'silent! nunmap ' g:autolinker#nmap endif if !empty(g:autolinker#imap) exec 'silent! iunmap ' g:autolinker#imap endif if !empty(g:autolinker#xmap) exec 'silent! xunmap ' g:autolinker#xmap endif if !empty(g:autolinker#map_forward) exec 'silent! nunmap' g:autolinker#map_forward endif if !empty(g:autolinker#map_backward) exec 'silent! nunmap' g:autolinker#map_backward endif endf function! s:prototype.InstallHotkey() abort dict "{{{3 if !empty(g:autolinker#nmap) exec 'silent! nnoremap ' g:autolinker#nmap ':call autolinker#Jump("n")' endif if !empty(g:autolinker#imap) exec 'silent! inoremap ' g:autolinker#imap ':call autolinker#Jump("i")' endif if !empty(g:autolinker#xmap) exec 'silent! xnoremap ' g:autolinker#xmap '""y:call autolinker#Jump("v")' endif if !empty(g:autolinker#map_forward) exec 'silent! nnoremap' g:autolinker#map_forward ':call autolinker#NextLink(v:count1)' endif if !empty(g:autolinker#map_backward) exec 'silent! nnoremap' g:autolinker#map_backward ':call autolinker#NextLink(- v:count1)' endif endf function! s:prototype.ClearHighlight() abort dict "{{{3 silent! syntax clear AutoHyperlink endf function! s:prototype.CfileGsubRx() abort dict "{{{3 let crx = [] for [rx, subst; rest] in self.cfile_gsub let rxs = substitute(rx, '\^', '', 'g') " let rxs = substitute(rxs, '[\\/]', '[\\\\/]', 'g') let rxs = escape(rxs, '/') call add(crx, rxs) endfor if empty(crx) let rv = '' else let rv = '\m\<'. printf('\%%(%s\)', join(crx, '\|')) .'\f*' endif Tlibtrace 'autolinker', rv return rv endf function! s:prototype.Highlight() abort dict "{{{3 " TLogVAR self.use_highlight if !empty(self.use_highlight) if index(self.use_highlight, 'word') != -1 call self.ClearHighlight() let rx = join(map(values(self.defs), 'v:val.rx'), '\|') " TLogVAR rx if !empty(rx) exec 'syntax match AutoHyperlinkWord /'. escape(rx, '/') .'/' endif endif if index(self.use_highlight, 'url') != -1 exec 'syntax match AutoHyperlinkURL /'. escape(g:autolinker#url_rx, '/') .'/' endif if index(self.use_highlight, 'cfile_gsub') != -1 let crx = self.CfileGsubRx() if !empty(crx) exec 'syntax match AutoHyperlinkCfile /'. crx .'/' endif endif " let col = &background == 'dark' ? 'Cyan' : 'DarkBlue' " exec 'hi AutoHyperlink term=underline cterm=underline gui=underline ctermfg='. col 'guifg='. col hi AutoHyperlinkWord term=underline cterm=underline gui=underline hi AutoHyperlinkURL term=underline cterm=underline gui=underline hi AutoHyperlinkCfile term=underline cterm=underline gui=underline endif endf function! s:prototype.Edit(filename, postprocess) abort dict "{{{3 Tlibtrace 'autolinker', a:filename try if !self.Jump_system(a:filename) let filename = a:filename if isdirectory(filename) let index = filename .'/'. eval(g:autolinker#index) if filereadable(index) let filename = index endif endif call s:Edit(filename, a:postprocess) call autolinker#Ensure() endif return 1 catch echohl ErrorMsg echom v:exception echom v:throwpoint echohl NONE return 0 endtry endf function! s:prototype.SplitFilename(filename) abort dict "{{{3 let fragment = matchstr(a:filename, '#\zs'. g:autolinker#fragment_rx) if !empty(fragment) let filename = substitute(a:filename, '^.\{-}\zs#'. g:autolinker#fragment_rx, '', '') if !(filereadable(a:filename) && !filereadable(filename)) if fragment =~# '^\d\+$' let postprocess = fragment elseif fragment =~# '^lnum=\d\+$' let postprocess = substitute(fragment, '^lnum=', '', '') elseif fragment =~# '^q=\S\+$' let postprocess = '/'. escape(substitute(fragment, '^q=', '', ''), '/') else throw 'autolinker.SplitFilename: Internal error: '. a:filename endif Tlibtrace 'autolinker', filename, postprocess return [filename, postprocess] endif endif return [a:filename, ''] endf function! s:prototype.GetCFile() abort dict "{{{3 if !has_key(self, 'cfile') if stridx('in', self.mode) != -1 if has_key(self, 'a_cfile') let cfile = self.a_cfile else let cfile = expand("") endif " TLogVAR 1, self.cfile let self.cfile = self.CleanCFile(cfile) " TLogVAR 2, self.cfile elseif self.mode ==# 'v' let self.cfile = @" else throw 'AutoLinker: Unsupported mode: '. self.mode endif endif return self.cfile endf function! s:prototype.GetCWord() abort dict "{{{3 if !has_key(self, 'cword') if stridx('in', self.mode) != -1 let self.cword = expand("") let self.cword = self.CleanCWord(self.cword) " TLogVAR cword elseif self.mode ==# 'v' let self.cword = @" else throw 'AutoLinker: Unsupported mode: '. self.mode endif endif return self.cword endf function! s:prototype.CleanCWord(text) abort dict "{{{3 return a:text endf function! s:prototype.CleanCFile(text, ...) abort dict "{{{3 let strip = a:0 >= 1 ? a:1 : 1 let text = a:text if strip && !empty(self.cfile_rstrip_rx) let text = substitute(text, self.cfile_rstrip_rx .'$', '', '') endif if !empty(&includeexpr) let text = eval(substitute(&includeexpr, 'v:fname', string(a:text), 'g')) endif " TLogVAR text if text =~ '^file://' let text = tlib#url#Decode(substitute(text, '^file://', '', '')) endif for [rx, sub; rest] in self.cfile_gsub let opts = get(rest, 0, {}) let text1 = substitute(text, '\m\C'. rx, sub, get(opts, 'flags', 'g')) if get(opts, 'stop', 0) && text != text1 break endif " TLogVAR rx, sub, text1 let text = text1 endfor return text endf function! s:prototype.IsInternalLink(cfile) abort dict "{{{3 return 0 endf function! s:prototype.JumpInternalLink(cfile) abort dict "{{{3 return search('\V\^\s\*'. tlib#rx#Escape(a:cfile, 'V') .'\>') > 0 endf function! s:prototype.Jump_internal() abort dict "{{{3 let cfile = self.GetCFile() Tlibtrace 'autolinker', 'internal', cfile if self.IsInternalLink(cfile) let [editdef, special] = s:GetEditCmd('file') " TLogVAR edit, special if special call s:EditEdit(expand('%:p'), editdef) endif return self.JumpInternalLink(cfile) else return 0 endif endf function! s:prototype.Jump_system(...) abort dict "{{{3 let cfile = a:0 >= 1 ? a:1 : self.GetCFile() Tlibtrace 'autolinker', 'system', cfile return tlib#sys#Open(cfile) endf " function! s:prototype.Jump_fileurl(mode, cword, cfile) abort dict "{{{3 " Tlibtrace 'autolinker', a:mode, a:cword, a:cfile " if a:cfile =~ '^file://' " let cfile = tlib#url#Decode(substitute(a:cfile, '^file://', '', '')) " let cfile = self.CleanCFile(cfile) " Tlibtrace 'autolinker', a:cfile, cfile " return self.Edit(cfile, '') " endif " return 0 " endf function! s:prototype.Jump_def() abort dict "{{{3 let [cword, postprocess] = self.SplitFilename(self.GetCWord()) Tlibtrace 'autolinker', 'def', cword let exact = [] let partly = [] for [rname, def] in items(self.defs) if rname ==# cword call add(exact, def.filename) elseif stridx(rname, cword) call add(partly, def.filename) endif endfor let filename = '' if !empty(exact) let matches = exact if len(matches) == 1 let filename = matches[0] endif else let matches = partly endif if !empty(filename) let filename = tlib#input#List('s', 'Select file:', matches) endif " TLogVAR filename, matches try if !empty(filename) return self.Edit(filename, postprocess) endif catch echohl ErrorMsg echom v:exception echom v:throwpoint echohl NONE endtry return 0 endf function! s:prototype.Jump_path() abort dict "{{{3 let [cfile, postprocess] = self.SplitFilename(self.GetCFile()) Tlibtrace 'autolinker', 'path', cfile let matches = s:Globpath(&path, cfile .'*') let brx = '\C\V\%(\^\|\[\/]\)'. substitute(cfile, '[\/]', '\\[\\/]', 'g') .'.\[^.]\+\$' let bmatches = filter(copy(matches), 'v:val =~ brx') " TLogVAR brx, bmatches, matches if !empty(bmatches) let matches = bmatches endif let nmatches = len(matches) if nmatches == 1 let filename = matches[0] else let filename = tlib#input#List('s', 'Select file:', matches) endif if !empty(filename) return self.Edit(filename, postprocess) else return 0 endif endf function! s:prototype.Jump_tag() abort dict "{{{3 let cword = self.GetCWord() Tlibtrace 'autolinker', 'tag', cword try exec 'tab' g:autolinker#tag cword return 1 catch /^Vim\%((\a\+)\)\=:E426/ catch /^Vim\%((\a\+)\)\=:E433/ endtry return 0 endf function! s:prototype.Jump_fallback() abort dict "{{{3 let [cfile, postprocess] = self.SplitFilename(self.GetCFile()) Tlibtrace 'autolinker', 'fallback', cfile try let fallback = self.fallback if stridx(fallback, ',') != -1 let fallbacks = map(split(fallback, ','), 'tlib#string#Printf1(v:val, cfile)') let fallback = tlib#input#List('s', 'Use', fallbacks) endif if !empty(fallback) if fallback =~# '^:' exec fallback else let restore = self.mode ==# 'v' ? 'gv' : '' exec 'norm!' restore . fallback endif return 1 endif catch echohl ErrorMsg echom v:exception echom v:throwpoint echohl NONE endtry return 0 endf function! autolinker#Ensure() abort "{{{3 if !exists('b:autolinker') call autolinker#EnableBuffer() endif return b:autolinker endf let s:ft_prototypes = {} function! autolinker#EnableBuffer() abort "{{{3 let ft = &ft if empty(ft) let ft = '' endif if !has_key(s:ft_prototypes, ft) let prototype = deepcopy(s:prototype) for fft in [ft, substitute(ft, '\..\+$', '', ''), ''] Tlibtrace 'autolinker', fft, ft if empty(fft) let s:ft_prototypes[ft] = prototype else try let fft_ = substitute(fft, '\W', '_', 'g') let s:ft_prototypes[ft] = autolinker#ft_{fft_}#GetInstance(prototype) catch /^Vim\%((\a\+)\)\=:E117/ continue endtry endif break endfor endif let b:autolinker = copy(s:ft_prototypes[ft]) let b:autolinker.defs = b:autolinker.WordLinks() call b:autolinker.Highlight() call b:autolinker.InstallHotkey() for c in ['t', 'w', 'v', 's'] exec 'nnoremap' printf(g:autolinker#map_options, c) ':let w:autolinker_'. c '= v:count' endfor call tlib#balloon#Register('autolinker#Balloon()') let b:undo_ftplugin = 'call autolinker#DisableBuffer()'. (exists('b:undo_ftplugin') ? '|'. b:undo_ftplugin : '') endf function! autolinker#DisableBuffer() abort "{{{3 call tlib#balloon#Remove('autolinker#Balloon()') call b:autolinker.ClearHighlight() call b:autolinker.UninstallHotkey() unlet! b:autolinker endf function! autolinker#Balloon() abort "{{{3 let autolinker = autolinker#Ensure() let cfile = tlib#balloon#Expand('') let cfile = autolinker.CleanCFile(cfile) " TLogVAR cfile, tlib#sys#IsSpecial(cfile), filereadable(cfile) if !tlib#sys#IsSpecial(cfile) && filereadable(cfile) let lines = readfile(cfile) return join(lines[0 : 5], "\n") elseif isdirectory(cfile) let items = tlib#file#Glob(tlib#file#Join([cfile, '*'])) return join(items[0 : 5], "\n") endif endf " Jump to the destination descibed by the word under the cursor. Use one " of the following methods (see |g:autolinker#types|): " 1. Jump by definition (depends on filetype; by default all files in the " current directory) " 2. Jump to special URLs matching |g:autolinker#system_rx| " 3. Jump to a file in 'path' mathing the word under cursor " 4. Jump to a tag " As last resort use |g:autolinker#fallback| function! autolinker#Jump(mode) abort "{{{3 let autolinker = copy(autolinker#Ensure()) let autolinker.mode = a:mode call s:Jump(autolinker, 0) endf let s:blacklist_recursive = ['fallback', 'internal'] function! s:Jump(autolinker, recursive) abort "{{{3 Tlibtrace 'autolinker', a:recursive, a:autolinker.types for type in a:autolinker.types if !a:recursive || index(s:blacklist_recursive, type) == -1 let method = 'Jump_'. type Tlibtrace 'autolinker', method if has_key(a:autolinker, method) && call(a:autolinker[method], [], a:autolinker) Tlibtrace 'autolinker', 'ok' return 1 endif endif endfor if !a:recursive echom 'Autolinker: I can''t dance --' a:autolinker.GetCFile() let wvars = filter(w:, 'v:val =~ "^autolinker_"') if !empty(wvars) exec 'unlet!' join(keys(map(wvars, '"w:". v:val'))) endif endif return 0 endf function! autolinker#Edit(cfile) abort "{{{3 let autolinker = copy(autolinker#Ensure()) let autolinker.a_cfile = a:cfile call s:Jump(autolinker, 0) endf function! autolinker#EditInPath(cfile) abort "{{{3 let path = filter(split(&path, ','), '!empty(v:val)') let filenames = map(path, 'tlib#file#Join([v:val, a:cfile], 1, 1)') call insert(filenames, a:cfile) let filenames = map(filenames, 'fnamemodify(v:val, ":p")') let filenames = tlib#list#Uniq(filenames) let filename = tlib#input#List('s', 'Select file:', filenames) return s:Edit(filename, '') endf function! s:GetEditCmd(what) abort "{{{3 let cmd = g:autolinker#edit_{a:what} let cnt = '' if exists('w:autolinker_t') let editdef = g:autolinker#layout.t let cnt = w:autolinker_t let special = 1 elseif exists('w:autolinker_w') let editdef = g:autolinker#layout.w let cnt = w:autolinker_w let special = 1 elseif exists('w:autolinker_v') let editdef = g:autolinker#layout.v let cnt = w:autolinker_v let special = 1 elseif exists('w:autolinker_s') let editdef = g:autolinker#layout.s let cnt = w:autolinker_s let special = 1 else let editdef = g:autolinker#layout['*'] let special = 0 endif if cnt == 0 let cnt = '' endif let editdef[0] = substitute(editdef[0], '', cnt, 'g') if !special if a:what ==# 'dir' let editdef = cmd elseif a:what ==# 'file' endif else let editdef[0] .= '|'. cmd[0] let editdef[1] = get(cmd, 1, '') endif return [editdef, special] endf function! s:Edit(filename, postprocess) abort "{{{3 Tlibtrace 'autolinker', a:filename if !empty(a:filename) let what = isdirectory(a:filename) ? 'dir' : 'file' let [editdef, special] = s:GetEditCmd(what) Tlibtrace 'autolinker', what, editdef, special call s:EditEdit(a:filename, editdef) if !empty(a:postprocess) exec a:postprocess endif return 1 endif return 0 endf function! s:EditEdit(filename, editdef) abort "{{{3 let filename = a:filename let fescape = get(a:editdef, 1, '') if !empty(fescape) let filename = call(fescape, [filename]) endif call tlib#dir#Ensure(fnamemodify(a:filename, ':p:h')) exec a:editdef[0] filename endf function! s:GetRx() abort "{{{3 if !exists('b:autolinker') return '' else let rxs = [] let rxs += map(values(b:autolinker.defs), 'v:val.rx') let rxs += g:tlib#sys#special_protocols let crx = b:autolinker.CfileGsubRx() if !empty(crx) call add(rxs, crx) endif return printf('\(%s\)', join(rxs, '\|')) endif endf function! autolinker#NextLink(n) abort "{{{3 let rx = s:GetRx() let n = abs(a:n) let flags = 'sw'. (a:n < 0 ? 'b' : '') for i in range(n) if !search(rx, flags) break endif endfor endf function! autolinker#FileSources(opts) abort "{{{3 let globs = [] let cfile_gsub = exists('b:autolinker') ? b:autolinker.cfile_gsub : g:autolinker#cfile_gsub let pattern = get(a:opts, 'glob', get(a:opts, 'deep', 1) ? '**' : '*') for [rx, subst; rest] in cfile_gsub if rx =~ '^\^' \ && (empty(g:autolinker#find_ignore_rx) || rx !~ g:autolinker#find_ignore_rx) \ && (empty(g:autolinker#find_ignore_subst) || subst !~ g:autolinker#find_ignore_subst) call add(globs, tlib#file#Join([subst, pattern])) endif endfor Tlibtrace 'autolinker', globs " TLogVAR globs return globs endf function! autolinker#CompleteFilename(ArgLead, CmdLine, CursorPos) abort "{{{3 let prototype = deepcopy(s:prototype) " TLogVAR a:ArgLead let names = filter(map(copy(g:autolinker#cfile_gsub), 'v:val[0]'), 'v:val =~# ''^\^''') let names = map(names, 'substitute(v:val, ''^\^'', "", "")') Tlibtrace 'autolinker', names if !empty(a:ArgLead) let nchars = len(a:ArgLead) let names = filter(names, 'strpart(v:val, 0, nchars) ==# a:ArgLead') Tlibtrace 'autolinker', a:ArgLead, names endif let filenames = [] if isdirectory(a:ArgLead) let dir = tlib#file#Join([a:ArgLead, '']) if a:ArgLead !=# dir call add(filenames, dir) endif let pattern = tlib#file#Join([a:ArgLead, '*']) else let pattern = a:ArgLead .'*' endif " TLogVAR pattern call extend(filenames, tlib#file#Globpath(&path, prototype.CleanCFile(pattern, 0))) " TLogVAR len(filenames) let rv = sort(names) + sort(filenames) let rv = map(rv, 'escape(v:val, '' '')') return rv endf function! s:ParseCFile(cfile) abort "{{{3 let post = matchstr(a:cfile, '@\%(/\S\+\|\d\+\)$') endf