]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/autoload/rails.vim
ca360759f6f4c45c6a17ecbfda2958b4defd8868
[rbdr/dotfiles] / vim / autoload / rails.vim
1 " autoload/rails.vim
2 " Author: Tim Pope <http://tpo.pe/>
3
4 " Install this file as autoload/rails.vim.
5
6 if exists('g:autoloaded_rails') || &cp
7 finish
8 endif
9 let g:autoloaded_rails = '4.4'
10
11 let s:cpo_save = &cpo
12 set cpo&vim
13
14 " Utility Functions {{{1
15
16 let s:app_prototype = {}
17 let s:file_prototype = {}
18 let s:buffer_prototype = {}
19 let s:readable_prototype = {}
20
21 function! s:add_methods(namespace, method_names)
22 for name in a:method_names
23 let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name)
24 endfor
25 endfunction
26
27 function! s:function(name)
28 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
29 endfunction
30
31 function! s:sub(str,pat,rep)
32 return substitute(a:str,'\v\C'.a:pat,a:rep,'')
33 endfunction
34
35 function! s:gsub(str,pat,rep)
36 return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
37 endfunction
38
39 function! s:startswith(string,prefix)
40 return strpart(a:string, 0, strlen(a:prefix)) ==# a:prefix
41 endfunction
42
43 function! s:compact(ary)
44 return s:sub(s:sub(s:gsub(a:ary,'\n\n+','\n'),'\n$',''),'^\n','')
45 endfunction
46
47 function! s:uniq(list)
48 let seen = {}
49 let i = 0
50 while i < len(a:list)
51 if has_key(seen,a:list[i])
52 call remove(a:list, i)
53 else
54 let seen[a:list[i]] = 1
55 let i += 1
56 endif
57 endwhile
58 return a:list
59 endfunction
60
61 function! s:scrub(collection,item)
62 " Removes item from a newline separated collection
63 let col = "\n" . a:collection
64 let idx = stridx(col,"\n".a:item."\n")
65 let cnt = 0
66 while idx != -1 && cnt < 100
67 let col = strpart(col,0,idx).strpart(col,idx+strlen(a:item)+1)
68 let idx = stridx(col,"\n".a:item."\n")
69 let cnt += 1
70 endwhile
71 return strpart(col,1)
72 endfunction
73
74 function! s:escarg(p)
75 return s:gsub(a:p,'[ !%#]','\\&')
76 endfunction
77
78 function! s:esccmd(p)
79 return s:gsub(a:p,'[!%#]','\\&')
80 endfunction
81
82 function! s:rquote(str)
83 " Imperfect but adequate for Ruby arguments
84 if a:str =~ '^[A-Za-z0-9_/.:-]\+$'
85 return a:str
86 elseif &shell =~? 'cmd'
87 return '"'.s:gsub(s:gsub(a:str,'\','\\'),'"','\\"').'"'
88 else
89 return "'".s:gsub(s:gsub(a:str,'\','\\'),"'","'\\\\''")."'"
90 endif
91 endfunction
92
93 function! s:sname()
94 return fnamemodify(s:file,':t:r')
95 endfunction
96
97 function! s:pop_command()
98 if exists("s:command_stack") && len(s:command_stack) > 0
99 exe remove(s:command_stack,-1)
100 endif
101 endfunction
102
103 function! s:push_chdir(...)
104 if !exists("s:command_stack") | let s:command_stack = [] | endif
105 if exists("b:rails_root") && (a:0 ? getcwd() !=# rails#app().path() : !s:startswith(getcwd(), rails#app().path()))
106 let chdir = exists("*haslocaldir") && haslocaldir() ? "lchdir " : "chdir "
107 call add(s:command_stack,chdir.s:escarg(getcwd()))
108 exe chdir.s:escarg(rails#app().path())
109 else
110 call add(s:command_stack,"")
111 endif
112 endfunction
113
114 function! s:app_path(...) dict
115 return join([self.root]+a:000,'/')
116 endfunction
117
118 function! s:app_has_file(file) dict
119 return filereadable(self.path(a:file))
120 endfunction
121
122 function! s:app_find_file(name, ...) dict abort
123 let trim = strlen(self.path())+1
124 if a:0
125 let path = s:pathjoin(map(s:pathsplit(a:1),'self.path(v:val)'))
126 else
127 let path = s:pathjoin([self.path()])
128 endif
129 let suffixesadd = s:pathjoin(get(a:000,1,&suffixesadd))
130 let default = get(a:000,2,'')
131 let oldsuffixesadd = &l:suffixesadd
132 try
133 let &suffixesadd = suffixesadd
134 " Versions before 7.1.256 returned directories from findfile
135 if type(default) == type(0) && (v:version < 702 || default == -1)
136 let all = findfile(a:name,path,-1)
137 if v:version < 702
138 call filter(all,'!isdirectory(v:val)')
139 endif
140 call map(all,'s:gsub(strpart(fnamemodify(v:val,":p"),trim),"\\\\","/")')
141 return default < 0 ? all : get(all,default-1,'')
142 elseif type(default) == type(0)
143 let found = findfile(a:name,path,default)
144 else
145 let i = 1
146 let found = findfile(a:name,path)
147 while v:version < 702 && found != "" && isdirectory(found)
148 let i += 1
149 let found = findfile(a:name,path,i)
150 endwhile
151 endif
152 return found == "" ? default : s:gsub(strpart(fnamemodify(found,':p'),trim),'\\','/')
153 finally
154 let &l:suffixesadd = oldsuffixesadd
155 endtry
156 endfunction
157
158 call s:add_methods('app',['path','has_file','find_file'])
159
160 " Split a path into a list. From pathogen.vim
161 function! s:pathsplit(path) abort
162 if type(a:path) == type([]) | return copy(a:path) | endif
163 let split = split(a:path,'\\\@<!\%(\\\\\)*\zs,')
164 return map(split,'substitute(v:val,''\\\([\\, ]\)'',''\1'',"g")')
165 endfunction
166
167 " Convert a list to a path. From pathogen.vim
168 function! s:pathjoin(...) abort
169 let i = 0
170 let path = ""
171 while i < a:0
172 if type(a:000[i]) == type([])
173 let list = a:000[i]
174 let j = 0
175 while j < len(list)
176 let escaped = substitute(list[j],'[\\, ]','\\&','g')
177 if exists("+shellslash") && !&shellslash
178 let escaped = substitute(escaped,'^\(\w:\\\)\\','\1','')
179 endif
180 let path .= ',' . escaped
181 let j += 1
182 endwhile
183 else
184 let path .= "," . a:000[i]
185 endif
186 let i += 1
187 endwhile
188 return substitute(path,'^,','','')
189 endfunction
190
191 function! s:readable_end_of(lnum) dict abort
192 if a:lnum == 0
193 return 0
194 endif
195 if self.name() =~# '\.yml$'
196 return -1
197 endif
198 let cline = self.getline(a:lnum)
199 let spc = matchstr(cline,'^\s*')
200 let endpat = '\<end\>'
201 if matchstr(self.getline(a:lnum+1),'^'.spc) && !matchstr(self.getline(a:lnum+1),'^'.spc.endpat) && matchstr(cline,endpat)
202 return a:lnum
203 endif
204 let endl = a:lnum
205 while endl <= self.line_count()
206 let endl += 1
207 if self.getline(endl) =~ '^'.spc.endpat
208 return endl
209 elseif self.getline(endl) =~ '^=begin\>'
210 while self.getline(endl) !~ '^=end\>' && endl <= self.line_count()
211 let endl += 1
212 endwhile
213 let endl += 1
214 elseif self.getline(endl) !~ '^'.spc && self.getline(endl) !~ '^\s*\%(#.*\)\=$'
215 return 0
216 endif
217 endwhile
218 return 0
219 endfunction
220
221 function! s:endof(lnum)
222 return rails#buffer().end_of(a:lnum)
223 endfunction
224
225 function! s:readable_last_opening_line(start,pattern,limit) dict abort
226 let line = a:start
227 while line > a:limit && self.getline(line) !~ a:pattern
228 let line -= 1
229 endwhile
230 let lend = self.end_of(line)
231 if line > a:limit && (lend < 0 || lend >= a:start)
232 return line
233 else
234 return -1
235 endif
236 endfunction
237
238 function! s:lastopeningline(pattern,limit,start)
239 return rails#buffer().last_opening_line(a:start,a:pattern,a:limit)
240 endfunction
241
242 function! s:readable_define_pattern() dict abort
243 if self.name() =~ '\.yml$'
244 return '^\%(\h\k*:\)\@='
245 endif
246 let define = '^\s*def\s\+\(self\.\)\='
247 if self.name() =~# '\.rake$'
248 let define .= "\\\|^\\s*\\%(task\\\|file\\)\\s\\+[:'\"]"
249 endif
250 if self.name() =~# '/schema\.rb$'
251 let define .= "\\\|^\\s*create_table\\s\\+[:'\"]"
252 endif
253 if self.type_name('test')
254 let define .= '\|^\s*test\s*[''"]'
255 endif
256 return define
257 endfunction
258
259 function! s:readable_last_method_line(start) dict abort
260 return self.last_opening_line(a:start,self.define_pattern(),0)
261 endfunction
262
263 function! s:lastmethodline(start)
264 return rails#buffer().last_method_line(a:start)
265 endfunction
266
267 function! s:readable_last_method(start) dict abort
268 let lnum = self.last_method_line(a:start)
269 let line = self.getline(lnum)
270 if line =~# '^\s*test\s*\([''"]\).*\1'
271 let string = matchstr(line,'^\s*\w\+\s*\([''"]\)\zs.*\ze\1')
272 return 'test_'.s:gsub(string,' +','_')
273 elseif lnum
274 return s:sub(matchstr(line,'\%('.self.define_pattern().'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','')
275 else
276 return ""
277 endif
278 endfunction
279
280 function! s:lastmethod(...)
281 return rails#buffer().last_method(a:0 ? a:1 : line("."))
282 endfunction
283
284 function! s:readable_last_format(start) dict abort
285 if self.type_name('view')
286 let format = fnamemodify(self.path(),':r:e')
287 if format == ''
288 return get({'rhtml': 'html', 'rxml': 'xml', 'rjs': 'js', 'haml': 'html'},fnamemodify(self.path(),':e'),'')
289 else
290 return format
291 endif
292 endif
293 let rline = self.last_opening_line(a:start,'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|',self.last_method_line(a:start))
294 if rline
295 let variable = matchstr(self.getline(rline),'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|')
296 let line = a:start
297 while line > rline
298 let match = matchstr(self.getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*')
299 if match != ''
300 return match
301 endif
302 let line -= 1
303 endwhile
304 endif
305 return ""
306 endfunction
307
308 function! s:lastformat(start)
309 return rails#buffer().last_format(a:start)
310 endfunction
311
312 function! s:format(...)
313 let format = rails#buffer().last_format(a:0 > 1 ? a:2 : line("."))
314 return format ==# '' && a:0 ? a:1 : format
315 endfunction
316
317 call s:add_methods('readable',['end_of','last_opening_line','last_method_line','last_method','last_format','define_pattern'])
318
319 let s:view_types = split('rhtml,erb,rxml,builder,rjs,mab,liquid,haml,dryml,mn,slim',',')
320
321 function! s:viewspattern()
322 return '\%('.join(s:view_types,'\|').'\)'
323 endfunction
324
325 function! s:controller(...)
326 return rails#buffer().controller_name(a:0 ? a:1 : 0)
327 endfunction
328
329 function! s:readable_controller_name(...) dict abort
330 let f = self.name()
331 if has_key(self,'getvar') && self.getvar('rails_controller') != ''
332 return self.getvar('rails_controller')
333 elseif f =~ '\<app/views/layouts/'
334 return s:sub(f,'.*<app/views/layouts/(.{-})\..*','\1')
335 elseif f =~ '\<app/views/'
336 return s:sub(f,'.*<app/views/(.{-})/\k+\.\k+%(\.\k+)=$','\1')
337 elseif f =~ '\<app/helpers/.*_helper\.rb$'
338 return s:sub(f,'.*<app/helpers/(.{-})_helper\.rb$','\1')
339 elseif f =~ '\<app/controllers/.*\.rb$'
340 return s:sub(f,'.*<app/controllers/(.{-})%(_controller)=\.rb$','\1')
341 elseif f =~ '\<app/mailers/.*\.rb$'
342 return s:sub(f,'.*<app/mailers/(.{-})\.rb$','\1')
343 elseif f =~ '\<app/apis/.*_api\.rb$'
344 return s:sub(f,'.*<app/apis/(.{-})_api\.rb$','\1')
345 elseif f =~ '\<test/functional/.*_test\.rb$'
346 return s:sub(f,'.*<test/functional/(.{-})%(_controller)=_test\.rb$','\1')
347 elseif f =~ '\<test/unit/helpers/.*_helper_test\.rb$'
348 return s:sub(f,'.*<test/unit/helpers/(.{-})_helper_test\.rb$','\1')
349 elseif f =~ '\<spec/controllers/.*_spec\.rb$'
350 return s:sub(f,'.*<spec/controllers/(.{-})%(_controller)=_spec\.rb$','\1')
351 elseif f =~ '\<spec/helpers/.*_helper_spec\.rb$'
352 return s:sub(f,'.*<spec/helpers/(.{-})_helper_spec\.rb$','\1')
353 elseif f =~ '\<spec/views/.*/\w\+_view_spec\.rb$'
354 return s:sub(f,'.*<spec/views/(.{-})/\w+_view_spec\.rb$','\1')
355 elseif f =~ '\<components/.*_controller\.rb$'
356 return s:sub(f,'.*<components/(.{-})_controller\.rb$','\1')
357 elseif f =~ '\<components/.*\.'.s:viewspattern().'$'
358 return s:sub(f,'.*<components/(.{-})/\k+\.\k+$','\1')
359 elseif f =~ '\<app/models/.*\.rb$' && self.type_name('mailer')
360 return s:sub(f,'.*<app/models/(.{-})\.rb$','\1')
361 elseif f =~ '\<\%(public\|app/assets\)/stylesheets/.*\.css\%(\.\w\+\)\=$'
362 return s:sub(f,'.*<%(public|app/assets)/stylesheets/(.{-})\.css%(\.\w+)=$','\1')
363 elseif f =~ '\<\%(public\|app/assets\)/javascripts/.*\.js\%(\.\w\+\)\=$'
364 return s:sub(f,'.*<%(public|app/assets)/javascripts/(.{-})\.js%(\.\w+)=$','\1')
365 elseif a:0 && a:1
366 return rails#pluralize(self.model_name())
367 endif
368 return ""
369 endfunction
370
371 function! s:model(...)
372 return rails#buffer().model_name(a:0 ? a:1 : 0)
373 endfunction
374
375 function! s:readable_model_name(...) dict abort
376 let f = self.name()
377 if has_key(self,'getvar') && self.getvar('rails_model') != ''
378 return self.getvar('rails_model')
379 elseif f =~ '\<app/models/.*_observer.rb$'
380 return s:sub(f,'.*<app/models/(.*)_observer\.rb$','\1')
381 elseif f =~ '\<app/models/.*\.rb$'
382 return s:sub(f,'.*<app/models/(.*)\.rb$','\1')
383 elseif f =~ '\<test/unit/.*_observer_test\.rb$'
384 return s:sub(f,'.*<test/unit/(.*)_observer_test\.rb$','\1')
385 elseif f =~ '\<test/unit/.*_test\.rb$'
386 return s:sub(f,'.*<test/unit/(.*)_test\.rb$','\1')
387 elseif f =~ '\<spec/models/.*_spec\.rb$'
388 return s:sub(f,'.*<spec/models/(.*)_spec\.rb$','\1')
389 elseif f =~ '\<\%(test\|spec\)/fixtures/.*\.\w*\~\=$'
390 return rails#singularize(s:sub(f,'.*<%(test|spec)/fixtures/(.*)\.\w*\~=$','\1'))
391 elseif f =~ '\<\%(test\|spec\)/blueprints/.*\.rb$'
392 return s:sub(f,'.*<%(test|spec)/blueprints/(.{-})%(_blueprint)=\.rb$','\1')
393 elseif f =~ '\<\%(test\|spec\)/exemplars/.*_exemplar\.rb$'
394 return s:sub(f,'.*<%(test|spec)/exemplars/(.*)_exemplar\.rb$','\1')
395 elseif f =~ '\<\%(test/\|spec/\)\=factories/.*\.rb$'
396 return s:sub(f,'.*<%(test/|spec/)=factories/(.{-})%(_factory)=\.rb$','\1')
397 elseif f =~ '\<\%(test/\|spec/\)\=fabricators/.*\.rb$'
398 return s:sub(f,'.*<%(test/|spec/)=fabricators/(.{-})%(_fabricator)=\.rb$','\1')
399 elseif a:0 && a:1
400 return rails#singularize(self.controller_name())
401 endif
402 return ""
403 endfunction
404
405 call s:add_methods('readable',['controller_name','model_name'])
406
407 function! s:readfile(path,...)
408 let nr = bufnr('^'.a:path.'$')
409 if nr < 0 && exists('+shellslash') && ! &shellslash
410 let nr = bufnr('^'.s:gsub(a:path,'/','\\').'$')
411 endif
412 if bufloaded(nr)
413 return getbufline(nr,1,a:0 ? a:1 : '$')
414 elseif !filereadable(a:path)
415 return []
416 elseif a:0
417 return readfile(a:path,'',a:1)
418 else
419 return readfile(a:path)
420 endif
421 endfunction
422
423 function! s:file_lines() dict abort
424 let ftime = getftime(self.path)
425 if ftime > get(self,last_lines_ftime,0)
426 let self.last_lines = readfile(self.path())
427 let self.last_lines_ftime = ftime
428 endif
429 return get(self,'last_lines',[])
430 endfunction
431
432 function! s:file_getline(lnum,...) dict abort
433 if a:0
434 return self.lines[lnum-1 : a:1-1]
435 else
436 return self.lines[lnum-1]
437 endif
438 endfunction
439
440 function! s:buffer_lines() dict abort
441 return self.getline(1,'$')
442 endfunction
443
444 function! s:buffer_getline(...) dict abort
445 if a:0 == 1
446 return get(call('getbufline',[self.number()]+a:000),0,'')
447 else
448 return call('getbufline',[self.number()]+a:000)
449 endif
450 endfunction
451
452 function! s:readable_line_count() dict abort
453 return len(self.lines())
454 endfunction
455
456 function! s:environment()
457 if exists('$RAILS_ENV')
458 return $RAILS_ENV
459 else
460 return "development"
461 endif
462 endfunction
463
464 function! s:Complete_environments(...)
465 return s:completion_filter(rails#app().environments(),a:0 ? a:1 : "")
466 endfunction
467
468 function! s:warn(str)
469 echohl WarningMsg
470 echomsg a:str
471 echohl None
472 " Sometimes required to flush output
473 echo ""
474 let v:warningmsg = a:str
475 endfunction
476
477 function! s:error(str)
478 echohl ErrorMsg
479 echomsg a:str
480 echohl None
481 let v:errmsg = a:str
482 endfunction
483
484 function! s:debug(str)
485 if exists("g:rails_debug") && g:rails_debug
486 echohl Debug
487 echomsg a:str
488 echohl None
489 endif
490 endfunction
491
492 function! s:buffer_getvar(varname) dict abort
493 return getbufvar(self.number(),a:varname)
494 endfunction
495
496 function! s:buffer_setvar(varname, val) dict abort
497 return setbufvar(self.number(),a:varname,a:val)
498 endfunction
499
500 call s:add_methods('buffer',['getvar','setvar'])
501
502 " }}}1
503 " "Public" Interface {{{1
504
505 " RailsRoot() is the only official public function
506
507 function! rails#underscore(str)
508 let str = s:gsub(a:str,'::','/')
509 let str = s:gsub(str,'(\u+)(\u\l)','\1_\2')
510 let str = s:gsub(str,'(\l|\d)(\u)','\1_\2')
511 let str = tolower(str)
512 return str
513 endfunction
514
515 function! rails#camelize(str)
516 let str = s:gsub(a:str,'/(.=)','::\u\1')
517 let str = s:gsub(str,'%([_-]|<)(.)','\u\1')
518 return str
519 endfunction
520
521 function! rails#singularize(word)
522 " Probably not worth it to be as comprehensive as Rails but we can
523 " still hit the common cases.
524 let word = a:word
525 if word =~? '\.js$' || word == ''
526 return word
527 endif
528 let word = s:sub(word,'eople$','ersons')
529 let word = s:sub(word,'%([Mm]ov|[aeio])@<!ies$','ys')
530 let word = s:sub(word,'xe[ns]$','xs')
531 let word = s:sub(word,'ves$','fs')
532 let word = s:sub(word,'ss%(es)=$','sss')
533 let word = s:sub(word,'s$','')
534 let word = s:sub(word,'%([nrt]ch|tatus|lias)\zse$','')
535 let word = s:sub(word,'%(nd|rt)\zsice$','ex')
536 return word
537 endfunction
538
539 function! rails#pluralize(word)
540 let word = a:word
541 if word == ''
542 return word
543 endif
544 let word = s:sub(word,'[aeio]@<!y$','ie')
545 let word = s:sub(word,'%(nd|rt)@<=ex$','ice')
546 let word = s:sub(word,'%([osxz]|[cs]h)$','&e')
547 let word = s:sub(word,'f@<!f$','ve')
548 let word .= 's'
549 let word = s:sub(word,'ersons$','eople')
550 return word
551 endfunction
552
553 function! rails#app(...)
554 let root = a:0 ? a:1 : RailsRoot()
555 " TODO: populate dynamically
556 " TODO: normalize path
557 return get(s:apps,root,0)
558 endfunction
559
560 function! rails#buffer(...)
561 return extend(extend({'#': bufnr(a:0 ? a:1 : '%')},s:buffer_prototype,'keep'),s:readable_prototype,'keep')
562 endif
563 endfunction
564
565 function! s:buffer_app() dict abort
566 if self.getvar('rails_root') != ''
567 return rails#app(self.getvar('rails_root'))
568 else
569 return 0
570 endif
571 endfunction
572
573 function! s:readable_app() dict abort
574 return self._app
575 endfunction
576
577 function! RailsRevision()
578 return 1000*matchstr(g:autoloaded_rails,'^\d\+')+matchstr(g:autoloaded_rails,'[1-9]\d*$')
579 endfunction
580
581 function! RailsRoot()
582 if exists("b:rails_root")
583 return b:rails_root
584 else
585 return ""
586 endif
587 endfunction
588
589 function! s:app_file(name)
590 return extend(extend({'_app': self, '_name': a:name}, s:file_prototype,'keep'),s:readable_prototype,'keep')
591 endfunction
592
593 function! s:file_path() dict abort
594 return self.app().path(self._name)
595 endfunction
596
597 function! s:file_name() dict abort
598 return self._name
599 endfunction
600
601 function! s:buffer_number() dict abort
602 return self['#']
603 endfunction
604
605 function! s:buffer_path() dict abort
606 return s:gsub(fnamemodify(bufname(self.number()),':p'),'\\ @!','/')
607 endfunction
608
609 function! s:buffer_name() dict abort
610 let app = self.app()
611 let f = s:gsub(resolve(fnamemodify(bufname(self.number()),':p')),'\\ @!','/')
612 let f = s:sub(f,'/$','')
613 let sep = matchstr(f,'^[^\\/]\{3,\}\zs[\\/]')
614 if sep != ""
615 let f = getcwd().sep.f
616 endif
617 if s:startswith(tolower(f),s:gsub(tolower(app.path()),'\\ @!','/')) || f == ""
618 return strpart(f,strlen(app.path())+1)
619 else
620 if !exists("s:path_warn")
621 let s:path_warn = 1
622 call s:warn("File ".f." does not appear to be under the Rails root ".self.app().path().". Please report to the rails.vim author!")
623 endif
624 return f
625 endif
626 endfunction
627
628 function! RailsFilePath()
629 if !exists("b:rails_root")
630 return ""
631 else
632 return rails#buffer().name()
633 endif
634 endfunction
635
636 function! RailsFile()
637 return RailsFilePath()
638 endfunction
639
640 function! RailsFileType()
641 if !exists("b:rails_root")
642 return ""
643 else
644 return rails#buffer().type_name()
645 end
646 endfunction
647
648 function! s:readable_calculate_file_type() dict abort
649 let f = self.name()
650 let e = fnamemodify(f,':e')
651 let r = "-"
652 let full_path = self.path()
653 let nr = bufnr('^'.full_path.'$')
654 if nr < 0 && exists('+shellslash') && ! &shellslash
655 let nr = bufnr('^'.s:gsub(full_path,'/','\\').'$')
656 endif
657 if f == ""
658 let r = f
659 elseif nr > 0 && getbufvar(nr,'rails_file_type') != ''
660 return getbufvar(nr,'rails_file_type')
661 elseif f =~ '_controller\.rb$' || f =~ '\<app/controllers/.*\.rb$'
662 if join(s:readfile(full_path,50),"\n") =~ '\<wsdl_service_name\>'
663 let r = "controller-api"
664 else
665 let r = "controller"
666 endif
667 elseif f =~ '_api\.rb'
668 let r = "api"
669 elseif f =~ '\<test/test_helper\.rb$'
670 let r = "test"
671 elseif f =~ '\<spec/spec_helper\.rb$'
672 let r = "spec"
673 elseif f =~ '_helper\.rb$'
674 let r = "helper"
675 elseif f =~ '\<app/metal/.*\.rb$'
676 let r = "metal"
677 elseif f =~ '\<app/mailers/.*\.rb'
678 let r = "mailer"
679 elseif f =~ '\<app/models/'
680 let top = join(s:readfile(full_path,50),"\n")
681 let class = matchstr(top,'\<Acti\w\w\u\w\+\%(::\h\w*\)\+\>')
682 if class == "ActiveResource::Base"
683 let class = "ares"
684 let r = "model-ares"
685 elseif class == 'ActionMailer::Base'
686 let r = "mailer"
687 elseif class != ''
688 let class = tolower(s:gsub(class,'[^A-Z]',''))
689 let r = "model-".class
690 elseif f =~ '_mailer\.rb$'
691 let r = "mailer"
692 elseif top =~ '\<\%(validates_\w\+_of\|set_\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>'
693 let r = "model-arb"
694 else
695 let r = "model"
696 endif
697 elseif f =~ '\<app/views/layouts\>.*\.'
698 let r = "view-layout-" . e
699 elseif f =~ '\<\%(app/views\|components\)/.*/_\k\+\.\k\+\%(\.\k\+\)\=$'
700 let r = "view-partial-" . e
701 elseif f =~ '\<app/views\>.*\.' || f =~ '\<components/.*/.*\.'.s:viewspattern().'$'
702 let r = "view-" . e
703 elseif f =~ '\<test/unit/.*_test\.rb$'
704 let r = "test-unit"
705 elseif f =~ '\<test/functional/.*_test\.rb$'
706 let r = "test-functional"
707 elseif f =~ '\<test/integration/.*_test\.rb$'
708 let r = "test-integration"
709 elseif f =~ '\<spec/lib/.*_spec\.rb$'
710 let r = 'spec-lib'
711 elseif f =~ '\<lib/.*\.rb$'
712 let r = 'lib'
713 elseif f =~ '\<spec/\w*s/.*_spec\.rb$'
714 let r = s:sub(f,'.*<spec/(\w*)s/.*','spec-\1')
715 elseif f =~ '\<features/.*\.feature$'
716 let r = 'cucumber-feature'
717 elseif f =~ '\<features/step_definitions/.*_steps\.rb$'
718 let r = 'cucumber-steps'
719 elseif f =~ '\<features/.*\.rb$'
720 let r = 'cucumber'
721 elseif f =~ '\<\%(test\|spec\)/fixtures\>'
722 if e == "yml"
723 let r = "fixtures-yaml"
724 else
725 let r = "fixtures" . (e == "" ? "" : "-" . e)
726 endif
727 elseif f =~ '\<test/.*_test\.rb'
728 let r = "test"
729 elseif f =~ '\<spec/.*_spec\.rb'
730 let r = "spec"
731 elseif f =~ '\<spec/support/.*\.rb'
732 let r = "spec"
733 elseif f =~ '\<db/migrate\>'
734 let r = "db-migration"
735 elseif f=~ '\<db/schema\.rb$'
736 let r = "db-schema"
737 elseif f =~ '\<vendor/plugins/.*/recipes/.*\.rb$' || f =~ '\.rake$' || f =~ '\<\%(Rake\|Cap\)file$' || f =~ '\<config/deploy\.rb$'
738 let r = "task"
739 elseif f =~ '\<log/.*\.log$'
740 let r = "log"
741 elseif e == "css" || e =~ "s[ac]ss" || e == "less"
742 let r = "stylesheet-".e
743 elseif e == "js"
744 let r = "javascript"
745 elseif e == "coffee"
746 let r = "javascript-coffee"
747 elseif e == "html"
748 let r = e
749 elseif f =~ '\<config/routes\>.*\.rb$'
750 let r = "config-routes"
751 elseif f =~ '\<config/'
752 let r = "config"
753 endif
754 return r
755 endfunction
756
757 function! s:buffer_type_name(...) dict abort
758 let type = getbufvar(self.number(),'rails_cached_file_type')
759 if type == ''
760 let type = self.calculate_file_type()
761 endif
762 return call('s:match_type',[type == '-' ? '' : type] + a:000)
763 endfunction
764
765 function! s:readable_type_name() dict abort
766 let type = self.calculate_file_type()
767 return call('s:match_type',[type == '-' ? '' : type] + a:000)
768 endfunction
769
770 function! s:match_type(type,...)
771 if a:0
772 return !empty(filter(copy(a:000),'a:type =~# "^".v:val."\\%(-\\|$\\)"'))
773 else
774 return a:type
775 endif
776 endfunction
777
778 function! s:app_environments() dict
779 if self.cache.needs('environments')
780 call self.cache.set('environments',self.relglob('config/environments/','**/*','.rb'))
781 endif
782 return copy(self.cache.get('environments'))
783 endfunction
784
785 function! s:app_default_locale() dict abort
786 if self.cache.needs('default_locale')
787 let candidates = map(filter(s:readfile(self.path('config/environment.rb')),'v:val =~ "^ *config.i18n.default_locale = :[\"'']\\=[A-Za-z-]\\+[\"'']\\= *$"'),'matchstr(v:val,"[A-Za-z-]\\+[\"'']\\= *$")')
788 call self.cache.set('default_locale',get(candidates,0,'en'))
789 endif
790 return self.cache.get('default_locale')
791 endfunction
792
793 function! s:app_has(feature) dict
794 let map = {
795 \'test': 'test/',
796 \'spec': 'spec/',
797 \'cucumber': 'features/',
798 \'sass': 'public/stylesheets/sass/',
799 \'lesscss': 'app/stylesheets/',
800 \'coffee': 'app/scripts/'}
801 if self.cache.needs('features')
802 call self.cache.set('features',{})
803 endif
804 let features = self.cache.get('features')
805 if !has_key(features,a:feature)
806 let path = get(map,a:feature,a:feature.'/')
807 let features[a:feature] = isdirectory(rails#app().path(path))
808 endif
809 return features[a:feature]
810 endfunction
811
812 " Returns the subset of ['test', 'spec', 'cucumber'] present on the app.
813 function! s:app_test_suites() dict
814 return filter(['test','spec','cucumber'],'self.has(v:val)')
815 endfunction
816
817 call s:add_methods('app',['default_locale','environments','file','has','test_suites'])
818 call s:add_methods('file',['path','name','lines','getline'])
819 call s:add_methods('buffer',['app','number','path','name','lines','getline','type_name'])
820 call s:add_methods('readable',['app','calculate_file_type','type_name','line_count'])
821
822 " }}}1
823 " Ruby Execution {{{1
824
825 function! s:app_ruby_shell_command(cmd) dict abort
826 if self.path() =~ '://'
827 return "ruby ".a:cmd
828 else
829 return "ruby -C ".s:rquote(self.path())." ".a:cmd
830 endif
831 endfunction
832
833 function! s:app_script_shell_command(cmd) dict abort
834 if self.has_file('script/rails') && a:cmd !~# '^rails\>'
835 let cmd = 'script/rails '.a:cmd
836 else
837 let cmd = 'script/'.a:cmd
838 endif
839 return self.ruby_shell_command(cmd)
840 endfunction
841
842 function! s:app_background_script_command(cmd) dict abort
843 let cmd = s:esccmd(self.script_shell_command(a:cmd))
844 if has_key(self,'options') && has_key(self.options,'gnu_screen')
845 let screen = self.options.gnu_screen
846 else
847 let screen = g:rails_gnu_screen
848 endif
849 if has("gui_win32")
850 if &shellcmdflag == "-c" && ($PATH . &shell) =~? 'cygwin'
851 silent exe "!cygstart -d ".s:rquote(self.path())." ruby ".a:cmd
852 else
853 exe "!start ".cmd
854 endif
855 elseif exists("$STY") && !has("gui_running") && screen && executable("screen")
856 silent exe "!screen -ln -fn -t ".s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.cmd
857 elseif exists("$TMUX") && !has("gui_running") && screen && executable("tmux")
858 silent exe '!tmux new-window -d -n "'.s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').'" "'.cmd.'"'
859 else
860 exe "!".cmd
861 endif
862 return v:shell_error
863 endfunction
864
865 function! s:app_execute_script_command(cmd) dict abort
866 exe '!'.s:esccmd(self.script_shell_command(a:cmd))
867 return v:shell_error
868 endfunction
869
870 function! s:app_lightweight_ruby_eval(ruby,...) dict abort
871 let def = a:0 ? a:1 : ""
872 if !executable("ruby")
873 return def
874 endif
875 let args = '-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby)
876 let cmd = self.ruby_shell_command(args)
877 " If the shell is messed up, this command could cause an error message
878 silent! let results = system(cmd)
879 return v:shell_error == 0 ? results : def
880 endfunction
881
882 function! s:app_eval(ruby,...) dict abort
883 let def = a:0 ? a:1 : ""
884 if !executable("ruby")
885 return def
886 endif
887 let args = "-r./config/boot -r ".s:rquote(self.path("config/environment"))." -e ".s:rquote(a:ruby)
888 let cmd = self.ruby_shell_command(args)
889 " If the shell is messed up, this command could cause an error message
890 silent! let results = system(cmd)
891 return v:shell_error == 0 ? results : def
892 endfunction
893
894 call s:add_methods('app', ['ruby_shell_command','script_shell_command','execute_script_command','background_script_command','lightweight_ruby_eval','eval'])
895
896 " }}}1
897 " Commands {{{1
898
899 function! s:prephelp()
900 let fn = fnamemodify(s:file,':h:h').'/doc/'
901 if filereadable(fn.'rails.txt')
902 if !filereadable(fn.'tags') || getftime(fn.'tags') <= getftime(fn.'rails.txt')
903 silent! helptags `=fn`
904 endif
905 endif
906 endfunction
907
908 function! RailsHelpCommand(...)
909 call s:prephelp()
910 let topic = a:0 ? a:1 : ""
911 if topic == "" || topic == "-"
912 return "help rails"
913 elseif topic =~ '^g:'
914 return "help ".topic
915 elseif topic =~ '^-'
916 return "help rails".topic
917 else
918 return "help rails-".topic
919 endif
920 endfunction
921
922 function! s:BufCommands()
923 call s:BufFinderCommands()
924 call s:BufNavCommands()
925 call s:BufScriptWrappers()
926 command! -buffer -bar -nargs=? -bang -count -complete=customlist,s:Complete_rake Rake :call s:Rake(<bang>0,!<count> && <line1> ? -1 : <count>,<q-args>)
927 command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rpreview :call s:Preview(<bang>0,<line1>,<q-args>)
928 command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_environments Rlog :call s:Log(<bang>0,<q-args>)
929 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_set Rset :call s:Set(<bang>0,<f-args>)
930 command! -buffer -bar -nargs=0 Rtags :call rails#app().tags_command()
931 " Embedding all this logic directly into the command makes the error
932 " messages more concise.
933 command! -buffer -bar -nargs=? -bang Rdoc :
934 \ if <bang>0 || <q-args> =~ "^\\([:'-]\\|g:\\)" |
935 \ exe RailsHelpCommand(<q-args>) |
936 \ else | call s:Doc(<bang>0,<q-args>) | endif
937 command! -buffer -bar -nargs=0 -bang Rrefresh :if <bang>0|unlet! g:autoloaded_rails|source `=s:file`|endif|call s:Refresh(<bang>0)
938 if exists(":NERDTree")
939 command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rtree :NERDTree `=rails#app().path(<f-args>)`
940 endif
941 if exists("g:loaded_dbext")
942 command! -buffer -bar -nargs=? -complete=customlist,s:Complete_environments Rdbext :call s:BufDatabase(2,<q-args>)|let b:dbext_buffer_defaulted = 1
943 endif
944 let ext = expand("%:e")
945 if ext =~ s:viewspattern()
946 " TODO: complete controller names with trailing slashes here
947 command! -buffer -bar -bang -nargs=? -range -complete=customlist,s:controllerList Rextract :<line1>,<line2>call s:Extract(<bang>0,<f-args>)
948 endif
949 if RailsFilePath() =~ '\<db/migrate/.*\.rb$'
950 command! -buffer -bar Rinvert :call s:Invert(<bang>0)
951 endif
952 endfunction
953
954 function! s:Doc(bang, string)
955 if a:string != ""
956 if exists("g:rails_search_url")
957 let query = substitute(a:string,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
958 let url = printf(g:rails_search_url, query)
959 else
960 return s:error("specify a g:rails_search_url with %s for a query placeholder")
961 endif
962 elseif isdirectory(rails#app().path("doc/api/classes"))
963 let url = rails#app().path("/doc/api/index.html")
964 elseif s:getpidfor("0.0.0.0","8808") > 0
965 let url = "http://localhost:8808"
966 else
967 let url = "http://api.rubyonrails.org"
968 endif
969 call s:initOpenURL()
970 if exists(":OpenURL")
971 exe "OpenURL ".s:escarg(url)
972 else
973 return s:error("No :OpenURL command found")
974 endif
975 endfunction
976
977 function! s:Log(bang,arg)
978 if a:arg == ""
979 let lf = "log/".s:environment().".log"
980 else
981 let lf = "log/".a:arg.".log"
982 endif
983 let size = getfsize(rails#app().path(lf))
984 if size >= 1048576
985 call s:warn("Log file is ".((size+512)/1024)."KB. Consider :Rake log:clear")
986 endif
987 if a:bang
988 exe "cgetfile ".lf
989 clast
990 else
991 if exists(":Tail")
992 Tail `=rails#app().path(lf)`
993 else
994 pedit `=rails#app().path(lf)`
995 endif
996 endif
997 endfunction
998
999 function! rails#new_app_command(bang,...)
1000 if a:0 == 0
1001 let msg = "rails.vim ".g:autoloaded_rails
1002 if a:bang && exists('b:rails_root') && rails#buffer().type_name() == ''
1003 echo msg." (Rails)"
1004 elseif a:bang && exists('b:rails_root')
1005 echo msg." (Rails-".rails#buffer().type_name().")"
1006 elseif a:bang
1007 echo msg
1008 else
1009 !rails
1010 endif
1011 return
1012 endif
1013 let args = map(copy(a:000),'expand(v:val)')
1014 if a:bang
1015 let args = ['--force'] + args
1016 endif
1017 exe '!rails '.join(map(copy(args),'s:rquote(v:val)'),' ')
1018 for dir in args
1019 if dir !~# '^-' && filereadable(dir.'/'.g:rails_default_file)
1020 edit `=dir.'/'.g:rails_default_file`
1021 return
1022 endif
1023 endfor
1024 endfunction
1025
1026 function! s:app_tags_command() dict
1027 if exists("g:Tlist_Ctags_Cmd")
1028 let cmd = g:Tlist_Ctags_Cmd
1029 elseif executable("exuberant-ctags")
1030 let cmd = "exuberant-ctags"
1031 elseif executable("ctags-exuberant")
1032 let cmd = "ctags-exuberant"
1033 elseif executable("ctags")
1034 let cmd = "ctags"
1035 elseif executable("ctags.exe")
1036 let cmd = "ctags.exe"
1037 else
1038 return s:error("ctags not found")
1039 endif
1040 exe '!'.cmd.' -f '.s:escarg(self.path("tmp/tags")).' -R --langmap="ruby:+.rake.builder.rjs" '.g:rails_ctags_arguments.' '.s:escarg(self.path())
1041 endfunction
1042
1043 call s:add_methods('app',['tags_command'])
1044
1045 function! s:Refresh(bang)
1046 if exists("g:rubycomplete_rails") && g:rubycomplete_rails && has("ruby") && exists('g:rubycomplete_completions')
1047 silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
1048 silent! ruby if defined?(ActiveSupport::Dependencies); ActiveSupport::Dependencies.clear; elsif defined?(Dependencies); Dependencies.clear; end
1049 if a:bang
1050 silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
1051 endif
1052 endif
1053 call rails#app().cache.clear()
1054 silent doautocmd User BufLeaveRails
1055 if a:bang
1056 for key in keys(s:apps)
1057 if type(s:apps[key]) == type({})
1058 call s:apps[key].cache.clear()
1059 endif
1060 call extend(s:apps[key],filter(copy(s:app_prototype),'type(v:val) == type(function("tr"))'),'force')
1061 endfor
1062 endif
1063 let i = 1
1064 let max = bufnr('$')
1065 while i <= max
1066 let rr = getbufvar(i,"rails_root")
1067 if rr != ""
1068 call setbufvar(i,"rails_refresh",1)
1069 endif
1070 let i += 1
1071 endwhile
1072 silent doautocmd User BufEnterRails
1073 endfunction
1074
1075 function! s:RefreshBuffer()
1076 if exists("b:rails_refresh") && b:rails_refresh
1077 let oldroot = b:rails_root
1078 unlet! b:rails_root
1079 let b:rails_refresh = 0
1080 call RailsBufInit(oldroot)
1081 unlet! b:rails_refresh
1082 endif
1083 endfunction
1084
1085 " }}}1
1086 " Rake {{{1
1087
1088 function! s:app_rake_tasks() dict
1089 if self.cache.needs('rake_tasks')
1090 call s:push_chdir()
1091 try
1092 let lines = split(system("rake -T"),"\n")
1093 finally
1094 call s:pop_command()
1095 endtry
1096 if v:shell_error != 0
1097 return []
1098 endif
1099 call map(lines,'matchstr(v:val,"^rake\\s\\+\\zs\\S*")')
1100 call filter(lines,'v:val != ""')
1101 call self.cache.set('rake_tasks',lines)
1102 endif
1103 return self.cache.get('rake_tasks')
1104 endfunction
1105
1106 call s:add_methods('app', ['rake_tasks'])
1107
1108 let s:efm_backtrace='%D(in\ %f),'
1109 \.'%\\s%#from\ %f:%l:%m,'
1110 \.'%\\s%#from\ %f:%l:,'
1111 \.'%\\s#{RAILS_ROOT}/%f:%l:\ %#%m,'
1112 \.'%\\s%##\ %f:%l:%m,'
1113 \.'%\\s%##\ %f:%l,'
1114 \.'%\\s%#[%f:%l:\ %#%m,'
1115 \.'%\\s%#%f:%l:\ %#%m,'
1116 \.'%\\s%#%f:%l:,'
1117 \.'%m\ [%f:%l]:'
1118
1119 function! s:makewithruby(arg,bang,...)
1120 let old_make = &makeprg
1121 try
1122 let &l:makeprg = rails#app().ruby_shell_command(a:arg)
1123 exe 'make'.(a:bang ? '!' : '')
1124 if !a:bang
1125 cwindow
1126 endif
1127 finally
1128 let &l:makeprg = old_make
1129 endtry
1130 endfunction
1131
1132 function! s:Rake(bang,lnum,arg)
1133 let self = rails#app()
1134 let lnum = a:lnum < 0 ? 0 : a:lnum
1135 let old_makeprg = &l:makeprg
1136 let old_errorformat = &l:errorformat
1137 try
1138 if exists('b:bundler_root') && b:bundler_root ==# rails#app().path()
1139 let &l:makeprg = 'bundle exec rake'
1140 else
1141 let &l:makeprg = 'rake'
1142 endif
1143 let &l:errorformat = s:efm_backtrace
1144 let arg = a:arg
1145 if &filetype == "ruby" && arg == '' && g:rails_modelines
1146 let mnum = s:lastmethodline(lnum)
1147 let str = getline(mnum)."\n".getline(mnum+1)."\n".getline(mnum+2)."\n"
1148 let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|$\)'
1149 let mat = matchstr(str,'#\s*rake'.pat)
1150 let mat = s:sub(mat,'\s+$','')
1151 if mat != ""
1152 let arg = mat
1153 endif
1154 endif
1155 if arg == ''
1156 let opt = s:getopt('task','bl')
1157 if opt != ''
1158 let arg = opt
1159 else
1160 let arg = rails#buffer().default_rake_task(lnum)
1161 endif
1162 endif
1163 if !has_key(self,'options') | let self.options = {} | endif
1164 if arg == '-'
1165 let arg = get(self.options,'last_rake_task','')
1166 endif
1167 let self.options['last_rake_task'] = arg
1168 let withrubyargs = '-r ./config/boot -r '.s:rquote(self.path('config/environment')).' -e "puts \%((in \#{Dir.getwd}))" '
1169 if arg =~# '^notes\>'
1170 let &l:errorformat = '%-P%f:,\ \ *\ [%*[\ ]%l]\ [%t%*[^]]] %m,\ \ *\ [%*[\ ]%l] %m,%-Q'
1171 " %D to chdir is apparently incompatible with %P multiline messages
1172 call s:push_chdir(1)
1173 exe 'make! '.arg
1174 call s:pop_command()
1175 if !a:bang
1176 cwindow
1177 endif
1178 elseif arg =~# '^\%(stats\|routes\|secret\|time:zones\|db:\%(charset\|collation\|fixtures:identify\>.*\|migrate:status\|version\)\)\%([: ]\|$\)'
1179 let &l:errorformat = '%D(in\ %f),%+G%.%#'
1180 exe 'make! '.arg
1181 if !a:bang
1182 copen
1183 endif
1184 elseif arg =~ '^preview\>'
1185 exe (lnum == 0 ? '' : lnum).'R'.s:gsub(arg,':','/')
1186 elseif arg =~ '^runner:'
1187 let arg = s:sub(arg,'^runner:','')
1188 let root = matchstr(arg,'%\%(:\w\)*')
1189 let file = expand(root).matchstr(arg,'%\%(:\w\)*\zs.*')
1190 if file =~ '#.*$'
1191 let extra = " -- -n ".matchstr(file,'#\zs.*')
1192 let file = s:sub(file,'#.*','')
1193 else
1194 let extra = ''
1195 endif
1196 if self.has_file(file) || self.has_file(file.'.rb')
1197 call s:makewithruby(withrubyargs.'-r"'.file.'"'.extra,a:bang,file !~# '_\%(spec\|test\)\%(\.rb\)\=$')
1198 else
1199 call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg)),a:bang)
1200 endif
1201 elseif arg == 'run' || arg == 'runner'
1202 call s:makewithruby(withrubyargs.'-r"'.RailsFilePath().'"',a:bang,RailsFilePath() !~# '_\%(spec\|test\)\%(\.rb\)\=$')
1203 elseif arg =~ '^run:'
1204 let arg = s:sub(arg,'^run:','')
1205 let arg = s:sub(arg,'^\%:h',expand('%:h'))
1206 let arg = s:sub(arg,'^%(\%|$|#@=)',expand('%'))
1207 let arg = s:sub(arg,'#(\w+[?!=]=)$',' -- -n\1')
1208 call s:makewithruby(withrubyargs.'-r'.arg,a:bang,arg !~# '_\%(spec\|test\)\.rb$')
1209 else
1210 exe 'make! '.arg
1211 if !a:bang
1212 cwindow
1213 endif
1214 endif
1215 finally
1216 let &l:errorformat = old_errorformat
1217 let &l:makeprg = old_makeprg
1218 endtry
1219 endfunction
1220
1221 function! s:readable_default_rake_task(lnum) dict abort
1222 let app = self.app()
1223 let lnum = a:lnum < 0 ? 0 : a:lnum
1224 if self.getvar('&buftype') == 'quickfix'
1225 return '-'
1226 elseif self.getline(lnum) =~# '# rake '
1227 return matchstr(self.getline(lnum),'\C# rake \zs.*')
1228 elseif self.getline(self.last_method_line(lnum)-1) =~# '# rake '
1229 return matchstr(self.getline(self.last_method_line(lnum)-1),'\C# rake \zs.*')
1230 elseif self.getline(self.last_method_line(lnum)) =~# '# rake '
1231 return matchstr(self.getline(self.last_method_line(lnum)),'\C# rake \zs.*')
1232 elseif self.getline(1) =~# '# rake ' && !lnum
1233 return matchstr(self.getline(1),'\C# rake \zs.*')
1234 elseif self.type_name('config-routes')
1235 return 'routes'
1236 elseif self.type_name('fixtures-yaml') && lnum
1237 return "db:fixtures:identify LABEL=".self.last_method(lnum)
1238 elseif self.type_name('fixtures') && lnum == 0
1239 return "db:fixtures:load FIXTURES=".s:sub(fnamemodify(self.name(),':r'),'^.{-}/fixtures/','')
1240 elseif self.type_name('task')
1241 let mnum = self.last_method_line(lnum)
1242 let line = getline(mnum)
1243 " We can't grab the namespace so only run tasks at the start of the line
1244 if line =~# '^\%(task\|file\)\>'
1245 return self.last_method(a:lnum)
1246 else
1247 return matchstr(self.getline(1),'\C# rake \zs.*')
1248 endif
1249 elseif self.type_name('spec')
1250 if self.name() =~# '\<spec/spec_helper\.rb$'
1251 return 'spec'
1252 elseif lnum > 0
1253 return 'spec SPEC="'.self.path().'":'.lnum
1254 else
1255 return 'spec SPEC="'.self.path().'"'
1256 endif
1257 elseif self.type_name('test')
1258 let meth = self.last_method(lnum)
1259 if meth =~ '^test_'
1260 let call = " -n".meth.""
1261 else
1262 let call = ""
1263 endif
1264 if self.type_name('test-unit','test-functional','test-integration')
1265 return s:sub(s:gsub(self.type_name(),'-',':'),'unit$|functional$','&s').' TEST="'.self.path().'"'.s:sub(call,'^ ',' TESTOPTS=')
1266 elseif self.name() =~# '\<test/test_helper\.rb$'
1267 return 'test'
1268 else
1269 return 'test:recent TEST="'.self.path().'"'.s:sub(call,'^ ',' TESTOPTS=')
1270 endif
1271 elseif self.type_name('db-migration')
1272 let ver = matchstr(self.name(),'\<db/migrate/0*\zs\d*\ze_')
1273 if ver != ""
1274 let method = self.last_method(lnum)
1275 if method == "down" || lnum == 1
1276 return "db:migrate:down VERSION=".ver
1277 elseif method == "up" || lnum == line('$')
1278 return "db:migrate:up VERSION=".ver
1279 elseif lnum > 0
1280 return "db:migrate:down db:migrate:up VERSION=".ver
1281 else
1282 return "db:migrate VERSION=".ver
1283 endif
1284 else
1285 return 'db:migrate'
1286 endif
1287 elseif self.name() =~# '\<db/seeds\.rb$'
1288 return 'db:seed'
1289 elseif self.type_name('controller') && lnum
1290 let lm = self.last_method(lnum)
1291 if lm != ''
1292 " rake routes doesn't support ACTION... yet...
1293 return 'routes CONTROLLER='.self.controller_name().' ACTION='.lm
1294 else
1295 return 'routes CONTROLLER='.self.controller_name()
1296 endif
1297 elseif app.has('spec') && self.name() =~# '^app/.*\.\w\+$' && app.has_file(s:sub(self.name(),'^app/(.*)\.\w\+$','spec/\1_spec.rb'))
1298 return 'spec SPEC="'.fnamemodify(s:sub(self.name(),'<app/','spec/'),':p:r').'_spec.rb"'
1299 elseif app.has('spec') && self.name() =~# '^app/.*\.\w\+$' && app.has_file(s:sub(self.name(),'^app/(.*)$','spec/\1_spec.rb'))
1300 return 'spec SPEC="'.fnamemodify(s:sub(self.name(),'<app/','spec/'),':p').'_spec.rb"'
1301 elseif self.type_name('model')
1302 return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/models/','test/unit/'),':p:r').'_test.rb"'
1303 elseif self.type_name('api','mailer')
1304 return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/%(apis|mailers|models)/','test/functional/'),':p:r').'_test.rb"'
1305 elseif self.type_name('helper')
1306 return 'test:units TEST="'.fnamemodify(s:sub(self.name(),'<app/','test/unit/'),':p:r').'_test.rb"'
1307 elseif self.type_name('controller','helper','view')
1308 if self.name() =~ '\<app/' && s:controller() !~# '^\%(application\)\=$'
1309 return 'test:functionals TEST="'.s:escarg(app.path('test/functional/'.s:controller().'_controller_test.rb')).'"'
1310 else
1311 return 'test:functionals'
1312 endif
1313 elseif self.type_name('cucumber-feature')
1314 if lnum > 0
1315 return 'cucumber FEATURE="'.self.path().'":'.lnum
1316 else
1317 return 'cucumber FEATURE="'.self.path().'"'
1318 endif
1319 elseif self.type_name('cucumber')
1320 return 'cucumber'
1321 else
1322 return ''
1323 endif
1324 endfunction
1325
1326 function! s:Complete_rake(A,L,P)
1327 return s:completion_filter(rails#app().rake_tasks(),a:A)
1328 endfunction
1329
1330 call s:add_methods('readable',['default_rake_task'])
1331
1332 " }}}1
1333 " Preview {{{1
1334
1335 function! s:initOpenURL()
1336 if !exists(":OpenURL")
1337 if has("gui_mac") || has("gui_macvim") || exists("$SECURITYSESSIONID")
1338 command -bar -nargs=1 OpenURL :!open <args>
1339 elseif has("gui_win32")
1340 command -bar -nargs=1 OpenURL :!start cmd /cstart /b <args>
1341 elseif executable("sensible-browser")
1342 command -bar -nargs=1 OpenURL :!sensible-browser <args>
1343 endif
1344 endif
1345 endfunction
1346
1347 function! s:scanlineforuris(line)
1348 let url = matchstr(a:line,"\\v\\C%(%(GET|PUT|POST|DELETE)\\s+|\\w+://[^/]*)/[^ \n\r\t<>\"]*[^] .,;\n\r\t<>\":]")
1349 if url =~ '\C^\u\+\s\+'
1350 let method = matchstr(url,'^\u\+')
1351 let url = matchstr(url,'\s\+\zs.*')
1352 if method !=? "GET"
1353 let url .= (url =~ '?' ? '&' : '?') . '_method='.tolower(method)
1354 endif
1355 endif
1356 if url != ""
1357 return [url]
1358 else
1359 return []
1360 endif
1361 endfunction
1362
1363 function! s:readable_preview_urls(lnum) dict abort
1364 let urls = []
1365 let start = self.last_method_line(a:lnum) - 1
1366 while start > 0 && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$'
1367 let urls = s:scanlineforuris(self.getline(start)) + urls
1368 let start -= 1
1369 endwhile
1370 let start = 1
1371 while start < self.line_count() && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$'
1372 let urls += s:scanlineforuris(self.getline(start))
1373 let start += 1
1374 endwhile
1375 if has_key(self,'getvar') && self.getvar('rails_preview') != ''
1376 let url += [self.getvar('rails_preview')]
1377 end
1378 if self.name() =~ '^public/stylesheets/sass/'
1379 let urls = urls + [s:sub(s:sub(self.name(),'^public/stylesheets/sass/','/stylesheets/'),'\.s[ac]ss$','.css')]
1380 elseif self.name() =~ '^public/'
1381 let urls = urls + [s:sub(self.name(),'^public','')]
1382 elseif self.name() =~ '^app/assets/stylesheets/'
1383 let urls = urls + ['/assets/application.css']
1384 elseif self.name() =~ '^app/assets/javascripts/'
1385 let urls = urls + ['/assets/application.js']
1386 elseif self.name() =~ '^app/stylesheets/'
1387 let urls = urls + [s:sub(s:sub(self.name(),'^app/stylesheets/','/stylesheets/'),'\.less$','.css')]
1388 elseif self.name() =~ '^app/scripts/'
1389 let urls = urls + [s:sub(s:sub(self.name(),'^app/scripts/','/javascripts/'),'\.coffee$','.js')]
1390 elseif self.controller_name() != '' && self.controller_name() != 'application'
1391 if self.type_name('controller') && self.last_method(a:lnum) != ''
1392 let urls += ['/'.self.controller_name().'/'.self.last_method(a:lnum).'/']
1393 elseif self.type_name('controller','view-layout','view-partial')
1394 let urls += ['/'.self.controller_name().'/']
1395 elseif self.type_name('view')
1396 let urls += ['/'.s:controller().'/'.fnamemodify(self.name(),':t:r:r').'/']
1397 endif
1398 endif
1399 return urls
1400 endfunction
1401
1402 call s:add_methods('readable',['preview_urls'])
1403
1404 function! s:Preview(bang,lnum,arg)
1405 let root = s:getopt("root_url")
1406 if root == ''
1407 let root = s:getopt("url")
1408 endif
1409 let root = s:sub(root,'/$','')
1410 if a:arg =~ '://'
1411 let uri = a:arg
1412 elseif a:arg != ''
1413 let uri = root.'/'.s:sub(a:arg,'^/','')
1414 else
1415 let uri = get(rails#buffer().preview_urls(a:lnum),0,'')
1416 let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','')
1417 endif
1418 call s:initOpenURL()
1419 if exists(':OpenURL') && !a:bang
1420 exe 'OpenURL '.uri
1421 else
1422 " Work around bug where URLs ending in / get handled as FTP
1423 let url = uri.(uri =~ '/$' ? '?' : '')
1424 silent exe 'pedit '.url
1425 wincmd w
1426 if &filetype == ''
1427 if uri =~ '\.css$'
1428 setlocal filetype=css
1429 elseif uri =~ '\.js$'
1430 setlocal filetype=javascript
1431 elseif getline(1) =~ '^\s*<'
1432 setlocal filetype=xhtml
1433 endif
1434 endif
1435 call RailsBufInit(rails#app().path())
1436 map <buffer> <silent> q :bwipe<CR>
1437 wincmd p
1438 if !a:bang
1439 call s:warn("Define a :OpenURL command to use a browser")
1440 endif
1441 endif
1442 endfunction
1443
1444 function! s:Complete_preview(A,L,P)
1445 return rails#buffer().preview_urls(a:L =~ '^\d' ? matchstr(a:L,'^\d\+') : line('.'))
1446 endfunction
1447
1448 " }}}1
1449 " Script Wrappers {{{1
1450
1451 function! s:BufScriptWrappers()
1452 command! -buffer -bar -nargs=* -complete=customlist,s:Complete_script Rscript :call rails#app().script_command(<bang>0,<f-args>)
1453 command! -buffer -bar -nargs=* -complete=customlist,s:Complete_generate Rgenerate :call rails#app().generate_command(<bang>0,<f-args>)
1454 command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Rdestroy :call rails#app().destroy_command(<bang>0,<f-args>)
1455 command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Rserver :call rails#app().server_command(<bang>0,<q-args>)
1456 command! -buffer -bang -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rrunner :call rails#app().runner_command(<bang>0 ? -2 : (<count>==<line2>?<count>:-1),<f-args>)
1457 command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rp :call rails#app().runner_command(<count>==<line2>?<count>:-1,'p begin '.<f-args>.' end')
1458 command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rpp :call rails#app().runner_command(<count>==<line2>?<count>:-1,'require %{pp}; pp begin '.<f-args>.' end')
1459 command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Ry :call rails#app().runner_command(<count>==<line2>?<count>:-1,'y begin '.<f-args>.' end')
1460 endfunction
1461
1462 function! s:app_generators() dict
1463 if self.cache.needs('generators')
1464 let generators = self.relglob("vendor/plugins/","*/generators/*")
1465 let generators += self.relglob("","lib/generators/*")
1466 call filter(generators,'v:val =~ "/$"')
1467 let generators += split(glob(expand("~/.rails/generators")."/*"),"\n")
1468 call map(generators,'s:sub(v:val,"^.*[\\\\/]generators[\\\\/]\\ze.","")')
1469 call map(generators,'s:sub(v:val,"[\\\\/]$","")')
1470 call self.cache.set('generators',generators)
1471 endif
1472 return sort(split(g:rails_generators,"\n") + self.cache.get('generators'))
1473 endfunction
1474
1475 function! s:app_script_command(bang,...) dict
1476 let str = ""
1477 let cmd = a:0 ? a:1 : "console"
1478 let c = 2
1479 while c <= a:0
1480 let str .= " " . s:rquote(a:{c})
1481 let c += 1
1482 endwhile
1483 if cmd ==# "plugin"
1484 call self.cache.clear('generators')
1485 endif
1486 if a:bang || cmd =~# 'console'
1487 return self.background_script_command(cmd.str)
1488 else
1489 return self.execute_script_command(cmd.str)
1490 endif
1491 endfunction
1492
1493 function! s:app_runner_command(count,args) dict
1494 if a:count == -2
1495 return self.script_command(a:bang,"runner",a:args)
1496 else
1497 let str = self.ruby_shell_command('-r./config/boot -e "require '."'commands/runner'".'" '.s:rquote(a:args))
1498 let res = s:sub(system(str),'\n$','')
1499 if a:count < 0
1500 echo res
1501 else
1502 exe a:count.'put =res'
1503 endif
1504 endif
1505 endfunction
1506
1507 function! s:getpidfor(bind,port)
1508 if has("win32") || has("win64")
1509 let netstat = system("netstat -anop tcp")
1510 let pid = matchstr(netstat,'\<'.a:bind.':'.a:port.'\>.\{-\}LISTENING\s\+\zs\d\+')
1511 elseif executable('lsof')
1512 let pid = system("lsof -i 4tcp@".a:bind.':'.a:port."|grep LISTEN|awk '{print $2}'")
1513 let pid = s:sub(pid,'\n','')
1514 else
1515 let pid = ""
1516 endif
1517 return pid
1518 endfunction
1519
1520 function! s:app_server_command(bang,arg) dict
1521 let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+')
1522 if port == ''
1523 let port = "3000"
1524 endif
1525 " TODO: Extract bind argument
1526 let bind = "0.0.0.0"
1527 if a:bang && executable("ruby")
1528 let pid = s:getpidfor(bind,port)
1529 if pid =~ '^\d\+$'
1530 echo "Killing server with pid ".pid
1531 if !has("win32")
1532 call system("ruby -e 'Process.kill(:TERM,".pid.")'")
1533 sleep 100m
1534 endif
1535 call system("ruby -e 'Process.kill(9,".pid.")'")
1536 sleep 100m
1537 endif
1538 if a:arg == "-"
1539 return
1540 endif
1541 endif
1542 if has_key(self,'options') && has_key(self.options,'gnu_screen')
1543 let screen = self.options.gnu_screen
1544 else
1545 let screen = g:rails_gnu_screen
1546 endif
1547 if has("win32") || has("win64") || (exists("$STY") && !has("gui_running") && screen && executable("screen")) || (exists("$TMUX") && !has("gui_running") && screen && executable("tmux"))
1548 call self.background_script_command('server '.a:arg)
1549 else
1550 " --daemon would be more descriptive but lighttpd does not support it
1551 call self.execute_script_command('server '.a:arg." -d")
1552 endif
1553 call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/')
1554 endfunction
1555
1556 function! s:app_destroy_command(bang,...) dict
1557 if a:0 == 0
1558 return self.execute_script_command('destroy')
1559 elseif a:0 == 1
1560 return self.execute_script_command('destroy '.s:rquote(a:1))
1561 endif
1562 let str = ""
1563 let c = 1
1564 while c <= a:0
1565 let str .= " " . s:rquote(a:{c})
1566 let c += 1
1567 endwhile
1568 call self.execute_script_command('destroy'.str)
1569 call self.cache.clear('user_classes')
1570 endfunction
1571
1572 function! s:app_generate_command(bang,...) dict
1573 if a:0 == 0
1574 return self.execute_script_command('generate')
1575 elseif a:0 == 1
1576 return self.execute_script_command('generate '.s:rquote(a:1))
1577 endif
1578 let cmd = join(map(copy(a:000),'s:rquote(v:val)'),' ')
1579 if cmd !~ '-p\>' && cmd !~ '--pretend\>'
1580 let execstr = self.script_shell_command('generate '.cmd.' -p -f')
1581 let res = system(execstr)
1582 let g:res = res
1583 let junk = '\%(\e\[[0-9;]*m\)\='
1584 let file = matchstr(res,junk.'\s\+\%(create\|force\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
1585 if file == ""
1586 let file = matchstr(res,junk.'\s\+\%(identical\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
1587 endif
1588 else
1589 let file = ""
1590 endif
1591 if !self.execute_script_command('generate '.cmd) && file != ''
1592 call self.cache.clear('user_classes')
1593 call self.cache.clear('features')
1594 if file =~ '^db/migrate/\d\d\d\d'
1595 let file = get(self.relglob('',s:sub(file,'\d+','[0-9]*[0-9]')),-1,file)
1596 endif
1597 edit `=self.path(file)`
1598 endif
1599 endfunction
1600
1601 call s:add_methods('app', ['generators','script_command','runner_command','server_command','destroy_command','generate_command'])
1602
1603 function! s:Complete_script(ArgLead,CmdLine,P)
1604 let cmd = s:sub(a:CmdLine,'^\u\w*\s+','')
1605 if cmd !~ '^[ A-Za-z0-9_=:-]*$'
1606 return []
1607 elseif cmd =~# '^\w*$'
1608 return s:completion_filter(rails#app().relglob("script/","**/*"),a:ArgLead)
1609 elseif cmd =~# '^\%(plugin\)\s\+'.a:ArgLead.'$'
1610 return s:completion_filter(["discover","list","install","update","remove","source","unsource","sources"],a:ArgLead)
1611 elseif cmd =~# '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$'
1612 return s:pluginList(a:ArgLead,a:CmdLine,a:P)
1613 elseif cmd =~# '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$'
1614 return s:completion_filter(rails#app().generators(),a:ArgLead)
1615 elseif cmd =~# '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$'
1616 let target = matchstr(cmd,'^\w\+\s\+\%(\w\+:\)\=\zs\w\+\ze\s\+')
1617 if target =~# '^\w*controller$'
1618 return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"')
1619 elseif target ==# 'generator'
1620 return s:completion_filter(map(rails#app().relglob('lib/generators/','*'),'s:sub(v:val,"/$","")'))
1621 elseif target ==# 'helper'
1622 return s:helperList(a:ArgLead,"","")
1623 elseif target ==# 'integration_test' || target ==# 'integration_spec' || target ==# 'feature'
1624 return s:integrationtestList(a:ArgLead,"","")
1625 elseif target ==# 'metal'
1626 return s:metalList(a:ArgLead,"","")
1627 elseif target ==# 'migration' || target ==# 'session_migration'
1628 return s:migrationList(a:ArgLead,"","")
1629 elseif target =~# '^\w*\%(model\|resource\)$' || target =~# '\w*scaffold\%(_controller\)\=$' || target ==# 'mailer'
1630 return s:modelList(a:ArgLead,"","")
1631 elseif target ==# 'observer'
1632 let observers = s:observerList("","","")
1633 let models = s:modelList("","","")
1634 if cmd =~# '^destroy\>'
1635 let models = []
1636 endif
1637 call filter(models,'index(observers,v:val) < 0')
1638 return s:completion_filter(observers + models,a:ArgLead)
1639 else
1640 return []
1641 endif
1642 elseif cmd =~# '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
1643 return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"')
1644 return s:completion_filter(rails#app().environments())
1645 elseif cmd =~# '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$"
1646 return s:completion_filter(rails#app().environments()+["-s","--sandbox"],a:ArgLead)
1647 elseif cmd =~# '^\%(server\)\s\+.*-e\s\+'.a:ArgLead."$"
1648 return s:completion_filter(rails#app().environments(),a:ArgLead)
1649 elseif cmd =~# '^\%(server\)\s\+'
1650 if a:ArgLead =~# '^--environment='
1651 return s:completion_filter(map(copy(rails#app().environments()),'"--environment=".v:val'),a:ArgLead)
1652 else
1653 return filter(["-p","-b","-e","-m","-d","-u","-c","-h","--port=","--binding=","--environment=","--mime-types=","--daemon","--debugger","--charset=","--help"],'s:startswith(v:val,a:ArgLead)')
1654 endif
1655 endif
1656 return ""
1657 endfunction
1658
1659 function! s:CustomComplete(A,L,P,cmd)
1660 let L = "Rscript ".a:cmd." ".s:sub(a:L,'^\h\w*\s+','')
1661 let P = a:P - strlen(a:L) + strlen(L)
1662 return s:Complete_script(a:A,L,P)
1663 endfunction
1664
1665 function! s:Complete_server(A,L,P)
1666 return s:CustomComplete(a:A,a:L,a:P,"server")
1667 endfunction
1668
1669 function! s:Complete_console(A,L,P)
1670 return s:CustomComplete(a:A,a:L,a:P,"console")
1671 endfunction
1672
1673 function! s:Complete_generate(A,L,P)
1674 return s:CustomComplete(a:A,a:L,a:P,"generate")
1675 endfunction
1676
1677 function! s:Complete_destroy(A,L,P)
1678 return s:CustomComplete(a:A,a:L,a:P,"destroy")
1679 endfunction
1680
1681 function! s:Complete_ruby(A,L,P)
1682 return s:completion_filter(rails#app().user_classes()+["ActiveRecord::Base"],a:A)
1683 endfunction
1684
1685 " }}}1
1686 " Navigation {{{1
1687
1688 function! s:BufNavCommands()
1689 command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rcd :cd `=rails#app().path(<q-args>)`
1690 command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rlcd :lcd `=rails#app().path(<q-args>)`
1691 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rfind :call s:warn( 'Rfind has been deprecated in favor of :1R or :find' )|call s:Find(<count>,'<bang>' ,<f-args>)
1692 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find REfind :call s:warn('REfind has been deprecated in favor of :1RE or :find')|call s:Find(<count>,'E<bang>',<f-args>)
1693 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RSfind :call s:warn('RSfind has been deprecated in favor of :1RS or :find')|call s:Find(<count>,'S<bang>',<f-args>)
1694 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RVfind :call s:warn('RVfind has been deprecated in favor of :1RV or :find')|call s:Find(<count>,'V<bang>',<f-args>)
1695 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find RTfind :call s:warn('RTfind has been deprecated in favor of :1RT or :find')|call s:Find(<count>,'T<bang>',<f-args>)
1696 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rsfind :call s:warn('Rsfind has been deprecated in favor of :1RS or :sfind')|<count>RSfind<bang> <args>
1697 command! -buffer -bar -nargs=* -count=1 -complete=customlist,s:Complete_find Rtabfind :call s:warn('Rtabfind has been deprecated in favor of :1RT or :tabfind')|<count>RTfind<bang> <args>
1698 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit Redit :call s:warn( 'Redit has been deprecated in favor of :R')|call s:Edit(<count>,'<bang>' ,<f-args>)
1699 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit REedit :call s:warn('REedit has been deprecated in favor of :RE')|call s:Edit(<count>,'E<bang>',<f-args>)
1700 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RSedit :call s:warn('RSedit has been deprecated in favor of :RS')|call s:Edit(<count>,'S<bang>',<f-args>)
1701 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RVedit :call s:warn('RVedit has been deprecated in favor of :RV')|call s:Edit(<count>,'V<bang>',<f-args>)
1702 command! -buffer -bar -nargs=* -bang -complete=customlist,s:Complete_edit RTedit :call s:warn('RTedit has been deprecated in favor of :RT')|call s:Edit(<count>,'T<bang>',<f-args>)
1703 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit RDedit :call s:warn('RDedit has been deprecated in favor of :RD')|call s:Edit(<count>,'<line1>D<bang>',<f-args>)
1704 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related A :call s:Alternate('<bang>', <line1>,<line2>,<count>,<f-args>)
1705 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AE :call s:Alternate('E<bang>',<line1>,<line2>,<count>,<f-args>)
1706 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AS :call s:Alternate('S<bang>',<line1>,<line2>,<count>,<f-args>)
1707 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AV :call s:Alternate('V<bang>',<line1>,<line2>,<count>,<f-args>)
1708 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AT :call s:Alternate('T<bang>',<line1>,<line2>,<count>,<f-args>)
1709 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AD :call s:Alternate('D<bang>',<line1>,<line2>,<count>,<f-args>)
1710 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AN :call s:Related('<bang>' ,<line1>,<line2>,<count>,<f-args>)
1711 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related R :call s:Related('<bang>' ,<line1>,<line2>,<count>,<f-args>)
1712 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RE :call s:Related('E<bang>',<line1>,<line2>,<count>,<f-args>)
1713 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RS :call s:Related('S<bang>',<line1>,<line2>,<count>,<f-args>)
1714 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RV :call s:Related('V<bang>',<line1>,<line2>,<count>,<f-args>)
1715 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RT :call s:Related('T<bang>',<line1>,<line2>,<count>,<f-args>)
1716 command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RD :call s:Related('D<bang>',<line1>,<line2>,<count>,<f-args>)
1717 endfunction
1718
1719 function! s:djump(def)
1720 let def = s:sub(a:def,'^[#:]','')
1721 if def =~ '^\d\+$'
1722 exe def
1723 elseif def =~ '^!'
1724 if expand('%') !~ '://' && !isdirectory(expand('%:p:h'))
1725 call mkdir(expand('%:p:h'),'p')
1726 endif
1727 elseif def != ''
1728 let ext = matchstr(def,'\.\zs.*')
1729 let def = matchstr(def,'[^.]*')
1730 let v:errmsg = ''
1731 silent! exe "djump ".def
1732 if ext != '' && (v:errmsg == '' || v:errmsg =~ '^E387')
1733 let rpat = '\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|'
1734 let end = s:endof(line('.'))
1735 let rline = search(rpat,'',end)
1736 if rline > 0
1737 let variable = matchstr(getline(rline),rpat)
1738 let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end)
1739 if !success
1740 silent! exe "djump ".def
1741 endif
1742 endif
1743 endif
1744 endif
1745 endfunction
1746
1747 function! s:Find(count,cmd,...)
1748 let str = ""
1749 if a:0
1750 let i = 1
1751 while i < a:0
1752 let str .= s:escarg(a:{i}) . " "
1753 let i += 1
1754 endwhile
1755 let file = a:{i}
1756 let tail = matchstr(file,'[#!].*$\|:\d*\%(:in\>.*\)\=$')
1757 if tail != ""
1758 let file = s:sub(file,'[#!].*$|:\d*%(:in>.*)=$','')
1759 endif
1760 if file != ""
1761 let file = s:RailsIncludefind(file)
1762 endif
1763 else
1764 let file = s:RailsFind()
1765 let tail = ""
1766 endif
1767 call s:findedit((a:count==1?'' : a:count).a:cmd,file.tail,str)
1768 endfunction
1769
1770 function! s:Edit(count,cmd,...)
1771 if a:0
1772 let str = ""
1773 let i = 1
1774 while i < a:0
1775 let str .= "`=a:".i."` "
1776 let i += 1
1777 endwhile
1778 let file = a:{i}
1779 call s:findedit(s:editcmdfor(a:cmd),file,str)
1780 else
1781 exe s:editcmdfor(a:cmd)
1782 endif
1783 endfunction
1784
1785 function! s:fuzzyglob(arg)
1786 return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*')
1787 endfunction
1788
1789 function! s:Complete_find(ArgLead, CmdLine, CursorPos)
1790 let paths = s:pathsplit(&l:path)
1791 let seen = {}
1792 for path in paths
1793 if s:startswith(path,rails#app().path()) && path !~ '[][*]'
1794 let path = path[strlen(rails#app().path()) + 1 : ]
1795 for file in rails#app().relglob(path == '' ? '' : path.'/',s:fuzzyglob(rails#underscore(a:ArgLead)), a:ArgLead =~# '\u' ? '.rb' : '')
1796 let seen[file] = 1
1797 endfor
1798 endif
1799 endfor
1800 return s:autocamelize(sort(keys(seen)),a:ArgLead)
1801 endfunction
1802
1803 function! s:Complete_edit(ArgLead, CmdLine, CursorPos)
1804 return s:completion_filter(rails#app().relglob("",s:fuzzyglob(a:ArgLead)),a:ArgLead)
1805 endfunction
1806
1807 function! s:Complete_cd(ArgLead, CmdLine, CursorPos)
1808 let all = rails#app().relglob("",a:ArgLead."*")
1809 call filter(all,'v:val =~ "/$"')
1810 return filter(all,'s:startswith(v:val,a:ArgLead)')
1811 endfunction
1812
1813 function! RailsIncludeexpr()
1814 " Is this foolproof?
1815 if mode() =~ '[iR]' || expand("<cfile>") != v:fname
1816 return s:RailsIncludefind(v:fname)
1817 else
1818 return s:RailsIncludefind(v:fname,1)
1819 endif
1820 endfunction
1821
1822 function! s:linepeak()
1823 let line = getline(line("."))
1824 let line = s:sub(line,'^(.{'.col(".").'}).*','\1')
1825 let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
1826 return line
1827 endfunction
1828
1829 function! s:matchcursor(pat)
1830 let line = getline(".")
1831 let lastend = 0
1832 while lastend >= 0
1833 let beg = match(line,'\C'.a:pat,lastend)
1834 let end = matchend(line,'\C'.a:pat,lastend)
1835 if beg < col(".") && end >= col(".")
1836 return matchstr(line,'\C'.a:pat,lastend)
1837 endif
1838 let lastend = end
1839 endwhile
1840 return ""
1841 endfunction
1842
1843 function! s:findit(pat,repl)
1844 let res = s:matchcursor(a:pat)
1845 if res != ""
1846 return substitute(res,'\C'.a:pat,a:repl,'')
1847 else
1848 return ""
1849 endif
1850 endfunction
1851
1852 function! s:findamethod(func,repl)
1853 return s:findit('\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
1854 endfunction
1855
1856 function! s:findasymbol(sym,repl)
1857 return s:findit('\s*\%(:\%('.a:sym.'\)\s*=>\|\<'.a:sym.':\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
1858 endfunction
1859
1860 function! s:findfromview(func,repl)
1861 " ( ) ( ) ( \1 ) ( )
1862 return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl)
1863 endfunction
1864
1865 function! s:RailsFind()
1866 if filereadable(expand("<cfile>"))
1867 return expand("<cfile>")
1868 endif
1869
1870 " UGH
1871 let buffer = rails#buffer()
1872 let format = s:format('html')
1873
1874 let res = s:findit('\v\s*<require\s*\(=\s*File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>.=',expand('%:h').'/\1')
1875 if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
1876
1877 let res = s:findit('\v<File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>['."'".'"]=',expand('%:h').'\1')
1878 if res != ""|return res|endif
1879
1880 let res = rails#underscore(s:findit('\v\s*<%(include|extend)\(=\s*<([[:alnum:]_:]+)>','\1'))
1881 if res != ""|return res.".rb"|endif
1882
1883 let res = s:findamethod('require','\1')
1884 if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
1885
1886 let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb')
1887 if res != ""|return res|endif
1888
1889 let res = rails#singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
1890 if res != ""|return res.".rb"|endif
1891
1892 let res = rails#singularize(s:findamethod('create_table\|change_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1'))
1893 if res != ""|return res.".rb"|endif
1894
1895 let res = rails#singularize(s:findasymbol('through','app/models/\1'))
1896 if res != ""|return res.".rb"|endif
1897
1898 let res = s:findamethod('fixtures','fixtures/\1')
1899 if res != ""
1900 return RailsFilePath() =~ '\<spec/' ? 'spec/'.res : res
1901 endif
1902
1903 let res = s:findamethod('\%(\w\+\.\)\=resources','app/controllers/\1_controller.rb')
1904 if res != ""|return res|endif
1905
1906 let res = s:findamethod('\%(\w\+\.\)\=resource','app/controllers/\1')
1907 if res != ""|return rails#pluralize(res)."_controller.rb"|endif
1908
1909 let res = s:findasymbol('to','app/controllers/\1')
1910 if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
1911
1912 let res = s:findamethod('root\s*\%(:to\s*=>\|\<to:\)\s*','app/controllers/\1')
1913 if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
1914
1915 let res = s:findamethod('\%(match\|get\|put\|post\|delete\|redirect\)\s*(\=\s*[:''"][^''"]*[''"]\=\s*\%(\%(,\s*:to\s*\)\==>\|,\s*to:\)\s*','app/controllers/\1')
1916 if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
1917
1918 let res = s:findamethod('layout','\=s:findlayout(submatch(1))')
1919 if res != ""|return res|endif
1920
1921 let res = s:findasymbol('layout','\=s:findlayout(submatch(1))')
1922 if res != ""|return res|endif
1923
1924 let res = s:findamethod('helper','app/helpers/\1_helper.rb')
1925 if res != ""|return res|endif
1926
1927 let res = s:findasymbol('controller','app/controllers/\1_controller.rb')
1928 if res != ""|return res|endif
1929
1930 let res = s:findasymbol('action','\1')
1931 if res != ""|return res|endif
1932
1933 let res = s:findasymbol('template','app/views/\1')
1934 if res != ""|return res|endif
1935
1936 let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'[^/]+$','_&')
1937 if res != ""|return res."\n".s:findview(res)|endif
1938
1939 let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:partial\s\+=>\|partial:\)\s*','\1'),'^/',''),'[^/]+$','_&')
1940 if res != ""|return res."\n".s:findview(res)|endif
1941
1942 let res = s:findamethod('render\>\s*\%(:\%(template\|action\)\s\+=>\|template:\|action:\)\s*','\1.'.format.'\n\1')
1943 if res != ""|return res|endif
1944
1945 let res = s:sub(s:findfromview('render','\1'),'^/','')
1946 if buffer.type_name('view') | let res = s:sub(res,'[^/]+$','_&') | endif
1947 if res != ""|return res."\n".s:findview(res)|endif
1948
1949 let res = s:findamethod('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\<action:\)\s*','\1')
1950 if res != ""|return res|endif
1951
1952 let res = s:findfromview('stylesheet_link_tag','public/stylesheets/\1')
1953 if res != '' && fnamemodify(res, ':e') == '' " Append the default extension iff the filename doesn't already contains an extension
1954 let res .= '.css'
1955 end
1956 if res != ""|return res|endif
1957
1958 let res = s:sub(s:findfromview('javascript_include_tag','public/javascripts/\1'),'/defaults>','/application')
1959 if res != '' && fnamemodify(res, ':e') == '' " Append the default extension iff the filename doesn't already contains an extension
1960 let res .= '.js'
1961 end
1962 if res != ""|return res|endif
1963
1964 if buffer.type_name('controller')
1965 let contr = s:controller()
1966 let view = s:findit('\s*\<def\s\+\(\k\+\)\>(\=','/\1')
1967 let res = s:findview(contr.'/'.view)
1968 if res != ""|return res|endif
1969 endif
1970
1971 let old_isfname = &isfname
1972 try
1973 set isfname=@,48-57,/,-,_,:,#
1974 " TODO: grab visual selection in visual mode
1975 let cfile = expand("<cfile>")
1976 finally
1977 let &isfname = old_isfname
1978 endtry
1979 let res = s:RailsIncludefind(cfile,1)
1980 return res
1981 endfunction
1982
1983 function! s:app_named_route_file(route) dict
1984 call self.route_names()
1985 if self.cache.has("named_routes") && has_key(self.cache.get("named_routes"),a:route)
1986 return self.cache.get("named_routes")[a:route]
1987 endif
1988 return ""
1989 endfunction
1990
1991 function! s:app_route_names() dict
1992 if self.cache.needs("named_routes")
1993 let exec = "ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} app/controllers/#{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}"
1994 let string = self.eval(exec)
1995 let routes = {}
1996 for line in split(string,"\n")
1997 let route = split(line," ")
1998 let name = route[0]
1999 let routes[name] = route[1]
2000 endfor
2001 call self.cache.set("named_routes",routes)
2002 endif
2003
2004 return keys(self.cache.get("named_routes"))
2005 endfunction
2006
2007 call s:add_methods('app', ['route_names','named_route_file'])
2008
2009 function! RailsNamedRoutes()
2010 return rails#app().route_names()
2011 endfunction
2012
2013 function! s:RailsIncludefind(str,...)
2014 if a:str ==# "ApplicationController"
2015 return "application_controller.rb\napp/controllers/application.rb"
2016 elseif a:str ==# "Test::Unit::TestCase"
2017 return "test/unit/testcase.rb"
2018 endif
2019 let str = a:str
2020 if a:0 == 1
2021 " Get the text before the filename under the cursor.
2022 " We'll cheat and peak at this in a bit
2023 let line = s:linepeak()
2024 let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','')
2025 else
2026 let line = ""
2027 endif
2028 let str = s:sub(str,'^\s*','')
2029 let str = s:sub(str,'\s*$','')
2030 let str = s:sub(str,'^:=[:@]','')
2031 let str = s:sub(str,':0x\x+$','') " For #<Object:0x...> style output
2032 let str = s:gsub(str,"[\"']",'')
2033 if line =~# '\<\(require\|load\)\s*(\s*$'
2034 return str
2035 elseif str =~# '^\l\w*#\w\+$'
2036 return 'app/controllers/'.s:sub(str,'#','_controller.rb#')
2037 endif
2038 let str = rails#underscore(str)
2039 let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*'
2040 if a:str =~# '\u'
2041 " Classes should always be in .rb files
2042 let str .= '.rb'
2043 elseif line =~# ':partial\s*=>\s*'
2044 let str = s:sub(str,'[^/]+$','_&')
2045 let str = s:findview(str)
2046 elseif line =~# '\<layout\s*(\=\s*' || line =~# ':layout\s*=>\s*'
2047 let str = s:findview(s:sub(str,'^/=','layouts/'))
2048 elseif line =~# ':controller\s*=>\s*'
2049 let str = 'app/controllers/'.str.'_controller.rb'
2050 elseif line =~# '\<helper\s*(\=\s*'
2051 let str = 'app/helpers/'.str.'_helper.rb'
2052 elseif line =~# '\<fixtures\s*(\='.fpat
2053 if RailsFilePath() =~# '\<spec/'
2054 let str = s:sub(str,'^/@!','spec/fixtures/')
2055 else
2056 let str = s:sub(str,'^/@!','test/fixtures/')
2057 endif
2058 elseif line =~# '\<stylesheet_\(link_tag\|path\)\s*(\='.fpat
2059 let str = s:sub(str,'^/@!','/stylesheets/')
2060 if str != '' && fnamemodify(str, ':e') == ''
2061 let str .= '.css'
2062 endif
2063 elseif line =~# '\<javascript_\(include_tag\|path\)\s*(\='.fpat
2064 if str ==# "defaults"
2065 let str = "application"
2066 endif
2067 let str = s:sub(str,'^/@!','/javascripts/')
2068 if str != '' && fnamemodify(str, ':e') == ''
2069 let str .= '.js'
2070 endif
2071 elseif line =~# '\<\(has_one\|belongs_to\)\s*(\=\s*'
2072 let str = 'app/models/'.str.'.rb'
2073 elseif line =~# '\<has_\(and_belongs_to_\)\=many\s*(\=\s*'
2074 let str = 'app/models/'.rails#singularize(str).'.rb'
2075 elseif line =~# '\<def\s\+' && expand("%:t") =~# '_controller\.rb'
2076 let str = s:findview(str)
2077 elseif str =~# '_\%(path\|url\)$' || (line =~# ':as\s*=>\s*$' && rails#buffer().type_name('config-routes'))
2078 if line !~# ':as\s*=>\s*$'
2079 let str = s:sub(str,'_%(path|url)$','')
2080 let str = s:sub(str,'^hash_for_','')
2081 endif
2082 let file = rails#app().named_route_file(str)
2083 if file == ""
2084 let str = s:sub(str,'^formatted_','')
2085 if str =~# '^\%(new\|edit\)_'
2086 let str = 'app/controllers/'.s:sub(rails#pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1')
2087 elseif str ==# rails#singularize(str)
2088 " If the word can't be singularized, it's probably a link to the show
2089 " method. We should verify by checking for an argument, but that's
2090 " difficult the way things here are currently structured.
2091 let str = 'app/controllers/'.rails#pluralize(str).'_controller.rb#show'
2092 else
2093 let str = 'app/controllers/'.str.'_controller.rb#index'
2094 endif
2095 else
2096 let str = file
2097 endif
2098 elseif str !~ '/'
2099 " If we made it this far, we'll risk making it singular.
2100 let str = rails#singularize(str)
2101 let str = s:sub(str,'_id$','')
2102 endif
2103 if str =~ '^/' && !filereadable(str)
2104 let str = s:sub(str,'^/','')
2105 endif
2106 if str =~# '^lib/' && !filereadable(str)
2107 let str = s:sub(str,'^lib/','')
2108 endif
2109 return str
2110 endfunction
2111
2112 " }}}1
2113 " File Finders {{{1
2114
2115 function! s:addfilecmds(type)
2116 let l = s:sub(a:type,'^.','\l&')
2117 let cmds = 'ESVTD '
2118 let cmd = ''
2119 while cmds != ''
2120 let cplt = " -complete=customlist,".s:sid.l."List"
2121 exe "command! -buffer -bar ".(cmd == 'D' ? '-range=0 ' : '')."-nargs=*".cplt." R".cmd.l." :call s:".l.'Edit("'.(cmd == 'D' ? '<line1>' : '').cmd.'<bang>",<f-args>)'
2122 let cmd = strpart(cmds,0,1)
2123 let cmds = strpart(cmds,1)
2124 endwhile
2125 endfunction
2126
2127 function! s:BufFinderCommands()
2128 command! -buffer -bar -nargs=+ Rnavcommand :call s:Navcommand(<bang>0,<f-args>)
2129 call s:addfilecmds("metal")
2130 call s:addfilecmds("model")
2131 call s:addfilecmds("view")
2132 call s:addfilecmds("controller")
2133 call s:addfilecmds("mailer")
2134 call s:addfilecmds("migration")
2135 call s:addfilecmds("observer")
2136 call s:addfilecmds("helper")
2137 call s:addfilecmds("layout")
2138 call s:addfilecmds("fixtures")
2139 call s:addfilecmds("locale")
2140 if rails#app().has('test') || rails#app().has('spec')
2141 call s:addfilecmds("unittest")
2142 call s:addfilecmds("functionaltest")
2143 endif
2144 if rails#app().has('test') || rails#app().has('spec') || rails#app().has('cucumber')
2145 call s:addfilecmds("integrationtest")
2146 endif
2147 if rails#app().has('spec')
2148 call s:addfilecmds("spec")
2149 endif
2150 call s:addfilecmds("stylesheet")
2151 call s:addfilecmds("javascript")
2152 call s:addfilecmds("plugin")
2153 call s:addfilecmds("task")
2154 call s:addfilecmds("lib")
2155 call s:addfilecmds("environment")
2156 call s:addfilecmds("initializer")
2157 endfunction
2158
2159 function! s:completion_filter(results,A)
2160 let results = sort(type(a:results) == type("") ? split(a:results,"\n") : copy(a:results))
2161 call filter(results,'v:val !~# "\\~$"')
2162 let filtered = filter(copy(results),'s:startswith(v:val,a:A)')
2163 if !empty(filtered) | return filtered | endif
2164 let regex = s:gsub(a:A,'[^/]','[&].*')
2165 let filtered = filter(copy(results),'v:val =~# "^".regex')
2166 if !empty(filtered) | return filtered | endif
2167 let regex = s:gsub(a:A,'.','[&].*')
2168 let filtered = filter(copy(results),'v:val =~# regex')
2169 return filtered
2170 endfunction
2171
2172 function! s:autocamelize(files,test)
2173 if a:test =~# '^\u'
2174 return s:completion_filter(map(copy(a:files),'rails#camelize(v:val)'),a:test)
2175 else
2176 return s:completion_filter(a:files,a:test)
2177 endif
2178 endfunction
2179
2180 function! s:app_relglob(path,glob,...) dict
2181 if exists("+shellslash") && ! &shellslash
2182 let old_ss = &shellslash
2183 let &shellslash = 1
2184 endif
2185 let path = a:path
2186 if path !~ '^/' && path !~ '^\w:'
2187 let path = self.path(path)
2188 endif
2189 let suffix = a:0 ? a:1 : ''
2190 let full_paths = split(glob(path.a:glob.suffix),"\n")
2191 let relative_paths = []
2192 for entry in full_paths
2193 if suffix == '' && isdirectory(entry) && entry !~ '/$'
2194 let entry .= '/'
2195 endif
2196 let relative_paths += [entry[strlen(path) : -strlen(suffix)-1]]
2197 endfor
2198 if exists("old_ss")
2199 let &shellslash = old_ss
2200 endif
2201 return relative_paths
2202 endfunction
2203
2204 call s:add_methods('app', ['relglob'])
2205
2206 function! s:relglob(...)
2207 return join(call(rails#app().relglob,a:000,rails#app()),"\n")
2208 endfunction
2209
2210 function! s:helperList(A,L,P)
2211 return s:autocamelize(rails#app().relglob("app/helpers/","**/*","_helper.rb"),a:A)
2212 endfunction
2213
2214 function! s:controllerList(A,L,P)
2215 let con = rails#app().relglob("app/controllers/","**/*",".rb")
2216 call map(con,'s:sub(v:val,"_controller$","")')
2217 return s:autocamelize(con,a:A)
2218 endfunction
2219
2220 function! s:mailerList(A,L,P)
2221 return s:autocamelize(rails#app().relglob("app/mailers/","**/*",".rb"),a:A)
2222 endfunction
2223
2224 function! s:viewList(A,L,P)
2225 let c = s:controller(1)
2226 let top = rails#app().relglob("app/views/",s:fuzzyglob(a:A))
2227 call filter(top,'v:val !~# "\\~$"')
2228 if c != '' && a:A !~ '/'
2229 let local = rails#app().relglob("app/views/".c."/","*.*[^~]")
2230 return s:completion_filter(local+top,a:A)
2231 endif
2232 return s:completion_filter(top,a:A)
2233 endfunction
2234
2235 function! s:layoutList(A,L,P)
2236 return s:completion_filter(rails#app().relglob("app/views/layouts/","*"),a:A)
2237 endfunction
2238
2239 function! s:stylesheetList(A,L,P)
2240 let list = rails#app().relglob('app/assets/stylesheets/','**/*.*','')
2241 call map(list,'s:sub(v:val,"\\..*$","")')
2242 let list += rails#app().relglob('public/stylesheets/','**/*','.css')
2243 if rails#app().has('sass')
2244 call extend(list,rails#app().relglob('public/stylesheets/sass/','**/*','.s?ss'))
2245 call s:uniq(list)
2246 endif
2247 return s:completion_filter(list,a:A)
2248 endfunction
2249
2250 function! s:javascriptList(A,L,P)
2251 let list = rails#app().relglob('app/assets/javascripts/','**/*.*','')
2252 call map(list,'s:sub(v:val,"\\..*$","")')
2253 let list += rails#app().relglob("public/javascripts/","**/*",".js")
2254 return s:completion_filter(list,a:A)
2255 endfunction
2256
2257 function! s:metalList(A,L,P)
2258 return s:autocamelize(rails#app().relglob("app/metal/","**/*",".rb"),a:A)
2259 endfunction
2260
2261 function! s:modelList(A,L,P)
2262 let models = rails#app().relglob("app/models/","**/*",".rb")
2263 call filter(models,'v:val !~# "_observer$"')
2264 return s:autocamelize(models,a:A)
2265 endfunction
2266
2267 function! s:observerList(A,L,P)
2268 return s:autocamelize(rails#app().relglob("app/models/","**/*","_observer.rb"),a:A)
2269 endfunction
2270
2271 function! s:fixturesList(A,L,P)
2272 return s:completion_filter(rails#app().relglob("test/fixtures/","**/*")+rails#app().relglob("spec/fixtures/","**/*"),a:A)
2273 endfunction
2274
2275 function! s:localeList(A,L,P)
2276 return s:completion_filter(rails#app().relglob("config/locales/","**/*"),a:A)
2277 endfunction
2278
2279 function! s:migrationList(A,L,P)
2280 if a:A =~ '^\d'
2281 let migrations = rails#app().relglob("db/migrate/",a:A."[0-9_]*",".rb")
2282 return map(migrations,'matchstr(v:val,"^[0-9]*")')
2283 else
2284 let migrations = rails#app().relglob("db/migrate/","[0-9]*[0-9]_*",".rb")
2285 call map(migrations,'s:sub(v:val,"^[0-9]*_","")')
2286 return s:autocamelize(migrations,a:A)
2287 endif
2288 endfunction
2289
2290 function! s:unittestList(A,L,P)
2291 let found = []
2292 if rails#app().has('test')
2293 let found += rails#app().relglob("test/unit/","**/*","_test.rb")
2294 endif
2295 if rails#app().has('spec')
2296 let found += rails#app().relglob("spec/models/","**/*","_spec.rb")
2297 endif
2298 return s:autocamelize(found,a:A)
2299 endfunction
2300
2301 function! s:functionaltestList(A,L,P)
2302 let found = []
2303 if rails#app().has('test')
2304 let found += rails#app().relglob("test/functional/","**/*","_test.rb")
2305 endif
2306 if rails#app().has('spec')
2307 let found += rails#app().relglob("spec/controllers/","**/*","_spec.rb")
2308 let found += rails#app().relglob("spec/mailers/","**/*","_spec.rb")
2309 endif
2310 return s:autocamelize(found,a:A)
2311 endfunction
2312
2313 function! s:integrationtestList(A,L,P)
2314 if a:A =~# '^\u'
2315 return s:autocamelize(rails#app().relglob("test/integration/","**/*","_test.rb"),a:A)
2316 endif
2317 let found = []
2318 if rails#app().has('test')
2319 let found += rails#app().relglob("test/integration/","**/*","_test.rb")
2320 endif
2321 if rails#app().has('spec')
2322 let found += rails#app().relglob("spec/requests/","**/*","_spec.rb")
2323 let found += rails#app().relglob("spec/integration/","**/*","_spec.rb")
2324 endif
2325 if rails#app().has('cucumber')
2326 let found += rails#app().relglob("features/","**/*",".feature")
2327 endif
2328 return s:completion_filter(found,a:A)
2329 endfunction
2330
2331 function! s:specList(A,L,P)
2332 return s:completion_filter(rails#app().relglob("spec/","**/*","_spec.rb"),a:A)
2333 endfunction
2334
2335 function! s:pluginList(A,L,P)
2336 if a:A =~ '/'
2337 return s:completion_filter(rails#app().relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*'),a:A)
2338 else
2339 return s:completion_filter(rails#app().relglob('vendor/plugins/',"*","/init.rb"),a:A)
2340 endif
2341 endfunction
2342
2343 " Task files, not actual rake tasks
2344 function! s:taskList(A,L,P)
2345 let all = rails#app().relglob("lib/tasks/","**/*",".rake")
2346 if RailsFilePath() =~ '\<vendor/plugins/.'
2347 let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','')
2348 let all = rails#app().relglob(path."tasks/","**/*",".rake")+rails#app().relglob(path."lib/tasks/","**/*",".rake")+all
2349 endif
2350 return s:autocamelize(all,a:A)
2351 endfunction
2352
2353 function! s:libList(A,L,P)
2354 let all = rails#app().relglob('lib/',"**/*",".rb")
2355 if RailsFilePath() =~ '\<vendor/plugins/.'
2356 let path = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')
2357 let all = rails#app().relglob(path,"**/*",".rb") + all
2358 endif
2359 return s:autocamelize(all,a:A)
2360 endfunction
2361
2362 function! s:environmentList(A,L,P)
2363 return s:completion_filter(rails#app().relglob("config/environments/","**/*",".rb"),a:A)
2364 endfunction
2365
2366 function! s:initializerList(A,L,P)
2367 return s:completion_filter(rails#app().relglob("config/initializers/","**/*",".rb"),a:A)
2368 endfunction
2369
2370 function! s:Navcommand(bang,...)
2371 let suffix = ".rb"
2372 let filter = "**/*"
2373 let prefix = ""
2374 let default = ""
2375 let name = ""
2376 let i = 0
2377 while i < a:0
2378 let i += 1
2379 let arg = a:{i}
2380 if arg =~# '^-suffix='
2381 let suffix = matchstr(arg,'-suffix=\zs.*')
2382 elseif arg =~# '^-default='
2383 let default = matchstr(arg,'-default=\zs.*')
2384 elseif arg =~# '^-\%(glob\|filter\)='
2385 let filter = matchstr(arg,'-\w*=\zs.*')
2386 elseif arg !~# '^-'
2387 " A literal '\n'. For evaluation below
2388 if name == ""
2389 let name = arg
2390 else
2391 let prefix .= "\\n".s:sub(arg,'/=$','/')
2392 endif
2393 endif
2394 endwhile
2395 let prefix = s:sub(prefix,'^\\n','')
2396 if name !~ '^[A-Za-z]\+$'
2397 return s:error("E182: Invalid command name")
2398 endif
2399 let cmds = 'ESVTD '
2400 let cmd = ''
2401 while cmds != ''
2402 exe 'command! -buffer -bar -bang -nargs=* -complete=customlist,'.s:sid.'CommandList R'.cmd.name." :call s:CommandEdit('".cmd."<bang>','".name."',\"".prefix."\",".string(suffix).",".string(filter).",".string(default).",<f-args>)"
2403 let cmd = strpart(cmds,0,1)
2404 let cmds = strpart(cmds,1)
2405 endwhile
2406 endfunction
2407
2408 function! s:CommandList(A,L,P)
2409 let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+')
2410 exe cmd." &"
2411 let lp = s:last_prefix . "\n"
2412 let res = []
2413 while lp != ""
2414 let p = matchstr(lp,'.\{-\}\ze\n')
2415 let lp = s:sub(lp,'.{-}\n','')
2416 let res += rails#app().relglob(p,s:last_filter,s:last_suffix)
2417 endwhile
2418 if s:last_camelize
2419 return s:autocamelize(res,a:A)
2420 else
2421 return s:completion_filter(res,a:A)
2422 endif
2423 endfunction
2424
2425 function! s:CommandEdit(cmd,name,prefix,suffix,filter,default,...)
2426 if a:0 && a:1 == "&"
2427 let s:last_prefix = a:prefix
2428 let s:last_suffix = a:suffix
2429 let s:last_filter = a:filter
2430 let s:last_camelize = (a:suffix =~# '\.rb$')
2431 else
2432 if a:default == "both()"
2433 if s:model() != ""
2434 let default = s:model()
2435 else
2436 let default = s:controller()
2437 endif
2438 elseif a:default == "model()"
2439 let default = s:model(1)
2440 elseif a:default == "controller()"
2441 let default = s:controller(1)
2442 else
2443 let default = a:default
2444 endif
2445 call s:EditSimpleRb(a:cmd,a:name,a:0 ? a:1 : default,a:prefix,a:suffix)
2446 endif
2447 endfunction
2448
2449 function! s:EditSimpleRb(cmd,name,target,prefix,suffix,...)
2450 let cmd = s:findcmdfor(a:cmd)
2451 if a:target == ""
2452 " Good idea to emulate error numbers like this?
2453 return s:error("E471: Argument required")
2454 endif
2455 let f = a:0 ? a:target : rails#underscore(a:target)
2456 let jump = matchstr(f,'[#!].*\|:\d*\%(:in\)\=$')
2457 let f = s:sub(f,'[#!].*|:\d*%(:in)=$','')
2458 if jump =~ '^!'
2459 let cmd = s:editcmdfor(cmd)
2460 endif
2461 if f == '.'
2462 let f = s:sub(f,'\.$','')
2463 else
2464 let f .= a:suffix.jump
2465 endif
2466 let f = s:gsub(a:prefix,'\n',f.'\n').f
2467 return s:findedit(cmd,f)
2468 endfunction
2469
2470 function! s:app_migration(file) dict
2471 let arg = a:file
2472 if arg =~ '^0$\|^0\=[#:]'
2473 let suffix = s:sub(arg,'^0*','')
2474 if self.has_file('db/schema.rb')
2475 return 'db/schema.rb'.suffix
2476 elseif self.has_file('db/'.s:environment().'_structure.sql')
2477 return 'db/'.s:environment().'_structure.sql'.suffix
2478 else
2479 return 'db/schema.rb'.suffix
2480 endif
2481 elseif arg =~ '^\d$'
2482 let glob = '00'.arg.'_*.rb'
2483 elseif arg =~ '^\d\d$'
2484 let glob = '0'.arg.'_*.rb'
2485 elseif arg =~ '^\d\d\d$'
2486 let glob = ''.arg.'_*.rb'
2487 elseif arg == ''
2488 let glob = '*.rb'
2489 else
2490 let glob = '*'.rails#underscore(arg).'*rb'
2491 endif
2492 let files = split(glob(self.path('db/migrate/').glob),"\n")
2493 if arg == ''
2494 return get(files,-1,'')
2495 endif
2496 call map(files,'strpart(v:val,1+strlen(self.path()))')
2497 let keep = get(files,0,'')
2498 if glob =~# '^\*.*\*rb'
2499 let pattern = glob[1:-4]
2500 call filter(files,'v:val =~# ''db/migrate/\d\+_''.pattern.''\.rb''')
2501 let keep = get(files,0,keep)
2502 endif
2503 return keep
2504 endfunction
2505
2506 call s:add_methods('app', ['migration'])
2507
2508 function! s:migrationEdit(cmd,...)
2509 let cmd = s:findcmdfor(a:cmd)
2510 let arg = a:0 ? a:1 : ''
2511 let migr = arg == "." ? "db/migrate" : rails#app().migration(arg)
2512 if migr != ''
2513 call s:findedit(cmd,migr)
2514 else
2515 return s:error("Migration not found".(arg=='' ? '' : ': '.arg))
2516 endif
2517 endfunction
2518
2519 function! s:fixturesEdit(cmd,...)
2520 if a:0
2521 let c = rails#underscore(a:1)
2522 else
2523 let c = rails#pluralize(s:model(1))
2524 endif
2525 if c == ""
2526 return s:error("E471: Argument required")
2527 endif
2528 let e = fnamemodify(c,':e')
2529 let e = e == '' ? e : '.'.e
2530 let c = fnamemodify(c,':r')
2531 let file = get(rails#app().test_suites(),0,'test').'/fixtures/'.c.e
2532 if file =~ '\.\w\+$' && rails#app().find_file(c.e,["test/fixtures","spec/fixtures"]) ==# ''
2533 call s:edit(a:cmd,file)
2534 else
2535 call s:findedit(a:cmd,rails#app().find_file(c.e,["test/fixtures","spec/fixtures"],[".yml",".csv"],file))
2536 endif
2537 endfunction
2538
2539 function! s:localeEdit(cmd,...)
2540 let c = a:0 ? a:1 : rails#app().default_locale()
2541 if c =~# '\.'
2542 call s:edit(a:cmd,rails#app().find_file(c,'config/locales',[],'config/locales/'.c))
2543 else
2544 call s:findedit(a:cmd,rails#app().find_file(c,'config/locales',['.yml','.rb'],'config/locales/'.c))
2545 endif
2546 endfunction
2547
2548 function! s:metalEdit(cmd,...)
2549 if a:0
2550 call s:EditSimpleRb(a:cmd,"metal",a:1,"app/metal/",".rb")
2551 else
2552 call s:EditSimpleRb(a:cmd,"metal",'config/boot',"",".rb")
2553 endif
2554 endfunction
2555
2556 function! s:modelEdit(cmd,...)
2557 call s:EditSimpleRb(a:cmd,"model",a:0? a:1 : s:model(1),"app/models/",".rb")
2558 endfunction
2559
2560 function! s:observerEdit(cmd,...)
2561 call s:EditSimpleRb(a:cmd,"observer",a:0? a:1 : s:model(1),"app/models/","_observer.rb")
2562 endfunction
2563
2564 function! s:viewEdit(cmd,...)
2565 if a:0 && a:1 =~ '^[^!#:]'
2566 let view = matchstr(a:1,'[^!#:]*')
2567 elseif rails#buffer().type_name('controller','mailer')
2568 let view = s:lastmethod(line('.'))
2569 else
2570 let view = ''
2571 endif
2572 if view == ''
2573 return s:error("No view name given")
2574 elseif view == '.'
2575 return s:edit(a:cmd,'app/views')
2576 elseif view !~ '/' && s:controller(1) != ''
2577 let view = s:controller(1) . '/' . view
2578 endif
2579 if view !~ '/'
2580 return s:error("Cannot find view without controller")
2581 endif
2582 let file = "app/views/".view
2583 let found = s:findview(view)
2584 if found != ''
2585 let dir = fnamemodify(rails#app().path(found),':h')
2586 if !isdirectory(dir)
2587 if a:0 && a:1 =~ '!'
2588 call mkdir(dir,'p')
2589 else
2590 return s:error('No such directory')
2591 endif
2592 endif
2593 call s:edit(a:cmd,found)
2594 elseif file =~ '\.\w\+$'
2595 call s:findedit(a:cmd,file)
2596 else
2597 let format = s:format(rails#buffer().type_name('mailer') ? 'text' : 'html')
2598 if glob(rails#app().path(file.'.'.format).'.*[^~]') != ''
2599 let file .= '.' . format
2600 endif
2601 call s:findedit(a:cmd,file)
2602 endif
2603 endfunction
2604
2605 function! s:findview(name)
2606 let self = rails#buffer()
2607 let name = a:name
2608 let pre = 'app/views/'
2609 if name !~# '/'
2610 let controller = self.controller_name(1)
2611 if controller != ''
2612 let name = controller.'/'.name
2613 endif
2614 endif
2615 if name =~# '\.\w\+\.\w\+$' || name =~# '\.'.s:viewspattern().'$'
2616 return pre.name
2617 else
2618 for format in ['.'.s:format('html'), '']
2619 for type in s:view_types
2620 if self.app().has_file(pre.name.format.'.'.type)
2621 return pre.name.format.'.'.type
2622 endif
2623 endfor
2624 endfor
2625 endif
2626 return ''
2627 endfunction
2628
2629 function! s:findlayout(name)
2630 return s:findview("layouts/".(a:name == '' ? 'application' : a:name))
2631 endfunction
2632
2633 function! s:layoutEdit(cmd,...)
2634 if a:0
2635 return s:viewEdit(a:cmd,"layouts/".a:1)
2636 endif
2637 let file = s:findlayout(s:controller(1))
2638 if file == ""
2639 let file = s:findlayout("application")
2640 endif
2641 if file == ""
2642 let file = "app/views/layouts/application.html.erb"
2643 endif
2644 call s:edit(a:cmd,s:sub(file,'^/',''))
2645 endfunction
2646
2647 function! s:controllerEdit(cmd,...)
2648 let suffix = '.rb'
2649 if a:0 == 0
2650 let controller = s:controller(1)
2651 if rails#buffer().type_name() =~# '^view\%(-layout\|-partial\)\@!'
2652 let suffix .= '#'.expand('%:t:r')
2653 endif
2654 else
2655 let controller = a:1
2656 endif
2657 if rails#app().has_file("app/controllers/".controller."_controller.rb") || !rails#app().has_file("app/controllers/".controller.".rb")
2658 let suffix = "_controller".suffix
2659 endif
2660 return s:EditSimpleRb(a:cmd,"controller",controller,"app/controllers/",suffix)
2661 endfunction
2662
2663 function! s:mailerEdit(cmd,...)
2664 return s:EditSimpleRb(a:cmd,"mailer",a:0? a:1 : s:controller(1),"app/mailers/\napp/models/",".rb")
2665 endfunction
2666
2667 function! s:helperEdit(cmd,...)
2668 return s:EditSimpleRb(a:cmd,"helper",a:0? a:1 : s:controller(1),"app/helpers/","_helper.rb")
2669 endfunction
2670
2671 function! s:stylesheetEdit(cmd,...)
2672 let name = a:0 ? a:1 : s:controller(1)
2673 if rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.sass')
2674 return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".sass",1)
2675 elseif rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.scss')
2676 return s:EditSimpleRb(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".scss",1)
2677 elseif rails#app().has('lesscss') && rails#app().has_file('app/stylesheets/'.name.'.less')
2678 return s:EditSimpleRb(a:cmd,"stylesheet",name,"app/stylesheets/",".less",1)
2679 else
2680 let types = rails#app().relglob('app/assets/stylesheets/'.name,'.*','')
2681 if !empty(types)
2682 return s:EditSimpleRb(a:cmd,'stylesheet',name,'app/assets/stylesheets/',types[0],1)
2683 else
2684 return s:EditSimpleRb(a:cmd,'stylesheet',name,'public/stylesheets/','.css',1)
2685 endif
2686 endif
2687 endfunction
2688
2689 function! s:javascriptEdit(cmd,...)
2690 let name = a:0 ? a:1 : s:controller(1)
2691 if rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.coffee')
2692 return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.coffee',1)
2693 elseif rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.js')
2694 return s:EditSimpleRb(a:cmd,'javascript',name,'app/scripts/','.js',1)
2695 else
2696 let types = rails#app().relglob('app/assets/javascripts/'.name,'.*','')
2697 if !empty(types)
2698 return s:EditSimpleRb(a:cmd,'javascript',name,'app/assets/javascripts/',types[0],1)
2699 else
2700 return s:EditSimpleRb(a:cmd,'javascript',name,'public/javascripts/','.js',1)
2701 endif
2702 endif
2703 endfunction
2704
2705 function! s:unittestEdit(cmd,...)
2706 let f = rails#underscore(a:0 ? matchstr(a:1,'[^!#:]*') : s:model(1))
2707 let jump = a:0 ? matchstr(a:1,'[!#:].*') : ''
2708 if jump =~ '!'
2709 let cmd = s:editcmdfor(a:cmd)
2710 else
2711 let cmd = s:findcmdfor(a:cmd)
2712 endif
2713 let mapping = {'test': ['test/unit/','_test.rb'], 'spec': ['spec/models/','_spec.rb']}
2714 let tests = map(filter(rails#app().test_suites(),'has_key(mapping,v:val)'),'get(mapping,v:val)')
2715 if empty(tests)
2716 let tests = [mapping['test']]
2717 endif
2718 for [prefix, suffix] in tests
2719 if !a:0 && rails#buffer().type_name('model-aro') && f != '' && f !~# '_observer$'
2720 if rails#app().has_file(prefix.f.'_observer'.suffix)
2721 return s:findedit(cmd,prefix.f.'_observer'.suffix.jump)
2722 endif
2723 endif
2724 endfor
2725 for [prefix, suffix] in tests
2726 if rails#app().has_file(prefix.f.suffix)
2727 return s:findedit(cmd,prefix.f.suffix.jump)
2728 endif
2729 endfor
2730 return s:EditSimpleRb(a:cmd,"unittest",f.jump,tests[0][0],tests[0][1],1)
2731 endfunction
2732
2733 function! s:functionaltestEdit(cmd,...)
2734 let f = rails#underscore(a:0 ? matchstr(a:1,'[^!#:]*') : s:controller(1))
2735 let jump = a:0 ? matchstr(a:1,'[!#:].*') : ''
2736 if jump =~ '!'
2737 let cmd = s:editcmdfor(a:cmd)
2738 else
2739 let cmd = s:findcmdfor(a:cmd)
2740 endif
2741 let mapping = {'test': [['test/functional/'],['_test.rb','_controller_test.rb']], 'spec': [['spec/controllers/','spec/mailers/'],['_spec.rb','_controller_spec.rb']]}
2742 let tests = map(filter(rails#app().test_suites(),'has_key(mapping,v:val)'),'get(mapping,v:val)')
2743 if empty(tests)
2744 let tests = [mapping[tests]]
2745 endif
2746 for [prefixes, suffixes] in tests
2747 for prefix in prefixes
2748 for suffix in suffixes
2749 if rails#app().has_file(prefix.f.suffix)
2750 return s:findedit(cmd,prefix.f.suffix.jump)
2751 endif
2752 endfor
2753 endfor
2754 endfor
2755 return s:EditSimpleRb(a:cmd,"functionaltest",f.jump,tests[0][0][0],tests[0][1][0],1)
2756 endfunction
2757
2758 function! s:integrationtestEdit(cmd,...)
2759 if !a:0
2760 return s:EditSimpleRb(a:cmd,"integrationtest","test/test_helper\nfeatures/support/env\nspec/spec_helper","",".rb")
2761 endif
2762 let f = rails#underscore(matchstr(a:1,'[^!#:]*'))
2763 let jump = matchstr(a:1,'[!#:].*')
2764 if jump =~ '!'
2765 let cmd = s:editcmdfor(a:cmd)
2766 else
2767 let cmd = s:findcmdfor(a:cmd)
2768 endif
2769 let tests = [['test/integration/','_test.rb'], [ 'spec/requests/','_spec.rb'], [ 'spec/integration/','_spec.rb'], [ 'features/','.feature']]
2770 call filter(tests, 'isdirectory(rails#app().path(v:val[0]))')
2771 if empty(tests)
2772 let tests = [['test/integration/','_test.rb']]
2773 endif
2774 for [prefix, suffix] in tests
2775 if rails#app().has_file(prefix.f.suffix)
2776 return s:findedit(cmd,prefix.f.suffix.jump)
2777 elseif rails#app().has_file(prefix.rails#underscore(f).suffix)
2778 return s:findedit(cmd,prefix.rails#underscore(f).suffix.jump)
2779 endif
2780 endfor
2781 return s:EditSimpleRb(a:cmd,"integrationtest",f.jump,tests[0][0],tests[0][1],1)
2782 endfunction
2783
2784 function! s:specEdit(cmd,...)
2785 if a:0
2786 return s:EditSimpleRb(a:cmd,"spec",a:1,"spec/","_spec.rb")
2787 else
2788 call s:EditSimpleRb(a:cmd,"spec","spec_helper","spec/",".rb")
2789 endif
2790 endfunction
2791
2792 function! s:pluginEdit(cmd,...)
2793 let cmd = s:findcmdfor(a:cmd)
2794 let plugin = ""
2795 let extra = ""
2796 if RailsFilePath() =~ '\<vendor/plugins/.'
2797 let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/\zs[^/]*\ze')
2798 let extra = "vendor/plugins/" . plugin . "/\n"
2799 endif
2800 if a:0
2801 if a:1 =~ '^[^/.]*/\=$' && rails#app().has_file("vendor/plugins/".a:1."/init.rb")
2802 return s:EditSimpleRb(a:cmd,"plugin",s:sub(a:1,'/$',''),"vendor/plugins/","/init.rb")
2803 elseif plugin == ""
2804 call s:edit(cmd,"vendor/plugins/".s:sub(a:1,'\.$',''))
2805 elseif a:1 == "."
2806 call s:findedit(cmd,"vendor/plugins/".plugin)
2807 elseif isdirectory(rails#app().path("vendor/plugins/".matchstr(a:1,'^[^/]*')))
2808 call s:edit(cmd,"vendor/plugins/".a:1)
2809 else
2810 call s:findedit(cmd,"vendor/plugins/".a:1."\nvendor/plugins/".plugin."/".a:1)
2811 endif
2812 else
2813 call s:findedit(a:cmd,"Gemfile")
2814 endif
2815 endfunction
2816
2817 function! s:taskEdit(cmd,...)
2818 let plugin = ""
2819 let extra = ""
2820 if RailsFilePath() =~ '\<vendor/plugins/.'
2821 let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/[^/]*')
2822 let extra = plugin."/tasks/\n".plugin."/lib/tasks/\n"
2823 endif
2824 if a:0
2825 call s:EditSimpleRb(a:cmd,"task",a:1,extra."lib/tasks/",".rake")
2826 else
2827 call s:findedit(a:cmd,(plugin != "" ? plugin."/Rakefile\n" : "")."Rakefile")
2828 endif
2829 endfunction
2830
2831 function! s:libEdit(cmd,...)
2832 let extra = ""
2833 if RailsFilePath() =~ '\<vendor/plugins/.'
2834 let extra = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')."\n"
2835 endif
2836 if a:0
2837 call s:EditSimpleRb(a:cmd,"lib",a:0? a:1 : "",extra."lib/",".rb")
2838 else
2839 call s:EditSimpleRb(a:cmd,"lib","seeds","db/",".rb")
2840 endif
2841 endfunction
2842
2843 function! s:environmentEdit(cmd,...)
2844 if a:0 || rails#app().has_file('config/application.rb')
2845 return s:EditSimpleRb(a:cmd,"environment",a:0? a:1 : "../application","config/environments/",".rb")
2846 else
2847 return s:EditSimpleRb(a:cmd,"environment","environment","config/",".rb")
2848 endif
2849 endfunction
2850
2851 function! s:initializerEdit(cmd,...)
2852 return s:EditSimpleRb(a:cmd,"initializer",a:0? a:1 : "../routes","config/initializers/",".rb")
2853 endfunction
2854
2855 " }}}1
2856 " Alternate/Related {{{1
2857
2858 function! s:findcmdfor(cmd)
2859 let bang = ''
2860 if a:cmd =~ '\!$'
2861 let bang = '!'
2862 let cmd = s:sub(a:cmd,'\!$','')
2863 else
2864 let cmd = a:cmd
2865 endif
2866 if cmd =~ '^\d'
2867 let num = matchstr(cmd,'^\d\+')
2868 let cmd = s:sub(cmd,'^\d+','')
2869 else
2870 let num = ''
2871 endif
2872 if cmd == '' || cmd == 'E' || cmd == 'F'
2873 return num.'find'.bang
2874 elseif cmd == 'S'
2875 return num.'sfind'.bang
2876 elseif cmd == 'V'
2877 return 'vert '.num.'sfind'.bang
2878 elseif cmd == 'T'
2879 return num.'tabfind'.bang
2880 elseif cmd == 'D'
2881 return num.'read'.bang
2882 else
2883 return num.cmd.bang
2884 endif
2885 endfunction
2886
2887 function! s:editcmdfor(cmd)
2888 let cmd = s:findcmdfor(a:cmd)
2889 let cmd = s:sub(cmd,'<sfind>','split')
2890 let cmd = s:sub(cmd,'find>','edit')
2891 return cmd
2892 endfunction
2893
2894 function! s:try(cmd) abort
2895 if !exists(":try")
2896 " I've seen at least one weird setup without :try
2897 exe a:cmd
2898 else
2899 try
2900 exe a:cmd
2901 catch
2902 call s:error(s:sub(v:exception,'^.{-}:\zeE',''))
2903 return 0
2904 endtry
2905 endif
2906 return 1
2907 endfunction
2908
2909 function! s:findedit(cmd,files,...) abort
2910 let cmd = s:findcmdfor(a:cmd)
2911 let files = type(a:files) == type([]) ? copy(a:files) : split(a:files,"\n")
2912 if len(files) == 1
2913 let file = files[0]
2914 else
2915 let file = get(filter(copy(files),'rails#app().has_file(s:sub(v:val,"#.*|:\\d*$",""))'),0,get(files,0,''))
2916 endif
2917 if file =~ '[#!]\|:\d*\%(:in\)\=$'
2918 let djump = matchstr(file,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$')
2919 let file = s:sub(file,'[#!].*|:\d*%(:in)=$','')
2920 else
2921 let djump = ''
2922 endif
2923 if file == ''
2924 let testcmd = "edit"
2925 elseif isdirectory(rails#app().path(file))
2926 let arg = file == "." ? rails#app().path() : rails#app().path(file)
2927 let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:escarg(arg)
2928 exe testcmd
2929 return
2930 elseif rails#app().path() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
2931 if file !~ '^/' && file !~ '^\w:' && file !~ '://'
2932 let file = s:escarg(rails#app().path(file))
2933 endif
2934 let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file
2935 else
2936 let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file
2937 endif
2938 if s:try(testcmd)
2939 call s:djump(djump)
2940 endif
2941 endfunction
2942
2943 function! s:edit(cmd,file,...)
2944 let cmd = s:editcmdfor(a:cmd)
2945 let cmd .= ' '.(a:0 ? a:1 . ' ' : '')
2946 let file = a:file
2947 if file !~ '^/' && file !~ '^\w:' && file !~ '://'
2948 exe cmd."`=fnamemodify(rails#app().path(file),':.')`"
2949 else
2950 exe cmd.file
2951 endif
2952 endfunction
2953
2954 function! s:Alternate(cmd,line1,line2,count,...)
2955 if a:0
2956 if a:count && a:cmd !~# 'D'
2957 return call('s:Find',[1,a:line1.a:cmd]+a:000)
2958 elseif a:count
2959 return call('s:Edit',[1,a:line1.a:cmd]+a:000)
2960 else
2961 return call('s:Edit',[1,a:cmd]+a:000)
2962 endif
2963 else
2964 let file = s:getopt(a:count ? 'related' : 'alternate', 'bl')
2965 if file == ''
2966 let file = rails#buffer().related(a:count)
2967 endif
2968 if file != ''
2969 call s:findedit(a:cmd,file)
2970 else
2971 call s:warn("No alternate file is defined")
2972 endif
2973 endif
2974 endfunction
2975
2976 function! s:Related(cmd,line1,line2,count,...)
2977 if a:count == 0 && a:0 == 0
2978 return s:Alternate(a:cmd,a:line1,a:line1,a:line1)
2979 else
2980 return call('s:Alternate',[a:cmd,a:line1,a:line2,a:count]+a:000)
2981 endif
2982 endfunction
2983
2984 function! s:Complete_related(A,L,P)
2985 if a:L =~# '^[[:alpha:]]'
2986 return s:Complete_edit(a:A,a:L,a:P)
2987 else
2988 return s:Complete_find(a:A,a:L,a:P)
2989 endif
2990 endfunction
2991
2992 function! s:readable_related(...) dict abort
2993 let f = self.name()
2994 if a:0 && a:1
2995 let lastmethod = self.last_method(a:1)
2996 if self.type_name('controller','mailer') && lastmethod != ""
2997 let root = s:sub(s:sub(s:sub(f,'/application%(_controller)=\.rb$','/shared_controller.rb'),'/%(controllers|models|mailers)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod)
2998 let format = self.last_format(a:1)
2999 if format == ''
3000 let format = self.type_name('mailer') ? 'text' : 'html'
3001 endif
3002 if glob(self.app().path().'/'.root.'.'.format.'.*[^~]') != ''
3003 return root . '.' . format
3004 else
3005 return root
3006 endif
3007 elseif f =~ '\<config/environments/'
3008 return "config/database.yml#". fnamemodify(f,':t:r')
3009 elseif f =~ '\<config/database\.yml$'
3010 if lastmethod != ""
3011 return "config/environments/".lastmethod.".rb"
3012 else
3013 return "config/application.rb\nconfig/environment.rb"
3014 endif
3015 elseif f =~ '\<config/routes\.rb$' | return "config/database.yml"
3016 elseif f =~ '\<config/\%(application\|environment\)\.rb$'
3017 return "config/routes.rb"
3018 elseif self.type_name('view-layout')
3019 return s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb')
3020 elseif self.type_name('view')
3021 let controller = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1')
3022 let controller2 = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1')
3023 let mailer = s:sub(s:sub(f,'/views/','/mailers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1')
3024 let model = s:sub(s:sub(f,'/views/','/models/'),'/(\k+)\..*$','.rb#\1')
3025 if self.app().has_file(s:sub(controller,'#.{-}$',''))
3026 return controller
3027 elseif self.app().has_file(s:sub(controller2,'#.{-}$',''))
3028 return controller2
3029 elseif self.app().has_file(s:sub(mailer,'#.{-}$',''))
3030 return mailer
3031 elseif self.app().has_file(s:sub(model,'#.{-}$','')) || model =~ '_mailer\.rb#'
3032 return model
3033 else
3034 return controller
3035 endif
3036 elseif self.type_name('controller')
3037 return s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb')
3038 " elseif self.type_name('helper')
3039 " return s:findlayout(s:controller())
3040 elseif self.type_name('model-arb')
3041 let table_name = matchstr(join(self.getline(1,50),"\n"),'\n\s*set_table_name\s*[:"'']\zs\w\+')
3042 if table_name == ''
3043 let table_name = rails#pluralize(s:gsub(s:sub(fnamemodify(f,':r'),'.{-}<app/models/',''),'/','_'))
3044 endif
3045 return self.app().migration('0#'.table_name)
3046 elseif self.type_name('model-aro')
3047 return s:sub(f,'_observer\.rb$','.rb')
3048 elseif self.type_name('db-schema')
3049 return self.app().migration(1)
3050 endif
3051 endif
3052 if f =~ '\<config/environments/'
3053 return "config/application.rb\nconfig/environment.rb"
3054 elseif f == 'README'
3055 return "config/database.yml"
3056 elseif f =~ '\<config/database\.yml$' | return "config/routes.rb"
3057 elseif f =~ '\<config/routes\.rb$'
3058 return "config/application.rb\nconfig/environment.rb"
3059 elseif f =~ '\<config/\%(application\|environment\)\.rb$'
3060 return "config/database.yml"
3061 elseif f ==# 'Gemfile'
3062 return 'Gemfile.lock'
3063 elseif f ==# 'Gemfile.lock'
3064 return 'Gemfile'
3065 elseif f =~ '\<db/migrate/'
3066 let migrations = sort(self.app().relglob('db/migrate/','*','.rb'))
3067 let me = matchstr(f,'\<db/migrate/\zs.*\ze\.rb$')
3068 if !exists('l:lastmethod') || lastmethod == 'down'
3069 let candidates = reverse(filter(copy(migrations),'v:val < me'))
3070 let migration = "db/migrate/".get(candidates,0,migrations[-1]).".rb"
3071 else
3072 let candidates = filter(copy(migrations),'v:val > me')
3073 let migration = "db/migrate/".get(candidates,0,migrations[0]).".rb"
3074 endif
3075 return migration . (exists('l:lastmethod') && lastmethod != '' ? '#'.lastmethod : '')
3076 elseif f =~ '\<application\.js$'
3077 return "app/helpers/application_helper.rb"
3078 elseif self.type_name('javascript')
3079 return "public/javascripts/application.js"
3080 elseif self.type_name('db/schema')
3081 return self.app().migration('')
3082 elseif self.type_name('view')
3083 let spec1 = fnamemodify(f,':s?\<app/?spec/?')."_spec.rb"
3084 let spec2 = fnamemodify(f,':r:s?\<app/?spec/?')."_spec.rb"
3085 let spec3 = fnamemodify(f,':r:r:s?\<app/?spec/?')."_spec.rb"
3086 if self.app().has_file(spec1)
3087 return spec1
3088 elseif self.app().has_file(spec2)
3089 return spec2
3090 elseif self.app().has_file(spec3)
3091 return spec3
3092 elseif self.app().has('spec')
3093 return spec2
3094 else
3095 if self.type_name('view-layout')
3096 let dest = fnamemodify(f,':r:s?/layouts\>??').'/layout.'.fnamemodify(f,':e')
3097 else
3098 let dest = f
3099 endif
3100 return s:sub(s:sub(dest,'<app/views/','test/functional/'),'/[^/]*$','_controller_test.rb')
3101 endif
3102 elseif self.type_name('controller-api')
3103 let api = s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb')
3104 return api
3105 elseif self.type_name('api')
3106 return s:sub(s:sub(f,'/apis/','/controllers/'),'_api\.rb$','_controller.rb')
3107 elseif self.type_name('fixtures') && f =~ '\<spec/'
3108 let file = rails#singularize(fnamemodify(f,":t:r")).'_spec.rb'
3109 return file
3110 elseif self.type_name('fixtures')
3111 let file = rails#singularize(fnamemodify(f,":t:r")).'_test.rb'
3112 return file
3113 elseif f == ''
3114 call s:warn("No filename present")
3115 elseif f =~ '\<test/unit/routing_test\.rb$'
3116 return 'config/routes.rb'
3117 elseif self.type_name('spec-view')
3118 return s:sub(s:sub(f,'<spec/','app/'),'_spec\.rb$','')
3119 elseif fnamemodify(f,":e") == "rb"
3120 let file = fnamemodify(f,":r")
3121 if file =~ '_\%(test\|spec\)$'
3122 let file = s:sub(file,'_%(test|spec)$','.rb')
3123 else
3124 let file .= '_test.rb'
3125 endif
3126 if self.type_name('helper')
3127 return s:sub(file,'<app/helpers/','test/unit/helpers/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/helpers/','spec/helpers/')
3128 elseif self.type_name('model')
3129 return s:sub(file,'<app/models/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/models/','spec/models/')
3130 elseif self.type_name('controller')
3131 return s:sub(file,'<app/controllers/','test/functional/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/controllers/','spec/controllers/')
3132 elseif self.type_name('mailer')
3133 return s:sub(file,'<app/m%(ailer|odel)s/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'<app/','spec/')
3134 elseif self.type_name('test-unit')
3135 return s:sub(s:sub(file,'test/unit/helpers/','app/helpers/'),'test/unit/','app/models/')."\n".s:sub(file,'test/unit/','lib/')
3136 elseif self.type_name('test-functional')
3137 if file =~ '_api\.rb'
3138 return s:sub(file,'test/functional/','app/apis/')
3139 elseif file =~ '_controller\.rb'
3140 return s:sub(file,'test/functional/','app/controllers/')
3141 else
3142 return s:sub(file,'test/functional/','')
3143 endif
3144 elseif self.type_name('spec-lib')
3145 return s:sub(file,'<spec/','')
3146 elseif self.type_name('lib')
3147 return s:sub(f, '<lib/(.*)\.rb$', 'test/unit/\1_test.rb')."\n".s:sub(f, '<lib/(.*)\.rb$', 'spec/lib/\1_spec.rb')
3148 elseif self.type_name('spec')
3149 return s:sub(file,'<spec/','app/')
3150 elseif file =~ '\<vendor/.*/lib/'
3151 return s:sub(file,'<vendor/.{-}/\zslib/','test/')
3152 elseif file =~ '\<vendor/.*/test/'
3153 return s:sub(file,'<vendor/.{-}/\zstest/','lib/')
3154 else
3155 return fnamemodify(file,':t')."\n".s:sub(s:sub(f,'\.rb$','_spec.rb'),'^app/','spec/')
3156 endif
3157 else
3158 return ""
3159 endif
3160 endfunction
3161
3162 call s:add_methods('readable',['related'])
3163
3164 " }}}1
3165 " Partial Extraction {{{1
3166
3167 function! s:Extract(bang,...) range abort
3168 if a:0 == 0 || a:0 > 1
3169 return s:error("Incorrect number of arguments")
3170 endif
3171 if a:1 =~ '[^a-z0-9_/.]'
3172 return s:error("Invalid partial name")
3173 endif
3174 let rails_root = rails#app().path()
3175 let ext = expand("%:e")
3176 let file = s:sub(a:1,'%(/|^)\zs_\ze[^/]*$','')
3177 let first = a:firstline
3178 let last = a:lastline
3179 let range = first.",".last
3180 if rails#buffer().type_name('view-layout')
3181 if RailsFilePath() =~ '\<app/views/layouts/application\>'
3182 let curdir = 'app/views/shared'
3183 if file !~ '/'
3184 let file = "shared/" .file
3185 endif
3186 else
3187 let curdir = s:sub(RailsFilePath(),'.*<app/views/layouts/(.*)%(\.\w*)$','app/views/\1')
3188 endif
3189 else
3190 let curdir = fnamemodify(RailsFilePath(),':h')
3191 endif
3192 let curdir = rails_root."/".curdir
3193 let dir = fnamemodify(file,":h")
3194 let fname = fnamemodify(file,":t")
3195 if fnamemodify(fname,":e") == ""
3196 let name = fname
3197 let fname .= ".".matchstr(expand("%:t"),'\.\zs.*')
3198 elseif fnamemodify(fname,":e") !~ '^'.s:viewspattern().'$'
3199 let name = fnamemodify(fname,":r")
3200 let fname .= ".".ext
3201 else
3202 let name = fnamemodify(fname,":r:r")
3203 endif
3204 let var = "@".name
3205 let collection = ""
3206 if dir =~ '^/'
3207 let out = (rails_root).dir."/_".fname
3208 elseif dir == "" || dir == "."
3209 let out = (curdir)."/_".fname
3210 elseif isdirectory(curdir."/".dir)
3211 let out = (curdir)."/".dir."/_".fname
3212 else
3213 let out = (rails_root)."/app/views/".dir."/_".fname
3214 endif
3215 if filereadable(out) && !a:bang
3216 return s:error('E13: File exists (add ! to override)')
3217 endif
3218 if !isdirectory(fnamemodify(out,':h'))
3219 if a:bang
3220 call mkdir(fnamemodify(out,':h'),'p')
3221 else
3222 return s:error('No such directory')
3223 endif
3224 endif
3225 " No tabs, they'll just complicate things
3226 if ext =~? '^\%(rhtml\|erb\|dryml\)$'
3227 let erub1 = '\<\%\s*'
3228 let erub2 = '\s*-=\%\>'
3229 else
3230 let erub1 = ''
3231 let erub2 = ''
3232 endif
3233 let spaces = matchstr(getline(first),"^ *")
3234 if getline(last+1) =~ '\v^\s*'.erub1.'end'.erub2.'\s*$'
3235 let fspaces = matchstr(getline(last+1),"^ *")
3236 if getline(first-1) =~ '\v^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ %>]+)'.erub2.'\s*$'
3237 let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ >]+)'.erub2.'\s*$','\1>\2')
3238 elseif getline(first-1) =~ '\v^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$'
3239 let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$','\2>\1')
3240 endif
3241 if collection != ''
3242 let var = matchstr(collection,'^\k\+')
3243 let collection = s:sub(collection,'^\k+\>','')
3244 let first -= 1
3245 let last += 1
3246 endif
3247 else
3248 let fspaces = spaces
3249 endif
3250 let renderstr = "render :partial => '".fnamemodify(file,":r:r")."'"
3251 if collection != ""
3252 let renderstr .= ", :collection => ".collection
3253 elseif "@".name != var
3254 let renderstr .= ", :object => ".var
3255 endif
3256 if ext =~? '^\%(rhtml\|erb\|dryml\)$'
3257 let renderstr = "<%= ".renderstr." %>"
3258 elseif ext == "rxml" || ext == "builder"
3259 let renderstr = "xml << ".s:sub(renderstr,"render ","render(").")"
3260 elseif ext == "rjs"
3261 let renderstr = "page << ".s:sub(renderstr,"render ","render(").")"
3262 elseif ext == "haml"
3263 let renderstr = "= ".renderstr
3264 elseif ext == "mn"
3265 let renderstr = "_".renderstr
3266 endif
3267 let buf = @@
3268 silent exe range."yank"
3269 let partial = @@
3270 let @@ = buf
3271 let old_ai = &ai
3272 try
3273 let &ai = 0
3274 silent exe "norm! :".first.",".last."change\<CR>".fspaces.renderstr."\<CR>.\<CR>"
3275 finally
3276 let &ai = old_ai
3277 endtry
3278 if renderstr =~ '<%'
3279 norm ^6w
3280 else
3281 norm ^5w
3282 endif
3283 let ft = &ft
3284 let shortout = fnamemodify(out,':.')
3285 silent split `=shortout`
3286 silent %delete
3287 let &ft = ft
3288 let @@ = partial
3289 silent put
3290 0delete
3291 let @@ = buf
3292 if spaces != ""
3293 silent! exe '%substitute/^'.spaces.'//'
3294 endif
3295 silent! exe '%substitute?\%(\w\|[@:"'."'".'-]\)\@<!'.var.'\>?'.name.'?g'
3296 1
3297 endfunction
3298
3299 " }}}1
3300 " Migration Inversion {{{1
3301
3302 function! s:mkeep(str)
3303 " Things to keep (like comments) from a migration statement
3304 return matchstr(a:str,' #[^{].*')
3305 endfunction
3306
3307 function! s:mextargs(str,num)
3308 if a:str =~ '^\s*\w\+\s*('
3309 return s:sub(matchstr(a:str,'^\s*\w\+\s*\zs(\%([^,)]\+[,)]\)\{,'.a:num.'\}'),',$',')')
3310 else
3311 return s:sub(s:sub(matchstr(a:str,'\w\+\>\zs\s*\%([^,){ ]*[, ]*\)\{,'.a:num.'\}'),'[, ]*$',''),'^\s+',' ')
3312 endif
3313 endfunction
3314
3315 function! s:migspc(line)
3316 return matchstr(a:line,'^\s*')
3317 endfunction
3318
3319 function! s:invertrange(beg,end)
3320 let str = ""
3321 let lnum = a:beg
3322 while lnum <= a:end
3323 let line = getline(lnum)
3324 let add = ""
3325 if line == ''
3326 let add = ' '
3327 elseif line =~ '^\s*\(#[^{].*\)\=$'
3328 let add = line
3329 elseif line =~ '\<create_table\>'
3330 let add = s:migspc(line)."drop_table".s:mextargs(line,1).s:mkeep(line)
3331 let lnum = s:endof(lnum)
3332 elseif line =~ '\<drop_table\>'
3333 let add = s:sub(line,'<drop_table>\s*\(=\s*([^,){ ]*).*','create_table \1 do |t|'."\n".matchstr(line,'^\s*').'end').s:mkeep(line)
3334 elseif line =~ '\<add_column\>'
3335 let add = s:migspc(line).'remove_column'.s:mextargs(line,2).s:mkeep(line)
3336 elseif line =~ '\<remove_column\>'
3337 let add = s:sub(line,'<remove_column>','add_column')
3338 elseif line =~ '\<add_index\>'
3339 let add = s:migspc(line).'remove_index'.s:mextargs(line,1)
3340 let mat = matchstr(line,':name\s*=>\s*\zs[^ ,)]*')
3341 if mat != ''
3342 let add = s:sub(add,'\)=$',', :name => '.mat.'&')
3343 else
3344 let mat = matchstr(line,'\<add_index\>[^,]*,\s*\zs\%(\[[^]]*\]\|[:"'."'".']\w*["'."'".']\=\)')
3345 if mat != ''
3346 let add = s:sub(add,'\)=$',', :column => '.mat.'&')
3347 endif
3348 endif
3349 let add .= s:mkeep(line)
3350 elseif line =~ '\<remove_index\>'
3351 let add = s:sub(s:sub(line,'<remove_index','add_index'),':column\s*\=\>\s*','')
3352 elseif line =~ '\<rename_\%(table\|column\|index\)\>'
3353 let add = s:sub(line,'<rename_%(table\s*\(=\s*|%(column|index)\s*\(=\s*[^,]*,\s*)\zs([^,]*)(,\s*)([^,]*)','\3\2\1')
3354 elseif line =~ '\<change_column\>'
3355 let add = s:migspc(line).'change_column'.s:mextargs(line,2).s:mkeep(line)
3356 elseif line =~ '\<change_column_default\>'
3357 let add = s:migspc(line).'change_column_default'.s:mextargs(line,2).s:mkeep(line)
3358 elseif line =~ '\.update_all(\(["'."'".']\).*\1)$' || line =~ '\.update_all \(["'."'".']\).*\1$'
3359 " .update_all('a = b') => .update_all('b = a')
3360 let pre = matchstr(line,'^.*\.update_all[( ][}'."'".'"]')
3361 let post = matchstr(line,'["'."'".'])\=$')
3362 let mat = strpart(line,strlen(pre),strlen(line)-strlen(pre)-strlen(post))
3363 let mat = s:gsub(','.mat.',','%(,\s*)@<=([^ ,=]{-})(\s*\=\s*)([^,=]{-})%(\s*,)@=','\3\2\1')
3364 let add = pre.s:sub(s:sub(mat,'^,',''),',$','').post
3365 elseif line =~ '^s\*\%(if\|unless\|while\|until\|for\)\>'
3366 let lnum = s:endof(lnum)
3367 endif
3368 if lnum == 0
3369 return -1
3370 endif
3371 if add == ""
3372 let add = s:sub(line,'^\s*\zs.*','raise ActiveRecord::IrreversibleMigration')
3373 elseif add == " "
3374 let add = ""
3375 endif
3376 let str = add."\n".str
3377 let lnum += 1
3378 endwhile
3379 let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversibleMigration\n)+','\1')
3380 return str
3381 endfunction
3382
3383 function! s:Invert(bang)
3384 let err = "Could not parse method"
3385 let src = "up"
3386 let dst = "down"
3387 let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
3388 let end = s:endof(beg)
3389 if beg + 1 == end
3390 let src = "down"
3391 let dst = "up"
3392 let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
3393 let end = s:endof(beg)
3394 endif
3395 if !beg || !end
3396 return s:error(err)
3397 endif
3398 let str = s:invertrange(beg+1,end-1)
3399 if str == -1
3400 return s:error(err)
3401 endif
3402 let beg = search('\%('.&l:define.'\).*'.dst.'\>',"w")
3403 let end = s:endof(beg)
3404 if !beg || !end
3405 return s:error(err)
3406 endif
3407 if foldclosed(beg) > 0
3408 exe beg."foldopen!"
3409 endif
3410 if beg + 1 < end
3411 exe (beg+1).",".(end-1)."delete _"
3412 endif
3413 if str != ''
3414 exe beg.'put =str'
3415 exe 1+beg
3416 endif
3417 endfunction
3418
3419 " }}}1
3420 " Cache {{{1
3421
3422 let s:cache_prototype = {'dict': {}}
3423
3424 function! s:cache_clear(...) dict
3425 if a:0 == 0
3426 let self.dict = {}
3427 elseif has_key(self,'dict') && has_key(self.dict,a:1)
3428 unlet! self.dict[a:1]
3429 endif
3430 endfunction
3431
3432 function! rails#cache_clear(...)
3433 if exists('b:rails_root')
3434 return call(rails#app().cache.clear,a:000,rails#app().cache)
3435 endif
3436 endfunction
3437
3438 function! s:cache_get(...) dict
3439 if a:0 == 1
3440 return self.dict[a:1]
3441 else
3442 return self.dict
3443 endif
3444 endfunction
3445
3446 function! s:cache_has(key) dict
3447 return has_key(self.dict,a:key)
3448 endfunction
3449
3450 function! s:cache_needs(key) dict
3451 return !has_key(self.dict,a:key)
3452 endfunction
3453
3454 function! s:cache_set(key,value) dict
3455 let self.dict[a:key] = a:value
3456 endfunction
3457
3458 call s:add_methods('cache', ['clear','needs','has','get','set'])
3459
3460 let s:app_prototype.cache = s:cache_prototype
3461
3462 " }}}1
3463 " Syntax {{{1
3464
3465 function! s:resetomnicomplete()
3466 if exists("+completefunc") && &completefunc == 'syntaxcomplete#Complete'
3467 if exists("g:loaded_syntax_completion")
3468 " Ugly but necessary, until we have our own completion
3469 unlet g:loaded_syntax_completion
3470 silent! delfunction syntaxcomplete#Complete
3471 endif
3472 endif
3473 endfunction
3474
3475 function! s:helpermethods()
3476 return ""
3477 \."action_name atom_feed audio_path audio_tag auto_discovery_link_tag "
3478 \."button_tag button_to button_to_function "
3479 \."cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag content_tag_for controller controller_name controller_path convert_to_model cookies csrf_meta_tag csrf_meta_tags current_cycle cycle "
3480 \."date_select datetime_select debug distance_of_time_in_words distance_of_time_in_words_to_now div_for dom_class dom_id "
3481 \."email_field email_field_tag escape_javascript escape_once excerpt "
3482 \."favicon_link_tag field_set_tag fields_for file_field file_field_tag flash form_for form_tag "
3483 \."grouped_collection_select grouped_options_for_select "
3484 \."headers hidden_field hidden_field_tag highlight "
3485 \."image_alt image_path image_submit_tag image_tag "
3486 \."j javascript_cdata_section javascript_include_tag javascript_path javascript_tag "
3487 \."l label label_tag link_to link_to_function link_to_if link_to_unless link_to_unless_current localize logger "
3488 \."mail_to "
3489 \."number_field number_field_tag number_to_currency number_to_human number_to_human_size number_to_percentage number_to_phone number_with_delimiter number_with_precision "
3490 \."option_groups_from_collection_for_select options_for_select options_from_collection_for_select "
3491 \."params password_field password_field_tag path_to_audio path_to_image path_to_javascript path_to_stylesheet path_to_video phone_field phone_field_tag pluralize provide "
3492 \."radio_button radio_button_tag range_field range_field_tag raw render request request_forgery_protection_token reset_cycle response "
3493 \."safe_concat safe_join sanitize sanitize_css search_field search_field_tag select select_date select_datetime select_day select_hour select_minute select_month select_second select_tag select_time select_year session simple_format strip_links strip_tags stylesheet_link_tag stylesheet_path submit_tag "
3494 \."t tag telephone_field telephone_field_tag text_area text_area_tag text_field text_field_tag time_ago_in_words time_select time_tag time_zone_options_for_select time_zone_select translate truncate "
3495 \."url_field url_field_tag url_for url_options "
3496 \."video_path video_tag "
3497 \."word_wrap"
3498 endfunction
3499
3500 function! s:app_user_classes() dict
3501 if self.cache.needs("user_classes")
3502 let controllers = self.relglob("app/controllers/","**/*",".rb")
3503 call map(controllers,'v:val == "application" ? v:val."_controller" : v:val')
3504 let classes =
3505 \ self.relglob("app/models/","**/*",".rb") +
3506 \ controllers +
3507 \ self.relglob("app/helpers/","**/*",".rb") +
3508 \ self.relglob("lib/","**/*",".rb")
3509 call map(classes,'rails#camelize(v:val)')
3510 call self.cache.set("user_classes",classes)
3511 endif
3512 return self.cache.get('user_classes')
3513 endfunction
3514
3515 function! s:app_user_assertions() dict
3516 if self.cache.needs("user_assertions")
3517 if self.has_file("test/test_helper.rb")
3518 let assertions = map(filter(s:readfile(self.path("test/test_helper.rb")),'v:val =~ "^ def assert_"'),'matchstr(v:val,"^ def \\zsassert_\\w\\+")')
3519 else
3520 let assertions = []
3521 endif
3522 call self.cache.set("user_assertions",assertions)
3523 endif
3524 return self.cache.get('user_assertions')
3525 endfunction
3526
3527 call s:add_methods('app', ['user_classes','user_assertions'])
3528
3529 function! s:BufSyntax()
3530 if (!exists("g:rails_syntax") || g:rails_syntax)
3531 let buffer = rails#buffer()
3532 let s:javascript_functions = "$ $$ $A $F $H $R $w jQuery"
3533 let classes = s:gsub(join(rails#app().user_classes(),' '),'::',' ')
3534 if &syntax == 'ruby'
3535 if classes != ''
3536 exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule"
3537 endif
3538 if buffer.type_name() == ''
3539 syn keyword rubyRailsMethod params request response session headers cookies flash
3540 endif
3541 if buffer.type_name('api')
3542 syn keyword rubyRailsAPIMethod api_method inflect_names
3543 endif
3544 if buffer.type_name() ==# 'model' || buffer.type_name('model-arb')
3545 syn keyword rubyRailsARMethod default_scope named_scope scope serialize
3546 syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many composed_of accepts_nested_attributes_for
3547 syn keyword rubyRailsARCallbackMethod before_create before_destroy before_save before_update before_validation before_validation_on_create before_validation_on_update
3548 syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update
3549 syn keyword rubyRailsARCallbackMethod around_create around_destroy around_save around_update
3550 syn keyword rubyRailsARCallbackMethod after_commit after_find after_initialize after_rollback after_touch
3551 syn keyword rubyRailsARClassMethod attr_accessible attr_protected attr_readonly establish_connection set_inheritance_column set_locking_column set_primary_key set_sequence_name set_table_name
3552 syn keyword rubyRailsARValidationMethod validate validates validate_on_create validate_on_update validates_acceptance_of validates_associated validates_confirmation_of validates_each validates_exclusion_of validates_format_of validates_inclusion_of validates_length_of validates_numericality_of validates_presence_of validates_size_of validates_uniqueness_of
3553 syn keyword rubyRailsMethod logger
3554 endif
3555 if buffer.type_name('model-aro')
3556 syn keyword rubyRailsARMethod observe
3557 endif
3558 if buffer.type_name('mailer')
3559 syn keyword rubyRailsMethod logger url_for polymorphic_path polymorphic_url
3560 syn keyword rubyRailsRenderMethod mail render
3561 syn keyword rubyRailsControllerMethod attachments default helper helper_attr helper_method
3562 endif
3563 if buffer.type_name('helper','view')
3564 syn keyword rubyRailsViewMethod polymorphic_path polymorphic_url
3565 exe "syn keyword rubyRailsHelperMethod ".s:gsub(s:helpermethods(),'<%(content_for|select)\s+','')
3566 syn match rubyRailsHelperMethod '\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!'
3567 syn match rubyRailsHelperMethod '\<\%(content_for?\=\|current_page?\)'
3568 syn match rubyRailsViewMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>'
3569 if buffer.type_name('view-partial')
3570 syn keyword rubyRailsMethod local_assigns
3571 endif
3572 elseif buffer.type_name('controller')
3573 syn keyword rubyRailsMethod params request response session headers cookies flash
3574 syn keyword rubyRailsRenderMethod render
3575 syn keyword rubyRailsMethod logger polymorphic_path polymorphic_url
3576 syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper protect_from_forgery caches_page cache_page caches_action expire_page expire_action rescue_from
3577 syn keyword rubyRailsRenderMethod head redirect_to render_to_string respond_with
3578 syn match rubyRailsRenderMethod '\<respond_to\>?\@!'
3579 syn keyword rubyRailsFilterMethod before_filter append_before_filter prepend_before_filter after_filter append_after_filter prepend_after_filter around_filter append_around_filter prepend_around_filter skip_before_filter skip_after_filter
3580 syn keyword rubyRailsFilterMethod verify
3581 endif
3582 if buffer.type_name('db-migration','db-schema')
3583 syn keyword rubyRailsMigrationMethod create_table change_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index rename_index execute
3584 endif
3585 if buffer.type_name('test')
3586 if !empty(rails#app().user_assertions())
3587 exe "syn keyword rubyRailsUserMethod ".join(rails#app().user_assertions())
3588 endif
3589 syn keyword rubyRailsTestMethod add_assertion assert assert_block assert_equal assert_in_delta assert_instance_of assert_kind_of assert_match assert_nil assert_no_match assert_not_equal assert_not_nil assert_not_same assert_nothing_raised assert_nothing_thrown assert_operator assert_raise assert_respond_to assert_same assert_send assert_throws assert_recognizes assert_generates assert_routing flunk fixtures fixture_path use_transactional_fixtures use_instantiated_fixtures assert_difference assert_no_difference assert_valid
3590 syn keyword rubyRailsTestMethod test setup teardown
3591 if !buffer.type_name('test-unit')
3592 syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
3593 syn keyword rubyRailsTestControllerMethod get_via_redirect post_via_redirect put_via_redirect delete_via_redirect request_via_redirect
3594 syn keyword rubyRailsTestControllerMethod assert_response assert_redirected_to assert_template assert_recognizes assert_generates assert_routing assert_dom_equal assert_dom_not_equal assert_select assert_select_rjs assert_select_encoded assert_select_email assert_tag assert_no_tag
3595 endif
3596 elseif buffer.type_name('spec')
3597 syn keyword rubyRailsTestMethod describe context it its specify shared_examples_for it_should_behave_like before after around subject fixtures controller_name helper_name
3598 syn match rubyRailsTestMethod '\<let\>!\='
3599 syn keyword rubyRailsTestMethod violated pending expect double mock mock_model stub_model
3600 syn match rubyRailsTestMethod '\.\@<!\<stub\>!\@!'
3601 if !buffer.type_name('spec-model')
3602 syn match rubyRailsTestControllerMethod '\.\@<!\<\%(get\|post\|put\|delete\|head\|process\|assigns\)\>'
3603 syn keyword rubyRailsTestControllerMethod integrate_views
3604 syn keyword rubyRailsMethod params request response session flash
3605 syn keyword rubyRailsMethod polymorphic_path polymorphic_url
3606 endif
3607 endif
3608 if buffer.type_name('task')
3609 syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!'
3610 endif
3611 if buffer.type_name('model-awss')
3612 syn keyword rubyRailsMethod member
3613 endif
3614 if buffer.type_name('config-routes')
3615 syn match rubyRailsMethod '\.\zs\%(connect\|named_route\)\>'
3616 syn keyword rubyRailsMethod match get put post delete redirect root resource resources collection member nested scope namespace controller constraints
3617 endif
3618 syn keyword rubyRailsMethod debugger
3619 syn keyword rubyRailsMethod alias_attribute alias_method_chain attr_accessor_with_default attr_internal attr_internal_accessor attr_internal_reader attr_internal_writer delegate mattr_accessor mattr_reader mattr_writer superclass_delegating_accessor superclass_delegating_reader superclass_delegating_writer
3620 syn keyword rubyRailsMethod cattr_accessor cattr_reader cattr_writer class_inheritable_accessor class_inheritable_array class_inheritable_array_writer class_inheritable_hash class_inheritable_hash_writer class_inheritable_option class_inheritable_reader class_inheritable_writer inheritable_attributes read_inheritable_attribute reset_inheritable_attributes write_inheritable_array write_inheritable_attribute write_inheritable_hash
3621 syn keyword rubyRailsInclude require_dependency
3622
3623 syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsOrderSpecial
3624 syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsOrderSpecial
3625 syn match railsOrderSpecial +\c\<\%(DE\|A\)SC\>+ contained
3626 syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsConditionsSpecial
3627 syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsConditionsSpecial
3628 syn match railsConditionsSpecial +?\|:\h\w*+ contained
3629 syn cluster rubyNotTop add=railsOrderSpecial,railsConditionsSpecial
3630
3631 " XHTML highlighting inside %Q<>
3632 unlet! b:current_syntax
3633 let removenorend = !exists("g:html_no_rendering")
3634 let g:html_no_rendering = 1
3635 syn include @htmlTop syntax/xhtml.vim
3636 if removenorend
3637 unlet! g:html_no_rendering
3638 endif
3639 let b:current_syntax = "ruby"
3640 " Restore syn sync, as best we can
3641 if !exists("g:ruby_minlines")
3642 let g:ruby_minlines = 50
3643 endif
3644 syn sync fromstart
3645 exe "syn sync minlines=" . g:ruby_minlines
3646 syn case match
3647 syn region rubyString matchgroup=rubyStringDelimiter start=+%Q\=<+ end=+>+ contains=@htmlTop,@rubyStringSpecial
3648 syn cluster htmlArgCluster add=@rubyStringSpecial
3649 syn cluster htmlPreProc add=@rubyStringSpecial
3650
3651 elseif &syntax =~# '^eruby\>' || &syntax == 'haml'
3652 syn case match
3653 if classes != ''
3654 exe 'syn keyword '.&syntax.'RailsUserClass '.classes.' contained containedin=@'.&syntax.'RailsRegions'
3655 endif
3656 if &syntax == 'haml'
3657 exe 'syn cluster hamlRailsRegions contains=hamlRubyCodeIncluded,hamlRubyCode,hamlRubyHash,@hamlEmbeddedRuby,rubyInterpolation'
3658 else
3659 exe 'syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation'
3660 endif
3661 exe 'syn keyword '.&syntax.'RailsHelperMethod '.s:gsub(s:helpermethods(),'<%(content_for|select)\s+','').' contained containedin=@'.&syntax.'RailsRegions'
3662 exe 'syn match '.&syntax.'RailsHelperMethod "\<select\>\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!" contained containedin=@'.&syntax.'RailsRegions'
3663 exe 'syn match '.&syntax.'RailsHelperMethod "\<\%(content_for?\=\|current_page?\)" contained containedin=@'.&syntax.'RailsRegions'
3664 exe 'syn keyword '.&syntax.'RailsMethod debugger polymorphic_path polymorphic_url contained containedin=@'.&syntax.'RailsRegions'
3665 exe 'syn match '.&syntax.'RailsViewMethod "\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>" contained containedin=@'.&syntax.'RailsRegions'
3666 if buffer.type_name('view-partial')
3667 exe 'syn keyword '.&syntax.'RailsMethod local_assigns contained containedin=@'.&syntax.'RailsRegions'
3668 endif
3669 exe 'syn keyword '.&syntax.'RailsRenderMethod render contained containedin=@'.&syntax.'RailsRegions'
3670 exe 'syn case match'
3671 set isk+=$
3672 exe 'syn keyword javascriptRailsFunction contained '.s:javascript_functions
3673 exe 'syn cluster htmlJavaScript add=javascriptRailsFunction'
3674 elseif &syntax == "yaml"
3675 syn case match
3676 " Modeled after syntax/eruby.vim
3677 unlet! b:current_syntax
3678 let g:main_syntax = 'eruby'
3679 syn include @rubyTop syntax/ruby.vim
3680 unlet g:main_syntax
3681 syn cluster yamlRailsRegions contains=yamlRailsOneLiner,yamlRailsBlock,yamlRailsExpression
3682 syn region yamlRailsOneLiner matchgroup=yamlRailsDelimiter start="^%%\@!" end="$" contains=@rubyRailsTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend oneline
3683 syn region yamlRailsBlock matchgroup=yamlRailsDelimiter start="<%%\@!" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
3684 syn region yamlRailsExpression matchgroup=yamlRailsDelimiter start="<%=" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment
3685 syn region yamlRailsComment matchgroup=yamlRailsDelimiter start="<%#" end="%>" contains=rubyTodo,@Spell containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend
3686 syn match yamlRailsMethod '\.\@<!\<\(h\|html_escape\|u\|url_encode\)\>' contained containedin=@yamlRailsRegions
3687 if classes != ''
3688 exe "syn keyword yamlRailsUserClass ".classes." contained containedin=@yamlRailsRegions"
3689 endif
3690 let b:current_syntax = "yaml"
3691 elseif &syntax == "html"
3692 syn case match
3693 set isk+=$
3694 exe "syn keyword javascriptRailsFunction contained ".s:javascript_functions
3695 syn cluster htmlJavaScript add=javascriptRailsFunction
3696 elseif &syntax == "javascript" || &syntax == "coffee"
3697 " The syntax file included with Vim incorrectly sets syn case ignore.
3698 syn case match
3699 set isk+=$
3700 exe "syn keyword javascriptRailsFunction ".s:javascript_functions
3701
3702 endif
3703 endif
3704 call s:HiDefaults()
3705 endfunction
3706
3707 function! s:HiDefaults()
3708 hi def link rubyRailsAPIMethod rubyRailsMethod
3709 hi def link rubyRailsARAssociationMethod rubyRailsARMethod
3710 hi def link rubyRailsARCallbackMethod rubyRailsARMethod
3711 hi def link rubyRailsARClassMethod rubyRailsARMethod
3712 hi def link rubyRailsARValidationMethod rubyRailsARMethod
3713 hi def link rubyRailsARMethod rubyRailsMethod
3714 hi def link rubyRailsRenderMethod rubyRailsMethod
3715 hi def link rubyRailsHelperMethod rubyRailsMethod
3716 hi def link rubyRailsViewMethod rubyRailsMethod
3717 hi def link rubyRailsMigrationMethod rubyRailsMethod
3718 hi def link rubyRailsControllerMethod rubyRailsMethod
3719 hi def link rubyRailsFilterMethod rubyRailsMethod
3720 hi def link rubyRailsTestControllerMethod rubyRailsTestMethod
3721 hi def link rubyRailsTestMethod rubyRailsMethod
3722 hi def link rubyRailsRakeMethod rubyRailsMethod
3723 hi def link rubyRailsMethod railsMethod
3724 hi def link rubyRailsInclude rubyInclude
3725 hi def link rubyRailsUserClass railsUserClass
3726 hi def link rubyRailsUserMethod railsUserMethod
3727 hi def link erubyRailsHelperMethod erubyRailsMethod
3728 hi def link erubyRailsViewMethod erubyRailsMethod
3729 hi def link erubyRailsRenderMethod erubyRailsMethod
3730 hi def link erubyRailsMethod railsMethod
3731 hi def link erubyRailsUserMethod railsUserMethod
3732 hi def link erubyRailsUserClass railsUserClass
3733 hi def link hamlRailsHelperMethod hamlRailsMethod
3734 hi def link hamlRailsViewMethod hamlRailsMethod
3735 hi def link hamlRailsRenderMethod hamlRailsMethod
3736 hi def link hamlRailsMethod railsMethod
3737 hi def link hamlRailsUserMethod railsUserMethod
3738 hi def link hamlRailsUserClass railsUserClass
3739 hi def link railsUserMethod railsMethod
3740 hi def link yamlRailsDelimiter Delimiter
3741 hi def link yamlRailsMethod railsMethod
3742 hi def link yamlRailsComment Comment
3743 hi def link yamlRailsUserClass railsUserClass
3744 hi def link yamlRailsUserMethod railsUserMethod
3745 hi def link javascriptRailsFunction railsMethod
3746 hi def link railsUserClass railsClass
3747 hi def link railsMethod Function
3748 hi def link railsClass Type
3749 hi def link railsOrderSpecial railsStringSpecial
3750 hi def link railsConditionsSpecial railsStringSpecial
3751 hi def link railsStringSpecial Identifier
3752 endfunction
3753
3754 function! rails#log_syntax()
3755 if has('conceal')
3756 syn match railslogEscape '\e\[[0-9;]*m' conceal
3757 syn match railslogEscapeMN '\e\[[0-9;]*m' conceal nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained
3758 syn match railslogEscapeSQL '\e\[[0-9;]*m' conceal nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained
3759 else
3760 syn match railslogEscape '\e\[[0-9;]*m'
3761 syn match railslogEscapeMN '\e\[[0-9;]*m' nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained
3762 syn match railslogEscapeSQL '\e\[[0-9;]*m' nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained
3763 endif
3764 syn match railslogRender '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\>'
3765 syn match railslogComment '^\s*# .*'
3766 syn match railslogModel '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=\u\%(\w\|:\)* \%(Load\%( Including Associations\| IDs For Limited Eager Loading\)\=\|Columns\|Count\|Create\|Update\|Destroy\|Delete all\)\>' skipwhite nextgroup=railslogModelNum,railslogEscapeMN
3767 syn match railslogModel '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=SQL\>' skipwhite nextgroup=railslogModelNum,railslogEscapeMN
3768 syn region railslogModelNum start='(' end=')' contains=railslogNumber contained skipwhite nextgroup=railslogSQL,railslogEscapeSQL
3769 syn match railslogSQL '\u[^\e]*' contained
3770 " Destroy generates multiline SQL, ugh
3771 syn match railslogSQL '\%(^ \%(\e\[[0-9;]*m\)\=\)\@<=\%(FROM\|WHERE\|ON\|AND\|OR\|ORDER\) .*$'
3772 syn match railslogNumber '\<\d\+\>%'
3773 syn match railslogNumber '[ (]\@<=\<\d\+\.\d\+\>\.\@!'
3774 syn region railslogString start='"' skip='\\"' end='"' oneline contained
3775 syn region railslogHash start='{' end='}' oneline contains=railslogHash,railslogString
3776 syn match railslogIP '\<\d\{1,3\}\%(\.\d\{1,3}\)\{3\}\>'
3777 syn match railslogTimestamp '\<\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\>'
3778 syn match railslogSessionID '\<\x\{32\}\>'
3779 syn match railslogIdentifier '^\s*\%(Session ID\|Parameters\)\ze:'
3780 syn match railslogSuccess '\<2\d\d \u[A-Za-z0-9 ]*\>'
3781 syn match railslogRedirect '\<3\d\d \u[A-Za-z0-9 ]*\>'
3782 syn match railslogError '\<[45]\d\d \u[A-Za-z0-9 ]*\>'
3783 syn match railslogError '^DEPRECATION WARNING\>'
3784 syn keyword railslogHTTP OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT
3785 syn region railslogStackTrace start=":\d\+:in `\w\+'$" end="^\s*$" keepend fold
3786 hi def link railslogEscapeMN railslogEscape
3787 hi def link railslogEscapeSQL railslogEscape
3788 hi def link railslogEscape Ignore
3789 hi def link railslogComment Comment
3790 hi def link railslogRender Keyword
3791 hi def link railslogModel Type
3792 hi def link railslogSQL PreProc
3793 hi def link railslogNumber Number
3794 hi def link railslogString String
3795 hi def link railslogSessionID Constant
3796 hi def link railslogIdentifier Identifier
3797 hi def link railslogRedirect railslogSuccess
3798 hi def link railslogSuccess Special
3799 hi def link railslogError Error
3800 hi def link railslogHTTP Special
3801 endfunction
3802
3803 " }}}1
3804 " Mappings {{{1
3805
3806 function! s:BufMappings()
3807 nnoremap <buffer> <silent> <Plug>RailsFind :<C-U>call <SID>Find(v:count1,'E')<CR>
3808 nnoremap <buffer> <silent> <Plug>RailsSplitFind :<C-U>call <SID>Find(v:count1,'S')<CR>
3809 nnoremap <buffer> <silent> <Plug>RailsVSplitFind :<C-U>call <SID>Find(v:count1,'V')<CR>
3810 nnoremap <buffer> <silent> <Plug>RailsTabFind :<C-U>call <SID>Find(v:count1,'T')<CR>
3811 if g:rails_mappings
3812 if !hasmapto("<Plug>RailsFind")
3813 nmap <buffer> gf <Plug>RailsFind
3814 endif
3815 if !hasmapto("<Plug>RailsSplitFind")
3816 nmap <buffer> <C-W>f <Plug>RailsSplitFind
3817 endif
3818 if !hasmapto("<Plug>RailsTabFind")
3819 nmap <buffer> <C-W>gf <Plug>RailsTabFind
3820 endif
3821 if exists("$CREAM")
3822 imap <buffer> <C-CR> <C-O><Plug>RailsFind
3823 imap <buffer> <M-[> <C-O><Plug>RailsAlternate
3824 imap <buffer> <M-]> <C-O><Plug>RailsRelated
3825 endif
3826 endif
3827 " SelectBuf you're a dirty hack
3828 let v:errmsg = ""
3829 endfunction
3830
3831 " }}}1
3832 " Database {{{1
3833
3834 function! s:extractdbvar(str,arg)
3835 return matchstr("\n".a:str."\n",'\n'.a:arg.'=\zs.\{-\}\ze\n')
3836 endfunction
3837
3838 function! s:app_dbext_settings(environment) dict
3839 if self.cache.needs('dbext_settings')
3840 call self.cache.set('dbext_settings',{})
3841 endif
3842 let cache = self.cache.get('dbext_settings')
3843 if !has_key(cache,a:environment)
3844 let dict = {}
3845 if self.has_file("config/database.yml")
3846 let cmdb = 'require %{yaml}; File.open(%q{'.self.path().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{'
3847 let cmde = '}]; i=0; e=y[e] while e.respond_to?(:to_str) && (i+=1)<16; e.each{|k,v|puts k.to_s+%{=}+v.to_s}}'
3848 let out = self.lightweight_ruby_eval(cmdb.a:environment.cmde)
3849 let adapter = s:extractdbvar(out,'adapter')
3850 let adapter = get({'mysql2': 'mysql', 'postgresql': 'pgsql', 'sqlite3': 'sqlite', 'sqlserver': 'sqlsrv', 'sybase': 'asa', 'oracle': 'ora'},adapter,adapter)
3851 let dict['type'] = toupper(adapter)
3852 let dict['user'] = s:extractdbvar(out,'username')
3853 let dict['passwd'] = s:extractdbvar(out,'password')
3854 if dict['passwd'] == '' && adapter == 'mysql'
3855 " Hack to override password from .my.cnf
3856 let dict['extra'] = ' --password='
3857 else
3858 let dict['extra'] = ''
3859 endif
3860 let dict['dbname'] = s:extractdbvar(out,'database')
3861 if dict['dbname'] == ''
3862 let dict['dbname'] = s:extractdbvar(out,'dbfile')
3863 endif
3864 if dict['dbname'] != '' && dict['dbname'] !~ '^:' && adapter =~? '^sqlite'
3865 let dict['dbname'] = self.path(dict['dbname'])
3866 endif
3867 let dict['profile'] = ''
3868 if adapter == 'ora'
3869 let dict['srvname'] = s:extractdbvar(out,'database')
3870 else
3871 let dict['srvname'] = s:extractdbvar(out,'host')
3872 endif
3873 let dict['host'] = s:extractdbvar(out,'host')
3874 let dict['port'] = s:extractdbvar(out,'port')
3875 let dict['dsnname'] = s:extractdbvar(out,'dsn')
3876 if dict['host'] =~? '^\cDBI:'
3877 if dict['host'] =~? '\c\<Trusted[_ ]Connection\s*=\s*yes\>'
3878 let dict['integratedlogin'] = 1
3879 endif
3880 let dict['host'] = matchstr(dict['host'],'\c\<\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
3881 endif
3882 call filter(dict,'v:val != ""')
3883 endif
3884 let cache[a:environment] = dict
3885 endif
3886 return cache[a:environment]
3887 endfunction
3888
3889 function! s:BufDatabase(...)
3890 if exists("s:lock_database") || !exists('g:loaded_dbext') || !exists('b:rails_root')
3891 return
3892 endif
3893 let self = rails#app()
3894 let s:lock_database = 1
3895 if (a:0 && a:1 > 1)
3896 call self.cache.clear('dbext_settings')
3897 endif
3898 if (a:0 > 1 && a:2 != '')
3899 let env = a:2
3900 else
3901 let env = s:environment()
3902 endif
3903 if (!self.cache.has('dbext_settings') || !has_key(self.cache.get('dbext_settings'),env)) && (a:0 ? a:1 : 0) <= 0
3904 unlet! s:lock_database
3905 return
3906 endif
3907 let dict = self.dbext_settings(env)
3908 for key in ['type', 'profile', 'bin', 'user', 'passwd', 'dbname', 'srvname', 'host', 'port', 'dsnname', 'extra', 'integratedlogin']
3909 let b:dbext_{key} = get(dict,key,'')
3910 endfor
3911 if b:dbext_type == 'PGSQL'
3912 let $PGPASSWORD = b:dbext_passwd
3913 elseif exists('$PGPASSWORD')
3914 let $PGPASSWORD = ''
3915 endif
3916 unlet! s:lock_database
3917 endfunction
3918
3919 call s:add_methods('app', ['dbext_settings'])
3920
3921 " }}}1
3922 " Abbreviations {{{1
3923
3924 function! s:selectiveexpand(pat,good,default,...)
3925 if a:0 > 0
3926 let nd = a:1
3927 else
3928 let nd = ""
3929 endif
3930 let c = nr2char(getchar(0))
3931 let good = a:good
3932 if c == "" " ^]
3933 return s:sub(good.(a:0 ? " ".a:1 : ''),'\s+$','')
3934 elseif c == "\t"
3935 return good.(a:0 ? " ".a:1 : '')
3936 elseif c =~ a:pat
3937 return good.c.(a:0 ? a:1 : '')
3938 else
3939 return a:default.c
3940 endif
3941 endfunction
3942
3943 function! s:TheCWord()
3944 let l = s:linepeak()
3945 if l =~ '\<\%(find\|first\|last\|all\|paginate\)\>'
3946 return s:selectiveexpand('..',':conditions => ',':c')
3947 elseif l =~ '\<render\s*(\=\s*:partial\s*=>\s*'
3948 return s:selectiveexpand('..',':collection => ',':c')
3949 elseif l =~ '\<\%(url_for\|link_to\|form_tag\)\>' || l =~ ':url\s*=>\s*{\s*'
3950 return s:selectiveexpand('..',':controller => ',':c')
3951 else
3952 return s:selectiveexpand('..',':conditions => ',':c')
3953 endif
3954 endfunction
3955
3956 function! s:AddSelectiveExpand(abbr,pat,expn,...)
3957 let expn = s:gsub(s:gsub(a:expn ,'[\"|]','\\&'),'\<','\\<Lt>')
3958 let expn2 = s:gsub(s:gsub(a:0 ? a:1 : '','[\"|]','\\&'),'\<','\\<Lt>')
3959 if a:0
3960 exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).",\"".expn2."\")<CR>"
3961 else
3962 exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).")<CR>"
3963 endif
3964 endfunction
3965
3966 function! s:AddTabExpand(abbr,expn)
3967 call s:AddSelectiveExpand(a:abbr,'..',a:expn)
3968 endfunction
3969
3970 function! s:AddBracketExpand(abbr,expn)
3971 call s:AddSelectiveExpand(a:abbr,'[[.]',a:expn)
3972 endfunction
3973
3974 function! s:AddColonExpand(abbr,expn)
3975 call s:AddSelectiveExpand(a:abbr,'[:.]',a:expn)
3976 endfunction
3977
3978 function! s:AddParenExpand(abbr,expn,...)
3979 if a:0
3980 call s:AddSelectiveExpand(a:abbr,'(',a:expn,a:1)
3981 else
3982 call s:AddSelectiveExpand(a:abbr,'(',a:expn,'')
3983 endif
3984 endfunction
3985
3986 function! s:BufAbbreviations()
3987 command! -buffer -bar -nargs=* -bang Rabbrev :call s:Abbrev(<bang>0,<f-args>)
3988 " Some of these were cherry picked from the TextMate snippets
3989 if g:rails_abbreviations
3990 let buffer = rails#buffer()
3991 " Limit to the right filetypes. But error on the liberal side
3992 if buffer.type_name('controller','view','helper','test-functional','test-integration')
3993 Rabbrev pa[ params
3994 Rabbrev rq[ request
3995 Rabbrev rs[ response
3996 Rabbrev se[ session
3997 Rabbrev hd[ headers
3998 Rabbrev coo[ cookies
3999 Rabbrev fl[ flash
4000 Rabbrev rr( render
4001 Rabbrev ra( render :action\ =>\
4002 Rabbrev rc( render :controller\ =>\
4003 Rabbrev rf( render :file\ =>\
4004 Rabbrev ri( render :inline\ =>\
4005 Rabbrev rj( render :json\ =>\
4006 Rabbrev rl( render :layout\ =>\
4007 Rabbrev rp( render :partial\ =>\
4008 Rabbrev rt( render :text\ =>\
4009 Rabbrev rx( render :xml\ =>\
4010 endif
4011 if buffer.type_name('view','helper')
4012 Rabbrev dotiw distance_of_time_in_words
4013 Rabbrev taiw time_ago_in_words
4014 endif
4015 if buffer.type_name('controller')
4016 Rabbrev re( redirect_to
4017 Rabbrev rea( redirect_to :action\ =>\
4018 Rabbrev rec( redirect_to :controller\ =>\
4019 Rabbrev rst( respond_to
4020 endif
4021 if buffer.type_name() ==# 'model' || buffer.type_name('model-arb')
4022 Rabbrev bt( belongs_to
4023 Rabbrev ho( has_one
4024 Rabbrev hm( has_many
4025 Rabbrev habtm( has_and_belongs_to_many
4026 Rabbrev co( composed_of
4027 Rabbrev va( validates_associated
4028 Rabbrev vb( validates_acceptance_of
4029 Rabbrev vc( validates_confirmation_of
4030 Rabbrev ve( validates_exclusion_of
4031 Rabbrev vf( validates_format_of
4032 Rabbrev vi( validates_inclusion_of
4033 Rabbrev vl( validates_length_of
4034 Rabbrev vn( validates_numericality_of
4035 Rabbrev vp( validates_presence_of
4036 Rabbrev vu( validates_uniqueness_of
4037 endif
4038 if buffer.type_name('db-migration','db-schema')
4039 Rabbrev mac( add_column
4040 Rabbrev mrnc( rename_column
4041 Rabbrev mrc( remove_column
4042 Rabbrev mct( create_table
4043 Rabbrev mcht( change_table
4044 Rabbrev mrnt( rename_table
4045 Rabbrev mdt( drop_table
4046 Rabbrev mcc( t.column
4047 endif
4048 if buffer.type_name('test')
4049 Rabbrev ase( assert_equal
4050 Rabbrev asko( assert_kind_of
4051 Rabbrev asnn( assert_not_nil
4052 Rabbrev asr( assert_raise
4053 Rabbrev asre( assert_response
4054 Rabbrev art( assert_redirected_to
4055 endif
4056 Rabbrev :a :action\ =>\
4057 " hax
4058 Rabbrev :c :co________\ =>\
4059 inoreabbrev <buffer> <silent> :c <C-R>=<SID>TheCWord()<CR>
4060 Rabbrev :i :id\ =>\
4061 Rabbrev :o :object\ =>\
4062 Rabbrev :p :partial\ =>\
4063 Rabbrev logd( logger.debug
4064 Rabbrev logi( logger.info
4065 Rabbrev logw( logger.warn
4066 Rabbrev loge( logger.error
4067 Rabbrev logf( logger.fatal
4068 Rabbrev fi( find
4069 Rabbrev AR:: ActiveRecord
4070 Rabbrev AV:: ActionView
4071 Rabbrev AC:: ActionController
4072 Rabbrev AD:: ActionDispatch
4073 Rabbrev AS:: ActiveSupport
4074 Rabbrev AM:: ActionMailer
4075 Rabbrev AO:: ActiveModel
4076 Rabbrev AE:: ActiveResource
4077 endif
4078 endfunction
4079
4080 function! s:Abbrev(bang,...) abort
4081 if !exists("b:rails_abbreviations")
4082 let b:rails_abbreviations = {}
4083 endif
4084 if a:0 > 3 || (a:bang && (a:0 != 1))
4085 return s:error("Rabbrev: invalid arguments")
4086 endif
4087 if a:0 == 0
4088 for key in sort(keys(b:rails_abbreviations))
4089 echo key . join(b:rails_abbreviations[key],"\t")
4090 endfor
4091 return
4092 endif
4093 let lhs = a:1
4094 let root = s:sub(lhs,'%(::|\(|\[)$','')
4095 if a:bang
4096 if has_key(b:rails_abbreviations,root)
4097 call remove(b:rails_abbreviations,root)
4098 endif
4099 exe "iunabbrev <buffer> ".root
4100 return
4101 endif
4102 if a:0 > 3 || a:0 < 2
4103 return s:error("Rabbrev: invalid arguments")
4104 endif
4105 let rhs = a:2
4106 if has_key(b:rails_abbreviations,root)
4107 call remove(b:rails_abbreviations,root)
4108 endif
4109 if lhs =~ '($'
4110 let b:rails_abbreviations[root] = ["(", rhs . (a:0 > 2 ? "\t".a:3 : "")]
4111 if a:0 > 2
4112 call s:AddParenExpand(root,rhs,a:3)
4113 else
4114 call s:AddParenExpand(root,rhs)
4115 endif
4116 return
4117 endif
4118 if a:0 > 2
4119 return s:error("Rabbrev: invalid arguments")
4120 endif
4121 if lhs =~ ':$'
4122 call s:AddColonExpand(root,rhs)
4123 elseif lhs =~ '\[$'
4124 call s:AddBracketExpand(root,rhs)
4125 elseif lhs =~ '\w$'
4126 call s:AddTabExpand(lhs,rhs)
4127 else
4128 return s:error("Rabbrev: unimplemented")
4129 endif
4130 let b:rails_abbreviations[root] = [matchstr(lhs,'\W*$'),rhs]
4131 endfunction
4132
4133 " }}}1
4134 " Settings {{{1
4135
4136 function! s:Set(bang,...)
4137 let c = 1
4138 let defscope = ''
4139 for arg in a:000
4140 if arg =~? '^<[abgl]\=>$'
4141 let defscope = (matchstr(arg,'<\zs.*\ze>'))
4142 elseif arg !~ '='
4143 if defscope != '' && arg !~ '^\w:'
4144 let arg = defscope.':'.opt
4145 endif
4146 let val = s:getopt(arg)
4147 if val == '' && !has_key(s:opts(),arg)
4148 call s:error("No such rails.vim option: ".arg)
4149 else
4150 echo arg."=".val
4151 endif
4152 else
4153 let opt = matchstr(arg,'[^=]*')
4154 let val = s:sub(arg,'^[^=]*\=','')
4155 if defscope != '' && opt !~ '^\w:'
4156 let opt = defscope.':'.opt
4157 endif
4158 call s:setopt(opt,val)
4159 endif
4160 endfor
4161 endfunction
4162
4163 function! s:getopt(opt,...)
4164 let app = rails#app()
4165 let opt = a:opt
4166 if a:0
4167 let scope = a:1
4168 elseif opt =~ '^[abgl]:'
4169 let scope = tolower(matchstr(opt,'^\w'))
4170 let opt = s:sub(opt,'^\w:','')
4171 else
4172 let scope = 'abgl'
4173 endif
4174 let lnum = a:0 > 1 ? a:2 : line('.')
4175 if scope =~ 'l' && &filetype != 'ruby'
4176 let scope = s:sub(scope,'l','b')
4177 endif
4178 if scope =~ 'l'
4179 call s:LocalModelines(lnum)
4180 endif
4181 let var = s:sname().'_'.opt
4182 let lastmethod = s:lastmethod(lnum)
4183 if lastmethod == '' | let lastmethod = ' ' | endif
4184 " Get buffer option
4185 if scope =~ 'l' && exists('b:_'.var) && has_key(b:_{var},lastmethod)
4186 return b:_{var}[lastmethod]
4187 elseif exists('b:'.var) && (scope =~ 'b' || (scope =~ 'l' && lastmethod == ' '))
4188 return b:{var}
4189 elseif scope =~ 'a' && has_key(app,'options') && has_key(app.options,opt)
4190 return app.options[opt]
4191 elseif scope =~ 'g' && exists("g:".s:sname()."_".opt)
4192 return g:{var}
4193 else
4194 return ""
4195 endif
4196 endfunction
4197
4198 function! s:setopt(opt,val)
4199 let app = rails#app()
4200 if a:opt =~? '[abgl]:'
4201 let scope = matchstr(a:opt,'^\w')
4202 let opt = s:sub(a:opt,'^\w:','')
4203 else
4204 let scope = ''
4205 let opt = a:opt
4206 endif
4207 let defscope = get(s:opts(),opt,'a')
4208 if scope == ''
4209 let scope = defscope
4210 endif
4211 if &filetype != 'ruby' && (scope ==# 'B' || scope ==# 'l')
4212 let scope = 'b'
4213 endif
4214 let var = s:sname().'_'.opt
4215 if opt =~ '\W'
4216 return s:error("Invalid option ".a:opt)
4217 elseif scope ==# 'B' && defscope == 'l'
4218 if !exists('b:_'.var) | let b:_{var} = {} | endif
4219 let b:_{var}[' '] = a:val
4220 elseif scope =~? 'b'
4221 let b:{var} = a:val
4222 elseif scope =~? 'a'
4223 if !has_key(app,'options') | let app.options = {} | endif
4224 let app.options[opt] = a:val
4225 elseif scope =~? 'g'
4226 let g:{var} = a:val
4227 elseif scope =~? 'l'
4228 if !exists('b:_'.var) | let b:_{var} = {} | endif
4229 let lastmethod = s:lastmethod(lnum)
4230 let b:_{var}[lastmethod == '' ? ' ' : lastmethod] = a:val
4231 else
4232 return s:error("Invalid scope for ".a:opt)
4233 endif
4234 endfunction
4235
4236 function! s:opts()
4237 return {'alternate': 'b', 'controller': 'b', 'gnu_screen': 'a', 'model': 'b', 'preview': 'l', 'task': 'b', 'related': 'l', 'root_url': 'a'}
4238 endfunction
4239
4240 function! s:Complete_set(A,L,P)
4241 if a:A =~ '='
4242 let opt = matchstr(a:A,'[^=]*')
4243 return [opt."=".s:getopt(opt)]
4244 else
4245 let extra = matchstr(a:A,'^[abgl]:')
4246 return filter(sort(map(keys(s:opts()),'extra.v:val')),'s:startswith(v:val,a:A)')
4247 endif
4248 return []
4249 endfunction
4250
4251 function! s:BufModelines()
4252 if !g:rails_modelines
4253 return
4254 endif
4255 let lines = getline("$")."\n".getline(line("$")-1)."\n".getline(1)."\n".getline(2)."\n".getline(3)."\n"
4256 let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
4257 let cnt = 1
4258 let mat = matchstr(lines,'\C\<Rset'.pat)
4259 let matend = matchend(lines,'\C\<Rset'.pat)
4260 while mat != "" && cnt < 10
4261 let mat = s:sub(mat,'\s+$','')
4262 let mat = s:gsub(mat,'\|','\\|')
4263 if mat != ''
4264 silent! exe "Rset <B> ".mat
4265 endif
4266 let mat = matchstr(lines,'\C\<Rset'.pat,matend)
4267 let matend = matchend(lines,'\C\<Rset'.pat,matend)
4268 let cnt += 1
4269 endwhile
4270 endfunction
4271
4272 function! s:LocalModelines(lnum)
4273 if !g:rails_modelines
4274 return
4275 endif
4276 let lbeg = s:lastmethodline(a:lnum)
4277 let lend = s:endof(lbeg)
4278 if lbeg == 0 || lend == 0
4279 return
4280 endif
4281 let lines = "\n"
4282 let lnum = lbeg
4283 while lnum < lend && lnum < lbeg + 5
4284 let lines .= getline(lnum) . "\n"
4285 let lnum += 1
4286 endwhile
4287 let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
4288 let cnt = 1
4289 let mat = matchstr(lines,'\C\<rset'.pat)
4290 let matend = matchend(lines,'\C\<rset'.pat)
4291 while mat != "" && cnt < 10
4292 let mat = s:sub(mat,'\s+$','')
4293 let mat = s:gsub(mat,'\|','\\|')
4294 if mat != ''
4295 silent! exe "Rset <l> ".mat
4296 endif
4297 let mat = matchstr(lines,'\C\<rset'.pat,matend)
4298 let matend = matchend(lines,'\C\<rset'.pat,matend)
4299 let cnt += 1
4300 endwhile
4301 endfunction
4302
4303 " }}}1
4304 " Detection {{{1
4305
4306 function! s:app_source_callback(file) dict
4307 if self.cache.needs('existence')
4308 call self.cache.set('existence',{})
4309 endif
4310 let cache = self.cache.get('existence')
4311 if !has_key(cache,a:file)
4312 let cache[a:file] = self.has_file(a:file)
4313 endif
4314 if cache[a:file]
4315 sandbox source `=self.path(a:file)`
4316 endif
4317 endfunction
4318
4319 call s:add_methods('app',['source_callback'])
4320
4321 function! RailsBufInit(path)
4322 let firsttime = !(exists("b:rails_root") && b:rails_root == a:path)
4323 let b:rails_root = a:path
4324 if !has_key(s:apps,a:path)
4325 let s:apps[a:path] = deepcopy(s:app_prototype)
4326 let s:apps[a:path].root = a:path
4327 endif
4328 let app = s:apps[a:path]
4329 let buffer = rails#buffer()
4330 " Apparently rails#buffer().calculate_file_type() can be slow if the
4331 " underlying file system is slow (even though it doesn't really do anything
4332 " IO related). This caching is a temporary hack; if it doesn't cause
4333 " problems it should probably be refactored.
4334 let b:rails_cached_file_type = buffer.calculate_file_type()
4335 if g:rails_history_size > 0
4336 if !exists("g:RAILS_HISTORY")
4337 let g:RAILS_HISTORY = ""
4338 endif
4339 let path = a:path
4340 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
4341 if has("win32")
4342 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
4343 endif
4344 let path = fnamemodify(path,':p:~:h')
4345 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
4346 if has("win32")
4347 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
4348 endif
4349 let g:RAILS_HISTORY = path."\n".g:RAILS_HISTORY
4350 let g:RAILS_HISTORY = s:sub(g:RAILS_HISTORY,'%(.{-}\n){,'.g:rails_history_size.'}\zs.*','')
4351 endif
4352 call app.source_callback("config/syntax.vim")
4353 if expand('%:t') =~ '\.yml\.example$'
4354 setlocal filetype=yaml
4355 elseif expand('%:e') =~ '^\%(rjs\|rxml\|builder\)$'
4356 setlocal filetype=ruby
4357 elseif firsttime
4358 " Activate custom syntax
4359 let &syntax = &syntax
4360 endif
4361 if expand('%:e') == 'log'
4362 nnoremap <buffer> <silent> R :checktime<CR>
4363 nnoremap <buffer> <silent> G :checktime<Bar>$<CR>
4364 nnoremap <buffer> <silent> q :bwipe<CR>
4365 setlocal modifiable filetype=railslog noswapfile autoread foldmethod=syntax
4366 if exists('+concealcursor')
4367 setlocal concealcursor=nc conceallevel=2
4368 else
4369 silent %s/\%(\e\[[0-9;]*m\|\r$\)//ge
4370 endif
4371 setlocal readonly nomodifiable
4372 $
4373 endif
4374 call s:BufSettings()
4375 call s:BufCommands()
4376 call s:BufAbbreviations()
4377 " snippetsEmu.vim
4378 if exists('g:loaded_snippet')
4379 silent! runtime! ftplugin/rails_snippets.vim
4380 " filetype snippets need to come last for higher priority
4381 exe "silent! runtime! ftplugin/".&filetype."_snippets.vim"
4382 endif
4383 let t = rails#buffer().type_name()
4384 let t = "-".t
4385 let f = '/'.RailsFilePath()
4386 if f =~ '[ !#$%\,]'
4387 let f = ''
4388 endif
4389 runtime! macros/rails.vim
4390 silent doautocmd User Rails
4391 if t != '-'
4392 exe "silent doautocmd User Rails".s:gsub(t,'-','.')
4393 endif
4394 if f != ''
4395 exe "silent doautocmd User Rails".f
4396 endif
4397 call app.source_callback("config/rails.vim")
4398 call s:BufModelines()
4399 call s:BufMappings()
4400 return b:rails_root
4401 endfunction
4402
4403 function! s:SetBasePath()
4404 let self = rails#buffer()
4405 if self.app().path() =~ '://'
4406 return
4407 endif
4408 let transformed_path = s:pathsplit(s:pathjoin([self.app().path()]))[0]
4409 let add_dot = self.getvar('&path') =~# '^\.\%(,\|$\)'
4410 let old_path = s:pathsplit(s:sub(self.getvar('&path'),'^\.%(,|$)',''))
4411 call filter(old_path,'!s:startswith(v:val,transformed_path)')
4412
4413 let path = ['app', 'app/models', 'app/controllers', 'app/helpers', 'config', 'lib', 'app/views']
4414 if self.controller_name() != ''
4415 let path += ['app/views/'.self.controller_name(), 'public']
4416 endif
4417 if self.app().has('test')
4418 let path += ['test', 'test/unit', 'test/functional', 'test/integration']
4419 endif
4420 if self.app().has('spec')
4421 let path += ['spec', 'spec/models', 'spec/controllers', 'spec/helpers', 'spec/views', 'spec/lib', 'spec/requests', 'spec/integration']
4422 endif
4423 let path += ['app/*', 'vendor', 'vendor/plugins/*/lib', 'vendor/plugins/*/test', 'vendor/rails/*/lib', 'vendor/rails/*/test']
4424 call map(path,'self.app().path(v:val)')
4425 call self.setvar('&path',(add_dot ? '.,' : '').s:pathjoin([self.app().path()],path,old_path))
4426 endfunction
4427
4428 function! s:BufSettings()
4429 if !exists('b:rails_root')
4430 return ''
4431 endif
4432 let self = rails#buffer()
4433 call s:SetBasePath()
4434 let rp = s:gsub(self.app().path(),'[ ,]','\\&')
4435 if stridx(&tags,rp.'/tmp/tags') == -1
4436 let &l:tags = rp . '/tmp/tags,' . &tags . ',' . rp . '/tags'
4437 endif
4438 if has("gui_win32") || has("gui_running")
4439 let code = '*.rb;*.rake;Rakefile'
4440 let templates = '*.'.join(s:view_types,';*.')
4441 let fixtures = '*.yml;*.csv'
4442 let statics = '*.html;*.css;*.js;*.xml;*.xsd;*.sql;.htaccess;README;README_FOR_APP'
4443 let b:browsefilter = ""
4444 \."All Rails Files\t".code.';'.templates.';'.fixtures.';'.statics."\n"
4445 \."Source Code (*.rb, *.rake)\t".code."\n"
4446 \."Templates (*.rhtml, *.rxml, *.rjs)\t".templates."\n"
4447 \."Fixtures (*.yml, *.csv)\t".fixtures."\n"
4448 \."Static Files (*.html, *.css, *.js)\t".statics."\n"
4449 \."All Files (*.*)\t*.*\n"
4450 endif
4451 call self.setvar('&includeexpr','RailsIncludeexpr()')
4452 call self.setvar('&suffixesadd', ".rb,.".join(s:view_types,',.'))
4453 let ft = self.getvar('&filetype')
4454 if ft =~ '^\%(e\=ruby\|[yh]aml\|coffee\|css\|s[ac]ss\|lesscss\)$'
4455 call self.setvar('&shiftwidth',2)
4456 call self.setvar('&softtabstop',2)
4457 call self.setvar('&expandtab',1)
4458 if exists('+completefunc') && self.getvar('&completefunc') == ''
4459 call self.setvar('&completefunc','syntaxcomplete#Complete')
4460 endif
4461 endif
4462 if ft == 'ruby'
4463 call self.setvar('&define',self.define_pattern())
4464 " This really belongs in after/ftplugin/ruby.vim but we'll be nice
4465 if exists('g:loaded_surround') && self.getvar('surround_101') == ''
4466 call self.setvar('surround_5', "\r\nend")
4467 call self.setvar('surround_69', "\1expr: \1\rend")
4468 call self.setvar('surround_101', "\r\nend")
4469 endif
4470 elseif ft == 'yaml' || fnamemodify(self.name(),':e') == 'yml'
4471 call self.setvar('&define',self.define_pattern())
4472 elseif ft =~# '^eruby\>'
4473 if exists("g:loaded_allml")
4474 call self.setvar('allml_stylesheet_link_tag', "<%= stylesheet_link_tag '\r' %>")
4475 call self.setvar('allml_javascript_include_tag', "<%= javascript_include_tag '\r' %>")
4476 call self.setvar('allml_doctype_index', 10)
4477 endif
4478 if exists("g:loaded_ragtag")
4479 call self.setvar('ragtag_stylesheet_link_tag', "<%= stylesheet_link_tag '\r' %>")
4480 call self.setvar('ragtag_javascript_include_tag', "<%= javascript_include_tag '\r' %>")
4481 call self.setvar('ragtag_doctype_index', 10)
4482 endif
4483 elseif ft == 'haml'
4484 if exists("g:loaded_allml")
4485 call self.setvar('allml_stylesheet_link_tag', "= stylesheet_link_tag '\r'")
4486 call self.setvar('allml_javascript_include_tag', "= javascript_include_tag '\r'")
4487 call self.setvar('allml_doctype_index', 10)
4488 endif
4489 if exists("g:loaded_ragtag")
4490 call self.setvar('ragtag_stylesheet_link_tag', "= stylesheet_link_tag '\r'")
4491 call self.setvar('ragtag_javascript_include_tag', "= javascript_include_tag '\r'")
4492 call self.setvar('ragtag_doctype_index', 10)
4493 endif
4494 endif
4495 if ft =~# '^eruby\>' || ft ==# 'yaml'
4496 " surround.vim
4497 if exists("g:loaded_surround")
4498 " The idea behind the || part here is that one can normally define the
4499 " surrounding to omit the hyphen (since standard ERuby does not use it)
4500 " but have it added in Rails ERuby files. Unfortunately, this makes it
4501 " difficult if you really don't want a hyphen in Rails ERuby files. If
4502 " this is your desire, you will need to accomplish it via a rails.vim
4503 " autocommand.
4504 if self.getvar('surround_45') == '' || self.getvar('surround_45') == "<% \r %>" " -
4505 call self.setvar('surround_45', "<% \r -%>")
4506 endif
4507 if self.getvar('surround_61') == '' " =
4508 call self.setvar('surround_61', "<%= \r %>")
4509 endif
4510 if self.getvar("surround_35") == '' " #
4511 call self.setvar('surround_35', "<%# \r %>")
4512 endif
4513 if self.getvar('surround_101') == '' || self.getvar('surround_101')== "<% \r %>\n<% end %>" "e
4514 call self.setvar('surround_5', "<% \r -%>\n<% end -%>")
4515 call self.setvar('surround_69', "<% \1expr: \1 -%>\r<% end -%>")
4516 call self.setvar('surround_101', "<% \r -%>\n<% end -%>")
4517 endif
4518 endif
4519 endif
4520 endfunction
4521
4522 " }}}1
4523 " Autocommands {{{1
4524
4525 augroup railsPluginAuto
4526 autocmd!
4527 autocmd User BufEnterRails call s:RefreshBuffer()
4528 autocmd User BufEnterRails call s:resetomnicomplete()
4529 autocmd User BufEnterRails call s:BufDatabase(-1)
4530 autocmd User dbextPreConnection call s:BufDatabase(1)
4531 autocmd BufWritePost */config/database.yml call rails#cache_clear("dbext_settings")
4532 autocmd BufWritePost */test/test_helper.rb call rails#cache_clear("user_assertions")
4533 autocmd BufWritePost */config/routes.rb call rails#cache_clear("named_routes")
4534 autocmd BufWritePost */config/environment.rb call rails#cache_clear("default_locale")
4535 autocmd BufWritePost */config/environments/*.rb call rails#cache_clear("environments")
4536 autocmd BufWritePost */tasks/**.rake call rails#cache_clear("rake_tasks")
4537 autocmd BufWritePost */generators/** call rails#cache_clear("generators")
4538 autocmd FileType * if exists("b:rails_root") | call s:BufSettings() | endif
4539 autocmd Syntax ruby,eruby,yaml,haml,javascript,coffee,railslog if exists("b:rails_root") | call s:BufSyntax() | endif
4540 autocmd QuickFixCmdPre *make* call s:push_chdir()
4541 autocmd QuickFixCmdPost *make* call s:pop_command()
4542 augroup END
4543
4544 " }}}1
4545 " Initialization {{{1
4546
4547 map <SID>xx <SID>xx
4548 let s:sid = s:sub(maparg("<SID>xx"),'xx$','')
4549 unmap <SID>xx
4550 let s:file = expand('<sfile>:p')
4551
4552 if !exists('s:apps')
4553 let s:apps = {}
4554 endif
4555
4556 " }}}1
4557
4558 let &cpo = s:cpo_save
4559
4560 " vim:set sw=2 sts=2: