colorful rat Ratfactor.com > Dave's Repos

vviki

An AsciiDoc-flavored wiki plugin for Vim
git clone http://ratfactor.com/repos/vviki/vviki.git

vviki/plugin/vviki.vim

Download raw file: plugin/vviki.vim

1 " Copyright (c) 2020 Dave Gauer 2 " MIT License 3 4 if exists('g:loaded_vviki') 5 finish 6 endif 7 let g:loaded_vviki = 1 8 9 " Initialize configuration defaults 10 " See 'Configuration' in the help documentation for full explanations. 11 " 12 if !exists('g:vviki_root') 13 " Default root directory for (current) wiki 14 let g:vviki_root = "~/wiki" 15 endif 16 17 if !exists('g:vviki_ext') 18 " Extension to append to pages when navigating internal links 19 let g:vviki_ext = ".adoc" 20 endif 21 22 if !exists('g:vviki_index') 23 " The start document for wiki root and subdirectories 24 " index + ext is the start filename (e.g. index.adoc) 25 let g:vviki_index = "index" 26 endif 27 28 if !exists('g:vviki_conceal_links') 29 " Use Vim's syntax concealing to temporarily hide link syntax 30 let g:vviki_conceal_links = 1 31 endif 32 33 if !exists('g:vviki_page_link_syntax') 34 " Set internal wiki page link syntax to one of: 35 " 'link' -> link:foo[My Foo] 36 " 'olink' -> olink:foo[My Foo] 37 " 'xref_hack' -> <<foo#,My Foo>> 38 let g:vviki_page_link_syntax = 'link' 39 endif 40 41 if !exists('g:vviki_visual_link_creation') 42 " Allow link creation from selected text in visual mode 43 let g:vviki_visual_link_creation = 0 44 endif 45 46 if !exists('g:vviki_links_include_ext') 47 " Internal wiki page links include the file extension. 48 " (File extension is set via g:vviki_ext.) 49 let g:vviki_links_include_ext = 0 50 endif 51 52 if !exists('g:vviki_custom_uri_function') 53 " Set to a custom function call to generate uri. 54 " If unset the URI will be equal to the selection. 55 " Make sure your function actually generates unique URIs 56 let g:vviki_custom_uri_function = '' 57 endif 58 59 " Navigation history for Backspace 60 let s:history = [] 61 62 63 " Supported link styles: 64 function! VVEnter() 65 " Attempt to match existing link under cursor, trying all link syntax 66 " types (this intentionally ignores g:vviki_page_link_syntax). 67 68 " Try to get path from AsciiDoc 'link' macro 69 " link:http://example.com[Example] - external 70 " link:page[My Page] - internal relative page 71 " link:/page[My Page] - internal absolute path to page 72 " link:../page[My Page] - internal relative path to page 73 let l:linkpath = VVGetLink() 74 if strlen(l:linkpath) > 0 75 echom "link:".l:linkpath 76 if l:linkpath =~ '^https\?://' 77 call VVGoUrl(l:linkpath) 78 else 79 call VVGoPath(l:linkpath) 80 endif 81 return 82 end 83 84 " Get path from AsciiDoc 'olink' macro (anticipating future support) 85 " olink:page[My Page] - internal relative page 86 " olink:../page[My Page] - internal relative path to page 87 let l:linkpath = VVGetOLink() 88 if strlen(l:linkpath) > 0 89 echom "olink:".l:linkpath 90 call VVGoPath(l:linkpath) 91 return 92 end 93 94 " Get path from AsciiDoc '<<xref#>>' macro (for AsciiDoctor export) 95 " <<page#,My Page>> - internal relative page 96 " <<../page#,My Page>> - internal relative path to page 97 let l:linkpath = VVGetXrefHack() 98 if strlen(l:linkpath) > 0 99 echom "xrefhack:".l:linkpath 100 call VVGoPath(l:linkpath) 101 return 102 end 103 104 105 " Did not match a link macro. Now there are three possibilities: 106 " 1. We are on whitespace 107 " 2. We are on a bare URL (http://...) 108 " 3. We are on an unlinked word 109 let l:whole_word = expand("<cWORD>") " selects all non-whitespace chars 110 let l:word = expand("<cword>") " selects only 'word' chars 111 112 " Cursor on whitespace 113 if l:whole_word == '' 114 return 115 endif 116 117 " Cursor on bare URL 118 if l:whole_word =~ '^https\?://' 119 call VVGoUrl(l:whole_word) 120 return 121 endif 122 123 " Cursor on unlinked word - make it a link! 124 let l:new_link = VVMakeLink(l:word, l:word) 125 execute "normal! ciw".l:new_link."\<ESC>" 126 endfunction 127 128 129 function! VVVisualEnter() 130 " Creates a new page link using whatever text is visually selected. 131 " Yank selection, replace with link, restore default register 132 let previous_register_contents = getreg('"') 133 normal! gvy 134 let user_selection = getreg('"') 135 let l:link = VVMakeLink(user_selection, user_selection) 136 normal! gvc 137 execute "normal! a" . l:link 138 call setreg('"', previous_register_contents) 139 endfunction 140 141 142 function! VVGetLink() 143 " Captures the <path> portion of 'link:<path>[description]' (if any) 144 " \< is Vim regex for word start boundary 145 return VVGetMatchUnderCursor('\<link:\([^[]\+\)\[[^]]\+\]') 146 endfunction 147 148 149 function! VVGetOLink() 150 " Captures the <path> portion of 'olink:<path>[description]' (if any) 151 " \< is Vim regex for word start boundary 152 return VVGetMatchUnderCursor('\<olink:\([^[]\+\)\[[^]]\+\]') 153 endfunction 154 155 156 function! VVGetXrefHack() 157 " Captures the <path> portion of '<<<path>#,description>>' (if any) 158 return VVGetMatchUnderCursor('<<\([^#]\+\)#,[^>]\+>>') 159 endfunction 160 161 162 function! VVGetMatchUnderCursor(matchrx) 163 " Grab cursor pos and current line contents 164 let l:cursor = col('.') 165 let l:linestr = getline('.') 166 167 " Loop through the regex matches on the line, see if our cursor 168 " is inside one of them. If so, return it. 169 let l:matchstart=0 170 let l:matchend=0 171 while 1 172 " Note: match() always functions as if pattern were in 'magic' mode! 173 let l:matchstart = match(l:linestr, a:matchrx, l:matchend) 174 let l:matched = matchlist(l:linestr, a:matchrx, l:matchend) 175 let l:matchend = matchend(l:linestr, a:matchrx, l:matchend) 176 177 " No match found or we're already past the cursor; done looking 178 if l:matchstart == -1 || l:matchstart > l:cursor 179 return "" 180 endif 181 182 if l:matchstart <= l:cursor && l:cursor <= l:matchend 183 return l:matched[1] 184 endif 185 endwhile 186 endfunction 187 188 189 function! VVMakeLink(uri, description) 190 " Returns string with link of desired AsciiDoc syntax 'style' 191 let l:uri = a:uri 192 193 if g:vviki_custom_uri_function != '' 194 let l:uri = call(g:vviki_custom_uri_function, []) 195 endif 196 if g:vviki_links_include_ext 197 " Attach the wiki file extension to the link URI 198 let l:uri = l:uri.g:vviki_ext 199 endif 200 if g:vviki_page_link_syntax == 'link' 201 return "link:".l:uri."[".a:description."]" 202 elseif g:vviki_page_link_syntax == 'olink' 203 return "olink:".l:uri."[".a:description."]" 204 elseif g:vviki_page_link_syntax == 'xref_hack' 205 return "<<".l:uri."#,".a:description.">>" 206 endif 207 endfunction 208 209 210 function! VVFindNextLink() 211 " Places cursor on next link of desired AsciiDoc syntax 212 if g:vviki_page_link_syntax == 'link' 213 call search('link:.\{-1,}]') 214 elseif g:vviki_page_link_syntax == 'olink' 215 call search('olink:.\{-1,}]') 216 elseif g:vviki_page_link_syntax == 'xref_hack' 217 call search('<<.\{-1,}#,.\{-1,}>>') 218 endif 219 endfunction 220 221 222 function! VVFindPreviousLink() 223 " Places cursor on next link of desired AsciiDoc syntax 224 if g:vviki_page_link_syntax == 'link' 225 call search('link:.\{-1,}]', 'b') 226 elseif g:vviki_page_link_syntax == 'olink' 227 call search('olink:.\{-1,}]', 'b') 228 elseif g:vviki_page_link_syntax == 'xref_hack' 229 call search('<<.\{-1,}#,.\{-1,}>>', 'b') 230 endif 231 endfunction 232 233 234 function! VVGoPath(path) 235 " Push current page onto history 236 call add(s:history, expand("%:p")) 237 238 let l:fname = a:path 239 240 if l:fname =~ '/$' 241 " Path points to a directory, append default 'index' page 242 let l:fname = l:fname.g:vviki_index 243 end 244 245 " fname will no longer change, we can add extension here 246 if !g:vviki_links_include_ext 247 " Links don't already include extension, add it 248 let l:fname = l:fname.g:vviki_ext 249 endif 250 251 if l:fname =~ '^/' 252 " Path absolute from wiki root 253 let l:fname = g:vviki_root."/".l:fname 254 else 255 " Path relative to current page 256 let l:fname = expand("%:p:h")."/".l:fname 257 endif 258 259 let l:fname = fnameescape(l:fname) 260 261 execute "edit ".l:fname 262 endfunction 263 264 265 function! VVGoUrl(url) 266 call system('xdg-open '.shellescape(a:url).' &') 267 endfunction 268 269 270 function! VVBack() 271 if len(s:history) < 1 272 return 273 endif 274 275 let l:last = remove(s:history, -1) 276 execute "edit ".fnameescape(l:last) 277 endfunction 278 279 280 function! VVConcealLinks() 281 " Conceal the AsciiDoc link syntax until the cursor enters the line. 282 set conceallevel=2 283 284 if g:vviki_page_link_syntax == 'link' 285 syntax region vvikiLink start=/link:/ end=/\]/ keepend 286 syntax match vvikiLinkGuts /link:[^[]\+\[/ containedin=vvikiLink contained conceal 287 syntax match vvikiLinkGuts /\]/ containedin=vvikiLink contained conceal 288 elseif g:vviki_page_link_syntax == 'olink' 289 syntax region vvikiLink start=/olink:/ end=/\]/ keepend 290 syntax match vvikiLinkGuts /olink:[^[]\+\[/ containedin=vvikiLink contained conceal 291 syntax match vvikiLinkGuts /\]/ containedin=vvikiLink contained conceal 292 elseif g:vviki_page_link_syntax == 'xref_hack' 293 syntax region vvikiLink start=/<</ end=/>>/ keepend 294 syntax match vvikiLinkGuts /<<[^>]\+#,/ containedin=vvikiLink contained conceal 295 syntax match vvikiLinkGuts />>/ containedin=vvikiLink contained conceal 296 endif 297 298 highlight link vvikiLink Macro 299 highlight link vvikiLinkGuts Comment 300 endfunction 301 302 303 function! VVSetup() 304 " Set wiki pages to automatically save 305 set autowriteall 306 307 " Map ENTER key to create/follow links 308 nnoremap <buffer><silent> <CR> :call VVEnter()<CR> 309 310 " Map BACKSPACE key to go back in history 311 nnoremap <buffer><silent> <BS> :call VVBack()<CR> 312 313 " Map TAB key to find next link in page 314 " NOTE: search() always uses 'magic' regexp mode. 315 " \{-1,} is Vim for match at least 1, non-greedy 316 nnoremap <buffer><silent> <TAB> :call VVFindNextLink()<CR> 317 " And backwards! 318 nnoremap <buffer><silent> <S-Tab> :call VVFindPreviousLink()<CR> 319 320 if g:vviki_visual_link_creation 321 vnoremap <buffer><silent> <CR> :call VVVisualEnter()<CR> 322 endif 323 324 if g:vviki_conceal_links 325 call VVConcealLinks() 326 endif 327 endfunction 328 329 function! VVShowHistory() 330 echo s:history 331 endfunction 332 333 " Detect wiki page 334 " If a buffer has the right parent directory and extension, 335 " map VViki keyboard shortcuts, etc. 336 augroup vviki 337 au! 338 execute "au BufNewFile,BufRead ".g:vviki_root."/*".g:vviki_ext." call VVSetup()" 339 augroup END 340