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