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