cucumber.vim 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. " Vim filetype plugin
  2. " Language: Cucumber
  3. " Maintainer: Tim Pope <vimNOSPAM@tpope.org>
  4. " Last Change: 2016 Aug 29
  5. " Only do this when not done yet for this buffer
  6. if (exists("b:did_ftplugin"))
  7. finish
  8. endif
  9. let b:did_ftplugin = 1
  10. let s:keepcpo= &cpo
  11. set cpo&vim
  12. setlocal formatoptions-=t formatoptions+=croql
  13. setlocal comments=:# commentstring=#\ %s
  14. setlocal omnifunc=CucumberComplete
  15. let b:undo_ftplugin = "setl fo< com< cms< ofu<"
  16. let b:cucumber_root = expand('%:p:h:s?.*[\/]\%(features\|stories\)\zs[\/].*??')
  17. if !exists("b:cucumber_steps_glob")
  18. let b:cucumber_steps_glob = b:cucumber_root.'/**/*.rb'
  19. endif
  20. if !exists("g:no_plugin_maps") && !exists("g:no_cucumber_maps")
  21. cnoremap <SID>foldopen <Bar>if &foldopen =~# 'tag'<Bar>exe 'norm! zv'<Bar>endif
  22. nnoremap <silent> <script> <buffer> [<C-D> :<C-U>exe <SID>jump('edit',v:count)<SID>foldopen<CR>
  23. nnoremap <silent> <script> <buffer> ]<C-D> :<C-U>exe <SID>jump('edit',v:count)<SID>foldopen<CR>
  24. nnoremap <silent> <script> <buffer> <C-W>d :<C-U>exe <SID>jump('split',v:count)<SID>foldopen<CR>
  25. nnoremap <silent> <script> <buffer> <C-W><C-D> :<C-U>exe <SID>jump('split',v:count)<SID>foldopen<CR>
  26. nnoremap <silent> <script> <buffer> [d :<C-U>exe <SID>jump('pedit',v:count)<CR>
  27. nnoremap <silent> <script> <buffer> ]d :<C-U>exe <SID>jump('pedit',v:count)<CR>
  28. let b:undo_ftplugin .=
  29. \ "|sil! nunmap <buffer> [<C-D>" .
  30. \ "|sil! nunmap <buffer> ]<C-D>" .
  31. \ "|sil! nunmap <buffer> <C-W>d" .
  32. \ "|sil! nunmap <buffer> <C-W><C-D>" .
  33. \ "|sil! nunmap <buffer> [d" .
  34. \ "|sil! nunmap <buffer> ]d"
  35. endif
  36. function! s:jump(command,count)
  37. let steps = s:steps('.')
  38. if len(steps) == 0 || len(steps) < a:count
  39. return 'echoerr "No matching step found"'
  40. elseif len(steps) > 1 && !a:count
  41. return 'echoerr "Multiple matching steps found"'
  42. else
  43. let c = a:count ? a:count-1 : 0
  44. return a:command.' +'.steps[c][1].' '.escape(steps[c][0],' %#')
  45. endif
  46. endfunction
  47. function! s:allsteps()
  48. let step_pattern = '\C^\s*\K\k*\>\s*(\=\s*\zs\S.\{-\}\ze\s*)\=\s*\%(do\|{\)\s*\%(|[^|]*|\s*\)\=\%($\|#\)'
  49. let steps = []
  50. for file in split(glob(b:cucumber_steps_glob),"\n")
  51. let lines = readfile(file)
  52. let num = 0
  53. for line in lines
  54. let num += 1
  55. if line =~ step_pattern
  56. let type = matchstr(line,'\w\+')
  57. let steps += [[file,num,type,matchstr(line,step_pattern)]]
  58. endif
  59. endfor
  60. endfor
  61. return steps
  62. endfunction
  63. function! s:steps(lnum)
  64. let c = match(getline(a:lnum), '\S') + 1
  65. while synIDattr(synID(a:lnum,c,1),'name') !~# '^$\|Region$'
  66. let c = c + 1
  67. endwhile
  68. let step = matchstr(getline(a:lnum)[c-1 : -1],'^\s*\zs.\{-\}\ze\s*$')
  69. return filter(s:allsteps(),'s:stepmatch(v:val[3],step)')
  70. endfunction
  71. function! s:stepmatch(receiver,target)
  72. if a:receiver =~ '^[''"].*[''"]$'
  73. let pattern = '^'.escape(substitute(a:receiver[1:-2],'$\w\+','(.*)','g'),'/').'$'
  74. elseif a:receiver =~ '^/.*/$'
  75. let pattern = a:receiver[1:-2]
  76. elseif a:receiver =~ '^%r..*.$'
  77. let pattern = escape(a:receiver[3:-2],'/')
  78. else
  79. return 0
  80. endif
  81. try
  82. let vimpattern = substitute(substitute(pattern,'\\\@<!(?:','%(','g'),'\\\@<!\*?','{-}','g')
  83. if a:target =~# '\v'.vimpattern
  84. return 1
  85. endif
  86. catch
  87. endtry
  88. if has("ruby") && pattern !~ '\\\@<!#{'
  89. ruby VIM.command("return #{if (begin; Kernel.eval('/'+VIM.evaluate('pattern')+'/'); rescue SyntaxError; end) === VIM.evaluate('a:target') then 1 else 0 end}")
  90. else
  91. return 0
  92. endif
  93. endfunction
  94. function! s:bsub(target,pattern,replacement)
  95. return substitute(a:target,'\C\\\@<!'.a:pattern,a:replacement,'g')
  96. endfunction
  97. function! CucumberComplete(findstart,base) abort
  98. let indent = indent('.')
  99. let group = synIDattr(synID(line('.'),indent+1,1),'name')
  100. let type = matchstr(group,'\Ccucumber\zs\%(Given\|When\|Then\)')
  101. let e = matchend(getline('.'),'^\s*\S\+\s')
  102. if type == '' || col('.') < col('$') || e < 0
  103. return -1
  104. endif
  105. if a:findstart
  106. return e
  107. endif
  108. let steps = []
  109. for step in s:allsteps()
  110. if step[2] ==# type
  111. if step[3] =~ '^[''"]'
  112. let steps += [step[3][1:-2]]
  113. elseif step[3] =~ '^/\^.*\$/$'
  114. let pattern = step[3][2:-3]
  115. let pattern = substitute(pattern,'\C^(?:|I )','I ','')
  116. let pattern = s:bsub(pattern,'\\[Sw]','w')
  117. let pattern = s:bsub(pattern,'\\d','1')
  118. let pattern = s:bsub(pattern,'\\[sWD]',' ')
  119. let pattern = s:bsub(pattern,'\[\^\\\="\]','_')
  120. let pattern = s:bsub(pattern,'[[:alnum:]. _-][?*]?\=','')
  121. let pattern = s:bsub(pattern,'\[\([^^]\).\{-\}\]','\1')
  122. let pattern = s:bsub(pattern,'+?\=','')
  123. let pattern = s:bsub(pattern,'(\([[:alnum:]. -]\{-\}\))','\1')
  124. let pattern = s:bsub(pattern,'\\\([[:punct:]]\)','\1')
  125. if pattern !~ '[\\()*?]'
  126. let steps += [pattern]
  127. endif
  128. endif
  129. endif
  130. endfor
  131. call filter(steps,'strpart(v:val,0,strlen(a:base)) ==# a:base')
  132. return sort(steps)
  133. endfunction
  134. let &cpo = s:keepcpo
  135. unlet s:keepcpo
  136. " vim:set sts=2 sw=2: