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