endwise.vim 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. " Location: plugin/endwise.vim
  2. " Author: Tim Pope <http://tpo.pe/>
  3. " Version: 1.3
  4. " License: Same as Vim itself. See :help license
  5. " GetLatestVimScripts: 2386 1 :AutoInstall: endwise.vim
  6. if exists("g:loaded_endwise") || v:version < 704 || &cp
  7. finish
  8. endif
  9. let g:loaded_endwise = 1
  10. augroup endwise " {{{1
  11. autocmd!
  12. autocmd FileType lua
  13. \ let b:endwise_addition = 'end' |
  14. \ let b:endwise_words = 'function,do,then' |
  15. \ let b:endwise_pattern = '^\s*\zs\%(\%(local\s\+\)\=function\)\>\%(.*\<end\>\)\@!\|\<\%(then\|do\)\ze\s*$' |
  16. \ let b:endwise_syngroups = 'luaFunction,luaStatement,luaCond,luaLocal,luaFuncKeyword,luaRepeat'
  17. autocmd FileType elixir
  18. \ let b:endwise_addition = 'end' |
  19. \ let b:endwise_words = 'do,fn' |
  20. \ let b:endwise_pattern = '.*[^.:@$]\zs\<\%(do\(:\)\@!\|fn\)\>\ze\%(.*[^.:@$]\<end\>\)\@!' |
  21. \ let b:endwise_end_pattern = '\%\(fn.*->.*\)\@<!end' |
  22. \ let b:endwise_syngroups = 'elixirBlockDefinition'
  23. autocmd FileType ruby
  24. \ let b:endwise_addition = 'end' |
  25. \ let b:endwise_words = 'module,class,def,if,unless,case,while,until,begin,do' |
  26. \ let b:endwise_pattern = '^\(.*=\)\?\s*\%(private\s\+\|protected\s\+\|public\s\+\|module_function\s\+\)*\zs\%(def\s\+[^[:space:]()]\+\s*\%(([^()]*)\)\=\s*=\)\@!\%(module\|class\|def\|if\|unless\|case\|while\|until\|for\|\|begin\)\>\%([^#]*[^.:@$#]\<end\>\)\@!\|\<do\ze\%(\s*|.*|\)\=\s*$' |
  27. \ let b:endwise_syngroups = 'rubyModule,rubyClass,rubyDefine,rubyControl,rubyConditional,rubyRepeat'
  28. autocmd FileType crystal
  29. \ let b:endwise_addition = 'end' |
  30. \ let b:endwise_words = 'module,class,lib,macro,struct,union,enum,def,if,unless,ifdef,case,while,until,for,begin,do' |
  31. \ let b:endwise_pattern = '^\(.*=\)\?\s*\%(private\s\+\|protected\s\+\|public\s\+\|abstract\s\+\)*\zs\%(module\|class\|lib\|macro\|struct\|union\|enum\|def\|if\|unless\|ifdef\|case\|while\|until\|for\|begin\)\>\%(.*[^.:@$]\<end\>\)\@!\|\<do\ze\%(\s*|.*|\)\=\s*$' |
  32. \ let b:endwise_syngroups = 'crystalModule,crystalClass,crystalLib,crystalMacro,crystalStruct,crystalEnum,crystalDefine,crystalConditional,crystalRepeat,crystalControl'
  33. autocmd FileType sh,bash,zsh
  34. \ let b:endwise_addition = '\=submatch(0)=="then" ? "fi" : submatch(0)=="case" ? "esac" : "done"' |
  35. \ let b:endwise_words = 'then,case,do' |
  36. \ let b:endwise_pattern = '\%(^\s*\zscase\>\ze\|\zs\<\%(do\|then\)\ze\s*$\)' |
  37. \ let b:endwise_syngroups = 'shConditional,shLoop,shIf,shFor,shRepeat,shCaseEsac,zshConditional,zshRepeat,zshDelimiter'
  38. autocmd FileType vb,vbnet,aspvbs
  39. \ let b:endwise_addition = 'End &' |
  40. \ let b:endwise_words = 'Function,Sub,Class,Module,Enum,Namespace' |
  41. \ let b:endwise_pattern = '\%(\<End\>.*\)\@<!\<&\>' |
  42. \ let b:endwise_syngroups = 'vbStatement,vbnetStorage,vbnetProcedure,vbnet.*Words,AspVBSStatement'
  43. autocmd FileType vim
  44. \ let b:endwise_addition = '\=submatch(0)=~"aug\\%[roup]" ? submatch(0) . " END" : "end" . submatch(0)' |
  45. \ let b:endwise_words = 'fu\%[nction],wh\%[ile],if,for,try,def,aug\%[roup]\%(\s\+\cEND\)\@!' |
  46. \ let b:endwise_end_pattern = '\%(end\%(fu\%[nction]\|wh\%[hile]\|if\|for\|try\|def\)\)\|aug\%[roup]\%(\s\+\cEND\)' |
  47. \ let b:endwise_syngroups = 'vimFuncKey,vimNotFunc,vimCommand,vimAugroupKey,vimAugroup,vimAugroupError'
  48. autocmd FileType c,cpp,xdefaults,haskell
  49. \ let b:endwise_addition = '#endif' |
  50. \ let b:endwise_words = 'if,ifdef,ifndef' |
  51. \ let b:endwise_pattern = '^\s*#\%(if\|ifdef\|ifndef\)\>' |
  52. \ let b:endwise_syngroups = 'cPreCondit,cPreConditMatch,cCppInWrapper,cCppOutWrapper,xdefaultsPreProc'
  53. autocmd FileType objc
  54. \ let b:endwise_addition = '@end' |
  55. \ let b:endwise_words = 'interface,implementation' |
  56. \ let b:endwise_pattern = '^\s*@\%(interface\|implementation\)\>' |
  57. \ let b:endwise_syngroups = 'objcObjDef'
  58. autocmd FileType make
  59. \ let b:endwise_addition = 'end&' |
  60. \ let b:endwise_words = 'ifdef,ifndef,ifeq,ifneq,define' |
  61. \ let b:endwise_pattern = '^\s*\(d\zsef\zeine\|\zsif\zen\=\(def\|eq\)\)\>' |
  62. \ let b:endwise_syngroups = 'makePreCondit,makeDefine'
  63. autocmd FileType verilog,systemverilog
  64. \ let b:endwise_addition = '\=submatch(0)==#"begin" ? "end" : "end" . submatch(0)' |
  65. \ let b:endwise_words = 'begin,module,case,function,primitive,specify,task,generate,package,interface,class,program,property,sequence,table,clocking,checker,config' |
  66. \ let b:endwise_pattern = '\<\%(begin\|module\|case\|function\|primitive\|specify\|task\|generate\|package\|interface\|class\|program\|property\|sequence\|table\|clocking\|checker\|config\)\>' |
  67. \ let b:endwise_syngroups = 'verilogConditional,verilogLabel,verilogStatement,systemverilogStatement'
  68. autocmd FileType matlab
  69. \ let b:endwise_addition = 'end' |
  70. \ let b:endwise_words = 'function,if,for,switch,while,try' |
  71. \ let b:endwise_syngroups = 'matlabStatement,matlabFunction,matlabConditional,matlabRepeat,matlabLabel,matlabExceptions'
  72. autocmd FileType htmldjango
  73. \ let b:endwise_addition = '{% end& %}' |
  74. \ let b:endwise_words = 'autoescape,block,blocktrans,cache,comment,filter,for,if,ifchanged,ifequal,ifnotequal,language,spaceless,verbatim,with' |
  75. \ let b:endwise_syngroups = 'djangoTagBlock,djangoStatement'
  76. autocmd FileType htmljinja,jinja.html
  77. \ let b:endwise_addition = '{% end& %}' |
  78. \ let b:endwise_words = 'autoescape,block,cache,call,filter,for,if,macro,raw,set,trans,with' |
  79. \ let b:endwise_syngroups = 'jinjaTagBlock,jinjaStatement'
  80. autocmd FileType snippets
  81. \ let b:endwise_addition = 'endsnippet' |
  82. \ let b:endwise_words = 'snippet' |
  83. \ let b:endwise_syngroups = 'snipSnippet,snipSnippetHeader,snipSnippetHeaderKeyword'
  84. autocmd FileType ocaml
  85. \ let b:endwise_addition = '\=submatch(0) ==# "do" ? "done" : submatch(0) =~# "match\\|try" ? "with" : "end"' |
  86. \ let b:endwise_words = 'struct,sig,begin,object,do,match,try' |
  87. \ let b:endwise_pattern = '\zs\<&\>\ze\%(.*\%(end\|done\|with\)\)\@!.*$' |
  88. \ let b:endwise_syngroups = 'ocamlStruct,ocamlStructEncl,ocamlSig,ocamlSigEncl,ocamlObject,ocamlLCIdentifier,ocamlKeyword,ocamlDo,ocamlEnd,'
  89. autocmd FileType * call s:abbrev()
  90. autocmd CmdwinEnter * call s:NeutralizeMap()
  91. autocmd VimEnter * call s:DefineMap()
  92. augroup END " }}}1
  93. function! s:abbrev() abort
  94. if get(g:, 'endwise_abbreviations', 0) && &buftype =~# '^\%(nowrite\)\=$'
  95. for word in split(get(b:, 'endwise_words', ''), ',')
  96. execute 'iabbrev <buffer><script>' word word.'<CR><SID>(endwise-append)<Space><C-U><BS>'
  97. endfor
  98. endif
  99. endfunction
  100. " Maps {{{1
  101. function! EndwiseAppend(...) abort
  102. if !a:0 || type(a:1) != type('')
  103. return "\<C-R>=EndwiseDiscretionary()\r"
  104. elseif a:1 =~# "\r" && &buftype =~# '^\%(nowrite\)\=$'
  105. return a:1 . "\<C-R>=EndwiseDiscretionary()\r"
  106. else
  107. return a:1
  108. endif
  109. endfunction
  110. function! EndwiseDiscretionary() abort
  111. return s:crend(0)
  112. endfunction
  113. function! EndwiseAlways() abort
  114. return s:crend(1)
  115. endfunction
  116. function! s:NeutralizeMap() abort
  117. if maparg('<CR>', 'i') =~# '[Ee]ndwise\|<Plug>DiscretionaryEnd'
  118. inoremap <buffer> <CR> <CR>
  119. endif
  120. endfunction
  121. imap <script><silent> <SID>(endwise-append) <C-R>=EndwiseDiscretionary()<CR>
  122. imap <script> <Plug>(endwise-append) <SID>(endwise-append)
  123. imap <script> <Plug>DiscretionaryEnd <SID>(endwise-append)
  124. function! s:DefineMap() abort
  125. let map = maparg('<CR>', 'i', 0, 1)
  126. let rhs = substitute(get(map, 'rhs', ''), '\c<sid>', '<SNR>' . get(map, 'sid') . '_', 'g')
  127. if get(g:, 'endwise_no_maps') || get(g:, 'endwise_no_mappings') || rhs =~# '[eE]ndwise\|<Plug>DiscretionaryEnd' || get(map, 'desc') =~# 'Endwise' || get(map, 'buffer')
  128. return
  129. endif
  130. let imap = get(map, 'script', rhs !~? '<plug>') || get(map, 'noremap') ? 'imap <script>' : 'imap'
  131. if get(map, 'expr') && type(get(map, 'callback')) == type(function('tr'))
  132. lua local m = vim.fn.maparg('<CR>', 'i', 0, 1); vim.api.nvim_set_keymap('i', '<CR>', m.rhs or '', { expr = true, silent = true, callback = function() return vim.fn.EndwiseAppend(vim.api.nvim_replace_termcodes(m.callback(), true, true, m.replace_keycodes)) end, desc = "EndwiseAppend() wrapped around " .. (m.desc or "Lua function") })
  133. elseif get(map, 'expr') && !empty(rhs)
  134. exe imap '<silent><expr> <CR> EndwiseAppend(' . rhs . ')'
  135. elseif rhs =~? '<cr>' || rhs =~# '<[Pp]lug>\w\+CR'
  136. exe imap '<silent> <CR>' rhs . '<SID>(endwise-append)'
  137. else
  138. imap <silent><script><expr> <CR> EndwiseAppend("<Bslash>r")
  139. endif
  140. endfunction
  141. call s:DefineMap()
  142. " }}}1
  143. " Code {{{1
  144. function! s:mysearchpair(beginpat, endpat, synidpat) abort
  145. let s:lastline = line('.')
  146. call s:synid()
  147. let line = searchpair(a:beginpat,'',a:endpat,'Wn','<SID>synid() !~# "^'.substitute(a:synidpat,'\\','\\\\','g').'$"',line('.')+50)
  148. return line
  149. endfunction
  150. function! s:crend(always) abort
  151. let n = ""
  152. if &buftype !~# '^\%(nowrite\)\=$' || !exists("b:endwise_addition") || !exists("b:endwise_words") || !exists("b:endwise_syngroups")
  153. return n
  154. endif
  155. let synids = join(map(split(b:endwise_syngroups, ','), 'hlID(v:val)'), ',')
  156. let wordchoice = '\%('.substitute(b:endwise_words,',','\\|','g').'\)'
  157. if exists("b:endwise_pattern")
  158. let beginpat = substitute(b:endwise_pattern,'&',substitute(wordchoice,'\\','\\&','g'),'g')
  159. else
  160. let beginpat = '\<'.wordchoice.'\>'
  161. endif
  162. let lnum = line('.') - 1
  163. let space = matchstr(getline(lnum),'^\s*')
  164. let col = match(getline(lnum),beginpat) + 1
  165. let word = matchstr(getline(lnum),beginpat)
  166. let endword = substitute(word,'.*',b:endwise_addition,'')
  167. let y = n.endword."\<C-O>O"
  168. if exists("b:endwise_end_pattern")
  169. let endpat = '\w\@<!'.substitute(word, '.*', substitute(b:endwise_end_pattern, '\\', '\\\\', 'g'), '').'\w\@!'
  170. elseif b:endwise_addition[0:1] ==# '\='
  171. let endpat = '\w\@<!'.endword.'\w\@!'
  172. else
  173. let endpat = '\w\@<!'.substitute('\w\+', '.*', b:endwise_addition, '').'\w\@!'
  174. endif
  175. let synidpat = '\%('.substitute(synids,',','\\|','g').'\)'
  176. if a:always
  177. return y
  178. elseif col <= 0 || synID(lnum,col,1) !~ '^'.synidpat.'$'
  179. return n
  180. elseif getline('.') !~# '^\s*$'
  181. return n
  182. endif
  183. let line = s:mysearchpair(beginpat,endpat,synidpat)
  184. " even is false if no end was found, or if the end found was less
  185. " indented than the current line
  186. let even = strlen(matchstr(getline(line),'^\s*')) >= strlen(space)
  187. if line == 0
  188. let even = 0
  189. endif
  190. if !even && line == line('.') + 1
  191. return y
  192. endif
  193. if even
  194. return n
  195. endif
  196. return y
  197. endfunction
  198. function! s:synid() abort
  199. " Checking this helps to force things to stay in sync
  200. while s:lastline < line('.')
  201. let s = synID(s:lastline,indent(s:lastline)+1,1)
  202. let s:lastline = nextnonblank(s:lastline + 1)
  203. endwhile
  204. let s = synID(line('.'),col('.'),1)
  205. let s:lastline = line('.')
  206. return s
  207. endfunction
  208. " }}}1
  209. " vim:set sw=2 sts=2: