2 " Author: Tim Pope <http://tpo.pe/>
4 " Install this file as autoload/rails.vim.
6 if exists('g:autoloaded_rails') || &cp
9 let g:autoloaded_rails = '4.4'
14 " Utility Functions {{{1
16 let s:app_prototype = {}
17 let s:file_prototype = {}
18 let s:buffer_prototype = {}
19 let s:readable_prototype = {}
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)
27 function! s:function(name)
28 return function(substitute(a:name,'^s:',matchstr(expand('<sfile>'), '<SNR>\d\+_'),''))
31 function! s:sub(str,pat,rep)
32 return substitute(a:str,'\v\C'.a:pat,a:rep,'')
35 function! s:gsub(str,pat,rep)
36 return substitute(a:str,'\v\C'.a:pat,a:rep,'g')
39 function! s:startswith(string,prefix)
40 return strpart(a:string, 0, strlen(a:prefix)) ==# a:prefix
43 function! s:compact(ary)
44 return s:sub(s:sub(s:gsub(a:ary,'\n\n+','\n'),'\n$',''),'^\n','')
47 function! s:uniq(list)
51 if has_key(seen,a:list[i])
52 call remove(a:list, i)
54 let seen[a:list[i]] = 1
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")
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")
75 return s:gsub(a:p,'[ !%#]','\\&')
79 return s:gsub(a:p,'[!%#]','\\&')
82 function! s:rquote(str)
83 " Imperfect but adequate for Ruby arguments
84 if a:str =~ '^[A-Za-z0-9_/.:-]\+$'
86 elseif &shell =~? 'cmd'
87 return '"'.s:gsub(s:gsub(a:str,'\','\\'),'"','\\"').'"'
89 return "'".s:gsub(s:gsub(a:str,'\','\\'),"'","'\\\\''")."'"
94 return fnamemodify(s:file,':t:r')
97 function! s:pop_command()
98 if exists("s:command_stack") && len(s:command_stack) > 0
99 exe remove(s:command_stack,-1)
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())
110 call add(s:command_stack,"")
114 function! s:app_path(...) dict
115 return join([self.root]+a:000,'/')
118 function! s:app_has_file(file) dict
119 return filereadable(self.path(a:file))
122 function! s:app_find_file(name, ...) dict abort
123 let trim = strlen(self.path())+1
125 let path = s:pathjoin(map(s:pathsplit(a:1),'self.path(v:val)'))
127 let path = s:pathjoin([self.path()])
129 let suffixesadd = s:pathjoin(get(a:000,1,&suffixesadd))
130 let default = get(a:000,2,'')
131 let oldsuffixesadd = &l:suffixesadd
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)
138 call filter(all,'!isdirectory(v:val)')
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)
146 let found = findfile(a:name,path)
147 while v:version < 702 && found != "" && isdirectory(found)
149 let found = findfile(a:name,path,i)
152 return found == "" ? default : s:gsub(strpart(fnamemodify(found,':p'),trim),'\\','/')
154 let &l:suffixesadd = oldsuffixesadd
158 call s:add_methods('app',['path','has_file','find_file'])
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")')
167 " Convert a list to a path. From pathogen.vim
168 function! s:pathjoin(...) abort
172 if type(a:000[i]) == type([])
176 let escaped = substitute(list[j],'[\\, ]','\\&','g')
177 if exists("+shellslash") && !&shellslash
178 let escaped = substitute(escaped,'^\(\w:\\\)\\','\1','')
180 let path .= ',' . escaped
184 let path .= "," . a:000[i]
188 return substitute(path,'^,','','')
191 function! s:readable_end_of(lnum) dict abort
195 if self.name() =~# '\.yml$'
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)
205 while endl <= self.line_count()
207 if self.getline(endl) =~ '^'.spc.endpat
209 elseif self.getline(endl) =~ '^=begin\>'
210 while self.getline(endl) !~ '^=end\>' && endl <= self.line_count()
214 elseif self.getline(endl) !~ '^'.spc && self.getline(endl) !~ '^\s*\%(#.*\)\=$'
221 function! s:endof(lnum)
222 return rails#buffer().end_of(a:lnum)
225 function! s:readable_last_opening_line(start,pattern,limit) dict abort
227 while line > a:limit && self.getline(line) !~ a:pattern
230 let lend = self.end_of(line)
231 if line > a:limit && (lend < 0 || lend >= a:start)
238 function! s:lastopeningline(pattern,limit,start)
239 return rails#buffer().last_opening_line(a:start,a:pattern,a:limit)
242 function! s:readable_define_pattern() dict abort
243 if self.name() =~ '\.yml$'
244 return '^\%(\h\k*:\)\@='
246 let define = '^\s*def\s\+\(self\.\)\='
247 if self.name() =~# '\.rake$'
248 let define .= "\\\|^\\s*\\%(task\\\|file\\)\\s\\+[:'\"]"
250 if self.name() =~# '/schema\.rb$'
251 let define .= "\\\|^\\s*create_table\\s\\+[:'\"]"
253 if self.type_name('test')
254 let define .= '\|^\s*test\s*[''"]'
259 function! s:readable_last_method_line(start) dict abort
260 return self.last_opening_line(a:start,self.define_pattern(),0)
263 function! s:lastmethodline(start)
264 return rails#buffer().last_method_line(a:start)
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,' +','_')
274 return s:sub(matchstr(line,'\%('.self.define_pattern().'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','')
280 function! s:lastmethod(...)
281 return rails#buffer().last_method(a:0 ? a:1 : line("."))
284 function! s:readable_last_format(start) dict abort
285 if self.type_name('view')
286 let format = fnamemodify(self.path(),':r:e')
288 return get({'rhtml': 'html', 'rxml': 'xml', 'rjs': 'js', 'haml': 'html'},fnamemodify(self.path(),':e'),'')
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))
295 let variable = matchstr(self.getline(rline),'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|')
298 let match = matchstr(self.getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*')
308 function! s:lastformat(start)
309 return rails#buffer().last_format(a:start)
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
317 call s:add_methods('readable',['end_of','last_opening_line','last_method_line','last_method','last_format','define_pattern'])
319 let s:view_types = split('rhtml,erb,rxml,builder,rjs,mab,liquid,haml,dryml,mn,slim',',')
321 function! s:viewspattern()
322 return '\%('.join(s:view_types,'\|').'\)'
325 function! s:controller(...)
326 return rails#buffer().controller_name(a:0 ? a:1 : 0)
329 function! s:readable_controller_name(...) dict abort
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')
366 return rails#pluralize(self.model_name())
371 function! s:model(...)
372 return rails#buffer().model_name(a:0 ? a:1 : 0)
375 function! s:readable_model_name(...) dict abort
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')
400 return rails#singularize(self.controller_name())
405 call s:add_methods('readable',['controller_name','model_name'])
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,'/','\\').'$')
413 return getbufline(nr,1,a:0 ? a:1 : '$')
414 elseif !filereadable(a:path)
417 return readfile(a:path,'',a:1)
419 return readfile(a:path)
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
429 return get(self,'last_lines',[])
432 function! s:file_getline(lnum,...) dict abort
434 return self.lines[lnum-1 : a:1-1]
436 return self.lines[lnum-1]
440 function! s:buffer_lines() dict abort
441 return self.getline(1,'$')
444 function! s:buffer_getline(...) dict abort
446 return get(call('getbufline',[self.number()]+a:000),0,'')
448 return call('getbufline',[self.number()]+a:000)
452 function! s:readable_line_count() dict abort
453 return len(self.lines())
456 function! s:environment()
457 if exists('$RAILS_ENV')
464 function! s:Complete_environments(...)
465 return s:completion_filter(rails#app().environments(),a:0 ? a:1 : "")
468 function! s:warn(str)
472 " Sometimes required to flush output
474 let v:warningmsg = a:str
477 function! s:error(str)
484 function! s:debug(str)
485 if exists("g:rails_debug") && g:rails_debug
492 function! s:buffer_getvar(varname) dict abort
493 return getbufvar(self.number(),a:varname)
496 function! s:buffer_setvar(varname, val) dict abort
497 return setbufvar(self.number(),a:varname,a:val)
500 call s:add_methods('buffer',['getvar','setvar'])
503 " "Public" Interface {{{1
505 " RailsRoot() is the only official public function
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)
515 function! rails#camelize(str)
516 let str = s:gsub(a:str,'/(.=)','::\u\1')
517 let str = s:gsub(str,'%([_-]|<)(.)','\u\1')
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.
525 if word =~? '\.js$' || word == ''
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')
539 function! rails#pluralize(word)
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')
549 let word = s:sub(word,'ersons$','eople')
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)
560 function! rails#buffer(...)
561 return extend(extend({'#': bufnr(a:0 ? a:1 : '%')},s:buffer_prototype,'keep'),s:readable_prototype,'keep')
565 function! s:buffer_app() dict abort
566 if self.getvar('rails_root') != ''
567 return rails#app(self.getvar('rails_root'))
573 function! s:readable_app() dict abort
577 function! RailsRevision()
578 return 1000*matchstr(g:autoloaded_rails,'^\d\+')+matchstr(g:autoloaded_rails,'[1-9]\d*$')
581 function! RailsRoot()
582 if exists("b:rails_root")
589 function! s:app_file(name)
590 return extend(extend({'_app': self, '_name': a:name}, s:file_prototype,'keep'),s:readable_prototype,'keep')
593 function! s:file_path() dict abort
594 return self.app().path(self._name)
597 function! s:file_name() dict abort
601 function! s:buffer_number() dict abort
605 function! s:buffer_path() dict abort
606 return s:gsub(fnamemodify(bufname(self.number()),':p'),'\\ @!','/')
609 function! s:buffer_name() dict abort
611 let f = s:gsub(resolve(fnamemodify(bufname(self.number()),':p')),'\\ @!','/')
612 let f = s:sub(f,'/$','')
613 let sep = matchstr(f,'^[^\\/]\{3,\}\zs[\\/]')
615 let f = getcwd().sep.f
617 if s:startswith(tolower(f),s:gsub(tolower(app.path()),'\\ @!','/')) || f == ""
618 return strpart(f,strlen(app.path())+1)
620 if !exists("s:path_warn")
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!")
628 function! RailsFilePath()
629 if !exists("b:rails_root")
632 return rails#buffer().name()
636 function! RailsFile()
637 return RailsFilePath()
640 function! RailsFileType()
641 if !exists("b:rails_root")
644 return rails#buffer().type_name()
648 function! s:readable_calculate_file_type() dict abort
650 let e = fnamemodify(f,':e')
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,'/','\\').'$')
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"
667 elseif f =~ '_api\.rb'
669 elseif f =~ '\<test/test_helper\.rb$'
671 elseif f =~ '\<spec/spec_helper\.rb$'
673 elseif f =~ '_helper\.rb$'
675 elseif f =~ '\<app/metal/.*\.rb$'
677 elseif f =~ '\<app/mailers/.*\.rb'
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"
685 elseif class == 'ActionMailer::Base'
688 let class = tolower(s:gsub(class,'[^A-Z]',''))
689 let r = "model-".class
690 elseif f =~ '_mailer\.rb$'
692 elseif top =~ '\<\%(validates_\w\+_of\|set_\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>'
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().'$'
703 elseif f =~ '\<test/unit/.*_test\.rb$'
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$'
711 elseif f =~ '\<lib/.*\.rb$'
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$'
721 elseif f =~ '\<\%(test\|spec\)/fixtures\>'
723 let r = "fixtures-yaml"
725 let r = "fixtures" . (e == "" ? "" : "-" . e)
727 elseif f =~ '\<test/.*_test\.rb'
729 elseif f =~ '\<spec/.*_spec\.rb'
731 elseif f =~ '\<spec/support/.*\.rb'
733 elseif f =~ '\<db/migrate\>'
734 let r = "db-migration"
735 elseif f=~ '\<db/schema\.rb$'
737 elseif f =~ '\<vendor/plugins/.*/recipes/.*\.rb$' || f =~ '\.rake$' || f =~ '\<\%(Rake\|Cap\)file$' || f =~ '\<config/deploy\.rb$'
739 elseif f =~ '\<log/.*\.log$'
741 elseif e == "css" || e =~ "s[ac]ss" || e == "less"
742 let r = "stylesheet-".e
746 let r = "javascript-coffee"
749 elseif f =~ '\<config/routes\>.*\.rb$'
750 let r = "config-routes"
751 elseif f =~ '\<config/'
757 function! s:buffer_type_name(...) dict abort
758 let type = getbufvar(self.number(),'rails_cached_file_type')
760 let type = self.calculate_file_type()
762 return call('s:match_type',[type == '-' ? '' : type] + a:000)
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)
770 function! s:match_type(type,...)
772 return !empty(filter(copy(a:000),'a:type =~# "^".v:val."\\%(-\\|$\\)"'))
778 function! s:app_environments() dict
779 if self.cache.needs('environments')
780 call self.cache.set('environments',self.relglob('config/environments/','**/*','.rb'))
782 return copy(self.cache.get('environments'))
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'))
790 return self.cache.get('default_locale')
793 function! s:app_has(feature) dict
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',{})
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))
809 return features[a:feature]
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)')
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'])
823 " Ruby Execution {{{1
825 function! s:app_ruby_shell_command(cmd) dict abort
826 if self.path() =~ '://'
829 return "ruby -C ".s:rquote(self.path())." ".a:cmd
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
837 let cmd = 'script/'.a:cmd
839 return self.ruby_shell_command(cmd)
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
847 let screen = g:rails_gnu_screen
850 if &shellcmdflag == "-c" && ($PATH . &shell) =~? 'cygwin'
851 silent exe "!cygstart -d ".s:rquote(self.path())." ruby ".a:cmd
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.'"'
865 function! s:app_execute_script_command(cmd) dict abort
866 exe '!'.s:esccmd(self.script_shell_command(a:cmd))
870 function! s:app_lightweight_ruby_eval(ruby,...) dict abort
871 let def = a:0 ? a:1 : ""
872 if !executable("ruby")
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
882 function! s:app_eval(ruby,...) dict abort
883 let def = a:0 ? a:1 : ""
884 if !executable("ruby")
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
894 call s:add_methods('app', ['ruby_shell_command','script_shell_command','execute_script_command','background_script_command','lightweight_ruby_eval','eval'])
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`
908 function! RailsHelpCommand(...)
910 let topic = a:0 ? a:1 : ""
911 if topic == "" || topic == "-"
913 elseif topic =~ '^g:'
916 return "help rails".topic
918 return "help rails-".topic
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>)`
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
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>)
949 if RailsFilePath() =~ '\<db/migrate/.*\.rb$'
950 command! -buffer -bar Rinvert :call s:Invert(<bang>0)
954 function! s:Doc(bang, 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)
960 return s:error("specify a g:rails_search_url with %s for a query placeholder")
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"
967 let url = "http://api.rubyonrails.org"
970 if exists(":OpenURL")
971 exe "OpenURL ".s:escarg(url)
973 return s:error("No :OpenURL command found")
977 function! s:Log(bang,arg)
979 let lf = "log/".s:environment().".log"
981 let lf = "log/".a:arg.".log"
983 let size = getfsize(rails#app().path(lf))
985 call s:warn("Log file is ".((size+512)/1024)."KB. Consider :Rake log:clear")
992 Tail `=rails#app().path(lf)`
994 pedit `=rails#app().path(lf)`
999 function! rails#new_app_command(bang,...)
1001 let msg = "rails.vim ".g:autoloaded_rails
1002 if a:bang && exists('b:rails_root') && rails#buffer().type_name() == ''
1004 elseif a:bang && exists('b:rails_root')
1005 echo msg." (Rails-".rails#buffer().type_name().")"
1013 let args = map(copy(a:000),'expand(v:val)')
1015 let args = ['--force'] + args
1017 exe '!rails '.join(map(copy(args),'s:rquote(v:val)'),' ')
1019 if dir !~# '^-' && filereadable(dir.'/'.g:rails_default_file)
1020 edit `=dir.'/'.g:rails_default_file`
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")
1035 elseif executable("ctags.exe")
1036 let cmd = "ctags.exe"
1038 return s:error("ctags not found")
1040 exe '!'.cmd.' -f '.s:escarg(self.path("tmp/tags")).' -R --langmap="ruby:+.rake.builder.rjs" '.g:rails_ctags_arguments.' '.s:escarg(self.path())
1043 call s:add_methods('app',['tags_command'])
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
1050 silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
1053 call rails#app().cache.clear()
1054 silent doautocmd User BufLeaveRails
1056 for key in keys(s:apps)
1057 if type(s:apps[key]) == type({})
1058 call s:apps[key].cache.clear()
1060 call extend(s:apps[key],filter(copy(s:app_prototype),'type(v:val) == type(function("tr"))'),'force')
1064 let max = bufnr('$')
1066 let rr = getbufvar(i,"rails_root")
1068 call setbufvar(i,"rails_refresh",1)
1072 silent doautocmd User BufEnterRails
1075 function! s:RefreshBuffer()
1076 if exists("b:rails_refresh") && b:rails_refresh
1077 let oldroot = b:rails_root
1079 let b:rails_refresh = 0
1080 call RailsBufInit(oldroot)
1081 unlet! b:rails_refresh
1088 function! s:app_rake_tasks() dict
1089 if self.cache.needs('rake_tasks')
1092 let lines = split(system("rake -T"),"\n")
1094 call s:pop_command()
1096 if v:shell_error != 0
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)
1103 return self.cache.get('rake_tasks')
1106 call s:add_methods('app', ['rake_tasks'])
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,'
1114 \.'%\\s%#[%f:%l:\ %#%m,'
1115 \.'%\\s%#%f:%l:\ %#%m,'
1119 function! s:makewithruby(arg,bang,...)
1120 let old_make = &makeprg
1122 let &l:makeprg = rails#app().ruby_shell_command(a:arg)
1123 exe 'make'.(a:bang ? '!' : '')
1128 let &l:makeprg = old_make
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
1138 if exists('b:bundler_root') && b:bundler_root ==# rails#app().path()
1139 let &l:makeprg = 'bundle exec rake'
1141 let &l:makeprg = 'rake'
1143 let &l:errorformat = s:efm_backtrace
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+$','')
1156 let opt = s:getopt('task','bl')
1160 let arg = rails#buffer().default_rake_task(lnum)
1163 if !has_key(self,'options') | let self.options = {} | endif
1165 let arg = get(self.options,'last_rake_task','')
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)
1174 call s:pop_command()
1178 elseif arg =~# '^\%(stats\|routes\|secret\|time:zones\|db:\%(charset\|collation\|fixtures:identify\>.*\|migrate:status\|version\)\)\%([: ]\|$\)'
1179 let &l:errorformat = '%D(in\ %f),%+G%.%#'
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.*')
1191 let extra = " -- -n ".matchstr(file,'#\zs.*')
1192 let file = s:sub(file,'#.*','')
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\)\=$')
1199 call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg)),a:bang)
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$')
1216 let &l:errorformat = old_errorformat
1217 let &l:makeprg = old_makeprg
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'
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')
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)
1247 return matchstr(self.getline(1),'\C# rake \zs.*')
1249 elseif self.type_name('spec')
1250 if self.name() =~# '\<spec/spec_helper\.rb$'
1253 return 'spec SPEC="'.self.path().'":'.lnum
1255 return 'spec SPEC="'.self.path().'"'
1257 elseif self.type_name('test')
1258 let meth = self.last_method(lnum)
1260 let call = " -n".meth.""
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$'
1269 return 'test:recent TEST="'.self.path().'"'.s:sub(call,'^ ',' TESTOPTS=')
1271 elseif self.type_name('db-migration')
1272 let ver = matchstr(self.name(),'\<db/migrate/0*\zs\d*\ze_')
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
1280 return "db:migrate:down db:migrate:up VERSION=".ver
1282 return "db:migrate VERSION=".ver
1287 elseif self.name() =~# '\<db/seeds\.rb$'
1289 elseif self.type_name('controller') && lnum
1290 let lm = self.last_method(lnum)
1292 " rake routes doesn't support ACTION... yet...
1293 return 'routes CONTROLLER='.self.controller_name().' ACTION='.lm
1295 return 'routes CONTROLLER='.self.controller_name()
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')).'"'
1311 return 'test:functionals'
1313 elseif self.type_name('cucumber-feature')
1315 return 'cucumber FEATURE="'.self.path().'":'.lnum
1317 return 'cucumber FEATURE="'.self.path().'"'
1319 elseif self.type_name('cucumber')
1326 function! s:Complete_rake(A,L,P)
1327 return s:completion_filter(rails#app().rake_tasks(),a:A)
1330 call s:add_methods('readable',['default_rake_task'])
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>
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.*')
1353 let url .= (url =~ '?' ? '&' : '?') . '_method='.tolower(method)
1363 function! s:readable_preview_urls(lnum) dict abort
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
1371 while start < self.line_count() && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$'
1372 let urls += s:scanlineforuris(self.getline(start))
1375 if has_key(self,'getvar') && self.getvar('rails_preview') != ''
1376 let url += [self.getvar('rails_preview')]
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').'/']
1402 call s:add_methods('readable',['preview_urls'])
1404 function! s:Preview(bang,lnum,arg)
1405 let root = s:getopt("root_url")
1407 let root = s:getopt("url")
1409 let root = s:sub(root,'/$','')
1413 let uri = root.'/'.s:sub(a:arg,'^/','')
1415 let uri = get(rails#buffer().preview_urls(a:lnum),0,'')
1416 let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','')
1418 call s:initOpenURL()
1419 if exists(':OpenURL') && !a:bang
1422 " Work around bug where URLs ending in / get handled as FTP
1423 let url = uri.(uri =~ '/$' ? '?' : '')
1424 silent exe 'pedit '.url
1428 setlocal filetype=css
1429 elseif uri =~ '\.js$'
1430 setlocal filetype=javascript
1431 elseif getline(1) =~ '^\s*<'
1432 setlocal filetype=xhtml
1435 call RailsBufInit(rails#app().path())
1436 map <buffer> <silent> q :bwipe<CR>
1439 call s:warn("Define a :OpenURL command to use a browser")
1444 function! s:Complete_preview(A,L,P)
1445 return rails#buffer().preview_urls(a:L =~ '^\d' ? matchstr(a:L,'^\d\+') : line('.'))
1449 " Script Wrappers {{{1
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')
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)
1472 return sort(split(g:rails_generators,"\n") + self.cache.get('generators'))
1475 function! s:app_script_command(bang,...) dict
1477 let cmd = a:0 ? a:1 : "console"
1480 let str .= " " . s:rquote(a:{c})
1484 call self.cache.clear('generators')
1486 if a:bang || cmd =~# 'console'
1487 return self.background_script_command(cmd.str)
1489 return self.execute_script_command(cmd.str)
1493 function! s:app_runner_command(count,args) dict
1495 return self.script_command(a:bang,"runner",a:args)
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$','')
1502 exe a:count.'put =res'
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','')
1520 function! s:app_server_command(bang,arg) dict
1521 let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+')
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)
1530 echo "Killing server with pid ".pid
1532 call system("ruby -e 'Process.kill(:TERM,".pid.")'")
1535 call system("ruby -e 'Process.kill(9,".pid.")'")
1542 if has_key(self,'options') && has_key(self.options,'gnu_screen')
1543 let screen = self.options.gnu_screen
1545 let screen = g:rails_gnu_screen
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)
1550 " --daemon would be more descriptive but lighttpd does not support it
1551 call self.execute_script_command('server '.a:arg." -d")
1553 call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/')
1556 function! s:app_destroy_command(bang,...) dict
1558 return self.execute_script_command('destroy')
1560 return self.execute_script_command('destroy '.s:rquote(a:1))
1565 let str .= " " . s:rquote(a:{c})
1568 call self.execute_script_command('destroy'.str)
1569 call self.cache.clear('user_classes')
1572 function! s:app_generate_command(bang,...) dict
1574 return self.execute_script_command('generate')
1576 return self.execute_script_command('generate '.s:rquote(a:1))
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)
1583 let junk = '\%(\e\[[0-9;]*m\)\='
1584 let file = matchstr(res,junk.'\s\+\%(create\|force\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
1586 let file = matchstr(res,junk.'\s\+\%(identical\)'.junk.'\s\+\zs\f\+\.rb\ze\n')
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)
1597 edit `=self.path(file)`
1601 call s:add_methods('app', ['generators','script_command','runner_command','server_command','destroy_command','generate_command'])
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_=:-]*$'
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\>'
1637 call filter(models,'index(observers,v:val) < 0')
1638 return s:completion_filter(observers + models,a:ArgLead)
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)
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)')
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)
1665 function! s:Complete_server(A,L,P)
1666 return s:CustomComplete(a:A,a:L,a:P,"server")
1669 function! s:Complete_console(A,L,P)
1670 return s:CustomComplete(a:A,a:L,a:P,"console")
1673 function! s:Complete_generate(A,L,P)
1674 return s:CustomComplete(a:A,a:L,a:P,"generate")
1677 function! s:Complete_destroy(A,L,P)
1678 return s:CustomComplete(a:A,a:L,a:P,"destroy")
1681 function! s:Complete_ruby(A,L,P)
1682 return s:completion_filter(rails#app().user_classes()+["ActiveRecord::Base"],a:A)
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>)
1719 function! s:djump(def)
1720 let def = s:sub(a:def,'^[#:]','')
1724 if expand('%') !~ '://' && !isdirectory(expand('%:p:h'))
1725 call mkdir(expand('%:p:h'),'p')
1728 let ext = matchstr(def,'\.\zs.*')
1729 let def = matchstr(def,'[^.]*')
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)
1737 let variable = matchstr(getline(rline),rpat)
1738 let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end)
1740 silent! exe "djump ".def
1747 function! s:Find(count,cmd,...)
1752 let str .= s:escarg(a:{i}) . " "
1756 let tail = matchstr(file,'[#!].*$\|:\d*\%(:in\>.*\)\=$')
1758 let file = s:sub(file,'[#!].*$|:\d*%(:in>.*)=$','')
1761 let file = s:RailsIncludefind(file)
1764 let file = s:RailsFind()
1767 call s:findedit((a:count==1?'' : a:count).a:cmd,file.tail,str)
1770 function! s:Edit(count,cmd,...)
1775 let str .= "`=a:".i."` "
1779 call s:findedit(s:editcmdfor(a:cmd),file,str)
1781 exe s:editcmdfor(a:cmd)
1785 function! s:fuzzyglob(arg)
1786 return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*')
1789 function! s:Complete_find(ArgLead, CmdLine, CursorPos)
1790 let paths = s:pathsplit(&l:path)
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' : '')
1800 return s:autocamelize(sort(keys(seen)),a:ArgLead)
1803 function! s:Complete_edit(ArgLead, CmdLine, CursorPos)
1804 return s:completion_filter(rails#app().relglob("",s:fuzzyglob(a:ArgLead)),a:ArgLead)
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)')
1813 function! RailsIncludeexpr()
1814 " Is this foolproof?
1815 if mode() =~ '[iR]' || expand("<cfile>") != v:fname
1816 return s:RailsIncludefind(v:fname)
1818 return s:RailsIncludefind(v:fname,1)
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*$','')
1829 function! s:matchcursor(pat)
1830 let line = getline(".")
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)
1843 function! s:findit(pat,repl)
1844 let res = s:matchcursor(a:pat)
1846 return substitute(res,'\C'.a:pat,a:repl,'')
1852 function! s:findamethod(func,repl)
1853 return s:findit('\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
1856 function! s:findasymbol(sym,repl)
1857 return s:findit('\s*\%(:\%('.a:sym.'\)\s*=>\|\<'.a:sym.':\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl)
1860 function! s:findfromview(func,repl)
1861 " ( ) ( ) ( \1 ) ( )
1862 return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl)
1865 function! s:RailsFind()
1866 if filereadable(expand("<cfile>"))
1867 return expand("<cfile>")
1871 let buffer = rails#buffer()
1872 let format = s:format('html')
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
1877 let res = s:findit('\v<File.dirname\(__FILE__\)\s*\+\s*[:'."'".'"](\f+)>['."'".'"]=',expand('%:h').'\1')
1878 if res != ""|return res|endif
1880 let res = rails#underscore(s:findit('\v\s*<%(include|extend)\(=\s*<([[:alnum:]_:]+)>','\1'))
1881 if res != ""|return res.".rb"|endif
1883 let res = s:findamethod('require','\1')
1884 if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif
1886 let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb')
1887 if res != ""|return res|endif
1889 let res = rails#singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1'))
1890 if res != ""|return res.".rb"|endif
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
1895 let res = rails#singularize(s:findasymbol('through','app/models/\1'))
1896 if res != ""|return res.".rb"|endif
1898 let res = s:findamethod('fixtures','fixtures/\1')
1900 return RailsFilePath() =~ '\<spec/' ? 'spec/'.res : res
1903 let res = s:findamethod('\%(\w\+\.\)\=resources','app/controllers/\1_controller.rb')
1904 if res != ""|return res|endif
1906 let res = s:findamethod('\%(\w\+\.\)\=resource','app/controllers/\1')
1907 if res != ""|return rails#pluralize(res)."_controller.rb"|endif
1909 let res = s:findasymbol('to','app/controllers/\1')
1910 if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
1912 let res = s:findamethod('root\s*\%(:to\s*=>\|\<to:\)\s*','app/controllers/\1')
1913 if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
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
1918 let res = s:findamethod('layout','\=s:findlayout(submatch(1))')
1919 if res != ""|return res|endif
1921 let res = s:findasymbol('layout','\=s:findlayout(submatch(1))')
1922 if res != ""|return res|endif
1924 let res = s:findamethod('helper','app/helpers/\1_helper.rb')
1925 if res != ""|return res|endif
1927 let res = s:findasymbol('controller','app/controllers/\1_controller.rb')
1928 if res != ""|return res|endif
1930 let res = s:findasymbol('action','\1')
1931 if res != ""|return res|endif
1933 let res = s:findasymbol('template','app/views/\1')
1934 if res != ""|return res|endif
1936 let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'[^/]+$','_&')
1937 if res != ""|return res."\n".s:findview(res)|endif
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
1942 let res = s:findamethod('render\>\s*\%(:\%(template\|action\)\s\+=>\|template:\|action:\)\s*','\1.'.format.'\n\1')
1943 if res != ""|return res|endif
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
1949 let res = s:findamethod('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\<action:\)\s*','\1')
1950 if res != ""|return res|endif
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
1956 if res != ""|return res|endif
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
1962 if res != ""|return res|endif
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
1971 let old_isfname = &isfname
1973 set isfname=@,48-57,/,-,_,:,#
1974 " TODO: grab visual selection in visual mode
1975 let cfile = expand("<cfile>")
1977 let &isfname = old_isfname
1979 let res = s:RailsIncludefind(cfile,1)
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]
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)
1996 for line in split(string,"\n")
1997 let route = split(line," ")
1999 let routes[name] = route[1]
2001 call self.cache.set("named_routes",routes)
2004 return keys(self.cache.get("named_routes"))
2007 call s:add_methods('app', ['route_names','named_route_file'])
2009 function! RailsNamedRoutes()
2010 return rails#app().route_names()
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"
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*$','')
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*$'
2035 elseif str =~# '^\l\w*#\w\+$'
2036 return 'app/controllers/'.s:sub(str,'#','_controller.rb#')
2038 let str = rails#underscore(str)
2039 let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*'
2041 " Classes should always be in .rb files
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/')
2056 let str = s:sub(str,'^/@!','test/fixtures/')
2058 elseif line =~# '\<stylesheet_\(link_tag\|path\)\s*(\='.fpat
2059 let str = s:sub(str,'^/@!','/stylesheets/')
2060 if str != '' && fnamemodify(str, ':e') == ''
2063 elseif line =~# '\<javascript_\(include_tag\|path\)\s*(\='.fpat
2064 if str ==# "defaults"
2065 let str = "application"
2067 let str = s:sub(str,'^/@!','/javascripts/')
2068 if str != '' && fnamemodify(str, ':e') == ''
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_','')
2082 let file = rails#app().named_route_file(str)
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'
2093 let str = 'app/controllers/'.str.'_controller.rb#index'
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$','')
2103 if str =~ '^/' && !filereadable(str)
2104 let str = s:sub(str,'^/','')
2106 if str =~# '^lib/' && !filereadable(str)
2107 let str = s:sub(str,'^lib/','')
2115 function! s:addfilecmds(type)
2116 let l = s:sub(a:type,'^.','\l&')
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)
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")
2144 if rails#app().has('test') || rails#app().has('spec') || rails#app().has('cucumber')
2145 call s:addfilecmds("integrationtest")
2147 if rails#app().has('spec')
2148 call s:addfilecmds("spec")
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")
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')
2172 function! s:autocamelize(files,test)
2174 return s:completion_filter(map(copy(a:files),'rails#camelize(v:val)'),a:test)
2176 return s:completion_filter(a:files,a:test)
2180 function! s:app_relglob(path,glob,...) dict
2181 if exists("+shellslash") && ! &shellslash
2182 let old_ss = &shellslash
2186 if path !~ '^/' && path !~ '^\w:'
2187 let path = self.path(path)
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 !~ '/$'
2196 let relative_paths += [entry[strlen(path) : -strlen(suffix)-1]]
2199 let &shellslash = old_ss
2201 return relative_paths
2204 call s:add_methods('app', ['relglob'])
2206 function! s:relglob(...)
2207 return join(call(rails#app().relglob,a:000,rails#app()),"\n")
2210 function! s:helperList(A,L,P)
2211 return s:autocamelize(rails#app().relglob("app/helpers/","**/*","_helper.rb"),a:A)
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)
2220 function! s:mailerList(A,L,P)
2221 return s:autocamelize(rails#app().relglob("app/mailers/","**/*",".rb"),a:A)
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)
2232 return s:completion_filter(top,a:A)
2235 function! s:layoutList(A,L,P)
2236 return s:completion_filter(rails#app().relglob("app/views/layouts/","*"),a:A)
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'))
2247 return s:completion_filter(list,a:A)
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)
2257 function! s:metalList(A,L,P)
2258 return s:autocamelize(rails#app().relglob("app/metal/","**/*",".rb"),a:A)
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)
2267 function! s:observerList(A,L,P)
2268 return s:autocamelize(rails#app().relglob("app/models/","**/*","_observer.rb"),a:A)
2271 function! s:fixturesList(A,L,P)
2272 return s:completion_filter(rails#app().relglob("test/fixtures/","**/*")+rails#app().relglob("spec/fixtures/","**/*"),a:A)
2275 function! s:localeList(A,L,P)
2276 return s:completion_filter(rails#app().relglob("config/locales/","**/*"),a:A)
2279 function! s:migrationList(A,L,P)
2281 let migrations = rails#app().relglob("db/migrate/",a:A."[0-9_]*",".rb")
2282 return map(migrations,'matchstr(v:val,"^[0-9]*")')
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)
2290 function! s:unittestList(A,L,P)
2292 if rails#app().has('test')
2293 let found += rails#app().relglob("test/unit/","**/*","_test.rb")
2295 if rails#app().has('spec')
2296 let found += rails#app().relglob("spec/models/","**/*","_spec.rb")
2298 return s:autocamelize(found,a:A)
2301 function! s:functionaltestList(A,L,P)
2303 if rails#app().has('test')
2304 let found += rails#app().relglob("test/functional/","**/*","_test.rb")
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")
2310 return s:autocamelize(found,a:A)
2313 function! s:integrationtestList(A,L,P)
2315 return s:autocamelize(rails#app().relglob("test/integration/","**/*","_test.rb"),a:A)
2318 if rails#app().has('test')
2319 let found += rails#app().relglob("test/integration/","**/*","_test.rb")
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")
2325 if rails#app().has('cucumber')
2326 let found += rails#app().relglob("features/","**/*",".feature")
2328 return s:completion_filter(found,a:A)
2331 function! s:specList(A,L,P)
2332 return s:completion_filter(rails#app().relglob("spec/","**/*","_spec.rb"),a:A)
2335 function! s:pluginList(A,L,P)
2337 return s:completion_filter(rails#app().relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*'),a:A)
2339 return s:completion_filter(rails#app().relglob('vendor/plugins/',"*","/init.rb"),a:A)
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
2350 return s:autocamelize(all,a:A)
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
2359 return s:autocamelize(all,a:A)
2362 function! s:environmentList(A,L,P)
2363 return s:completion_filter(rails#app().relglob("config/environments/","**/*",".rb"),a:A)
2366 function! s:initializerList(A,L,P)
2367 return s:completion_filter(rails#app().relglob("config/initializers/","**/*",".rb"),a:A)
2370 function! s:Navcommand(bang,...)
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.*')
2387 " A literal '\n'. For evaluation below
2391 let prefix .= "\\n".s:sub(arg,'/=$','/')
2395 let prefix = s:sub(prefix,'^\\n','')
2396 if name !~ '^[A-Za-z]\+$'
2397 return s:error("E182: Invalid command name")
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)
2408 function! s:CommandList(A,L,P)
2409 let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+')
2411 let lp = s:last_prefix . "\n"
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)
2419 return s:autocamelize(res,a:A)
2421 return s:completion_filter(res,a:A)
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$')
2432 if a:default == "both()"
2434 let default = s:model()
2436 let default = s:controller()
2438 elseif a:default == "model()"
2439 let default = s:model(1)
2440 elseif a:default == "controller()"
2441 let default = s:controller(1)
2443 let default = a:default
2445 call s:EditSimpleRb(a:cmd,a:name,a:0 ? a:1 : default,a:prefix,a:suffix)
2449 function! s:EditSimpleRb(cmd,name,target,prefix,suffix,...)
2450 let cmd = s:findcmdfor(a:cmd)
2452 " Good idea to emulate error numbers like this?
2453 return s:error("E471: Argument required")
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)=$','')
2459 let cmd = s:editcmdfor(cmd)
2462 let f = s:sub(f,'\.$','')
2464 let f .= a:suffix.jump
2466 let f = s:gsub(a:prefix,'\n',f.'\n').f
2467 return s:findedit(cmd,f)
2470 function! s:app_migration(file) dict
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
2479 return 'db/schema.rb'.suffix
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'
2490 let glob = '*'.rails#underscore(arg).'*rb'
2492 let files = split(glob(self.path('db/migrate/').glob),"\n")
2494 return get(files,-1,'')
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)
2506 call s:add_methods('app', ['migration'])
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)
2513 call s:findedit(cmd,migr)
2515 return s:error("Migration not found".(arg=='' ? '' : ': '.arg))
2519 function! s:fixturesEdit(cmd,...)
2521 let c = rails#underscore(a:1)
2523 let c = rails#pluralize(s:model(1))
2526 return s:error("E471: Argument required")
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)
2535 call s:findedit(a:cmd,rails#app().find_file(c.e,["test/fixtures","spec/fixtures"],[".yml",".csv"],file))
2539 function! s:localeEdit(cmd,...)
2540 let c = a:0 ? a:1 : rails#app().default_locale()
2542 call s:edit(a:cmd,rails#app().find_file(c,'config/locales',[],'config/locales/'.c))
2544 call s:findedit(a:cmd,rails#app().find_file(c,'config/locales',['.yml','.rb'],'config/locales/'.c))
2548 function! s:metalEdit(cmd,...)
2550 call s:EditSimpleRb(a:cmd,"metal",a:1,"app/metal/",".rb")
2552 call s:EditSimpleRb(a:cmd,"metal",'config/boot',"",".rb")
2556 function! s:modelEdit(cmd,...)
2557 call s:EditSimpleRb(a:cmd,"model",a:0? a:1 : s:model(1),"app/models/",".rb")
2560 function! s:observerEdit(cmd,...)
2561 call s:EditSimpleRb(a:cmd,"observer",a:0? a:1 : s:model(1),"app/models/","_observer.rb")
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('.'))
2573 return s:error("No view name given")
2575 return s:edit(a:cmd,'app/views')
2576 elseif view !~ '/' && s:controller(1) != ''
2577 let view = s:controller(1) . '/' . view
2580 return s:error("Cannot find view without controller")
2582 let file = "app/views/".view
2583 let found = s:findview(view)
2585 let dir = fnamemodify(rails#app().path(found),':h')
2586 if !isdirectory(dir)
2587 if a:0 && a:1 =~ '!'
2590 return s:error('No such directory')
2593 call s:edit(a:cmd,found)
2594 elseif file =~ '\.\w\+$'
2595 call s:findedit(a:cmd,file)
2597 let format = s:format(rails#buffer().type_name('mailer') ? 'text' : 'html')
2598 if glob(rails#app().path(file.'.'.format).'.*[^~]') != ''
2599 let file .= '.' . format
2601 call s:findedit(a:cmd,file)
2605 function! s:findview(name)
2606 let self = rails#buffer()
2608 let pre = 'app/views/'
2610 let controller = self.controller_name(1)
2612 let name = controller.'/'.name
2615 if name =~# '\.\w\+\.\w\+$' || name =~# '\.'.s:viewspattern().'$'
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
2629 function! s:findlayout(name)
2630 return s:findview("layouts/".(a:name == '' ? 'application' : a:name))
2633 function! s:layoutEdit(cmd,...)
2635 return s:viewEdit(a:cmd,"layouts/".a:1)
2637 let file = s:findlayout(s:controller(1))
2639 let file = s:findlayout("application")
2642 let file = "app/views/layouts/application.html.erb"
2644 call s:edit(a:cmd,s:sub(file,'^/',''))
2647 function! s:controllerEdit(cmd,...)
2650 let controller = s:controller(1)
2651 if rails#buffer().type_name() =~# '^view\%(-layout\|-partial\)\@!'
2652 let suffix .= '#'.expand('%:t:r')
2655 let controller = a:1
2657 if rails#app().has_file("app/controllers/".controller."_controller.rb") || !rails#app().has_file("app/controllers/".controller.".rb")
2658 let suffix = "_controller".suffix
2660 return s:EditSimpleRb(a:cmd,"controller",controller,"app/controllers/",suffix)
2663 function! s:mailerEdit(cmd,...)
2664 return s:EditSimpleRb(a:cmd,"mailer",a:0? a:1 : s:controller(1),"app/mailers/\napp/models/",".rb")
2667 function! s:helperEdit(cmd,...)
2668 return s:EditSimpleRb(a:cmd,"helper",a:0? a:1 : s:controller(1),"app/helpers/","_helper.rb")
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)
2680 let types = rails#app().relglob('app/assets/stylesheets/'.name,'.*','')
2682 return s:EditSimpleRb(a:cmd,'stylesheet',name,'app/assets/stylesheets/',types[0],1)
2684 return s:EditSimpleRb(a:cmd,'stylesheet',name,'public/stylesheets/','.css',1)
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)
2696 let types = rails#app().relglob('app/assets/javascripts/'.name,'.*','')
2698 return s:EditSimpleRb(a:cmd,'javascript',name,'app/assets/javascripts/',types[0],1)
2700 return s:EditSimpleRb(a:cmd,'javascript',name,'public/javascripts/','.js',1)
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,'[!#:].*') : ''
2709 let cmd = s:editcmdfor(a:cmd)
2711 let cmd = s:findcmdfor(a:cmd)
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)')
2716 let tests = [mapping['test']]
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)
2725 for [prefix, suffix] in tests
2726 if rails#app().has_file(prefix.f.suffix)
2727 return s:findedit(cmd,prefix.f.suffix.jump)
2730 return s:EditSimpleRb(a:cmd,"unittest",f.jump,tests[0][0],tests[0][1],1)
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,'[!#:].*') : ''
2737 let cmd = s:editcmdfor(a:cmd)
2739 let cmd = s:findcmdfor(a:cmd)
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)')
2744 let tests = [mapping[tests]]
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)
2755 return s:EditSimpleRb(a:cmd,"functionaltest",f.jump,tests[0][0][0],tests[0][1][0],1)
2758 function! s:integrationtestEdit(cmd,...)
2760 return s:EditSimpleRb(a:cmd,"integrationtest","test/test_helper\nfeatures/support/env\nspec/spec_helper","",".rb")
2762 let f = rails#underscore(matchstr(a:1,'[^!#:]*'))
2763 let jump = matchstr(a:1,'[!#:].*')
2765 let cmd = s:editcmdfor(a:cmd)
2767 let cmd = s:findcmdfor(a:cmd)
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]))')
2772 let tests = [['test/integration/','_test.rb']]
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)
2781 return s:EditSimpleRb(a:cmd,"integrationtest",f.jump,tests[0][0],tests[0][1],1)
2784 function! s:specEdit(cmd,...)
2786 return s:EditSimpleRb(a:cmd,"spec",a:1,"spec/","_spec.rb")
2788 call s:EditSimpleRb(a:cmd,"spec","spec_helper","spec/",".rb")
2792 function! s:pluginEdit(cmd,...)
2793 let cmd = s:findcmdfor(a:cmd)
2796 if RailsFilePath() =~ '\<vendor/plugins/.'
2797 let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/\zs[^/]*\ze')
2798 let extra = "vendor/plugins/" . plugin . "/\n"
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")
2804 call s:edit(cmd,"vendor/plugins/".s:sub(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)
2810 call s:findedit(cmd,"vendor/plugins/".a:1."\nvendor/plugins/".plugin."/".a:1)
2813 call s:findedit(a:cmd,"Gemfile")
2817 function! s:taskEdit(cmd,...)
2820 if RailsFilePath() =~ '\<vendor/plugins/.'
2821 let plugin = matchstr(RailsFilePath(),'\<vendor/plugins/[^/]*')
2822 let extra = plugin."/tasks/\n".plugin."/lib/tasks/\n"
2825 call s:EditSimpleRb(a:cmd,"task",a:1,extra."lib/tasks/",".rake")
2827 call s:findedit(a:cmd,(plugin != "" ? plugin."/Rakefile\n" : "")."Rakefile")
2831 function! s:libEdit(cmd,...)
2833 if RailsFilePath() =~ '\<vendor/plugins/.'
2834 let extra = s:sub(RailsFilePath(),'<vendor/plugins/[^/]*/\zs.*','lib/')."\n"
2837 call s:EditSimpleRb(a:cmd,"lib",a:0? a:1 : "",extra."lib/",".rb")
2839 call s:EditSimpleRb(a:cmd,"lib","seeds","db/",".rb")
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")
2847 return s:EditSimpleRb(a:cmd,"environment","environment","config/",".rb")
2851 function! s:initializerEdit(cmd,...)
2852 return s:EditSimpleRb(a:cmd,"initializer",a:0? a:1 : "../routes","config/initializers/",".rb")
2856 " Alternate/Related {{{1
2858 function! s:findcmdfor(cmd)
2862 let cmd = s:sub(a:cmd,'\!$','')
2867 let num = matchstr(cmd,'^\d\+')
2868 let cmd = s:sub(cmd,'^\d+','')
2872 if cmd == '' || cmd == 'E' || cmd == 'F'
2873 return num.'find'.bang
2875 return num.'sfind'.bang
2877 return 'vert '.num.'sfind'.bang
2879 return num.'tabfind'.bang
2881 return num.'read'.bang
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')
2894 function! s:try(cmd) abort
2896 " I've seen at least one weird setup without :try
2902 call s:error(s:sub(v:exception,'^.{-}:\zeE',''))
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")
2915 let file = get(filter(copy(files),'rails#app().has_file(s:sub(v:val,"#.*|:\\d*$",""))'),0,get(files,0,''))
2917 if file =~ '[#!]\|:\d*\%(:in\)\=$'
2918 let djump = matchstr(file,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$')
2919 let file = s:sub(file,'[#!].*|:\d*%(:in)=$','')
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)
2930 elseif rails#app().path() =~ '://' || cmd =~ 'edit' || cmd =~ 'split'
2931 if file !~ '^/' && file !~ '^\w:' && file !~ '://'
2932 let file = s:escarg(rails#app().path(file))
2934 let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file
2936 let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file
2943 function! s:edit(cmd,file,...)
2944 let cmd = s:editcmdfor(a:cmd)
2945 let cmd .= ' '.(a:0 ? a:1 . ' ' : '')
2947 if file !~ '^/' && file !~ '^\w:' && file !~ '://'
2948 exe cmd."`=fnamemodify(rails#app().path(file),':.')`"
2954 function! s:Alternate(cmd,line1,line2,count,...)
2956 if a:count && a:cmd !~# 'D'
2957 return call('s:Find',[1,a:line1.a:cmd]+a:000)
2959 return call('s:Edit',[1,a:line1.a:cmd]+a:000)
2961 return call('s:Edit',[1,a:cmd]+a:000)
2964 let file = s:getopt(a:count ? 'related' : 'alternate', 'bl')
2966 let file = rails#buffer().related(a:count)
2969 call s:findedit(a:cmd,file)
2971 call s:warn("No alternate file is defined")
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)
2980 return call('s:Alternate',[a:cmd,a:line1,a:line2,a:count]+a:000)
2984 function! s:Complete_related(A,L,P)
2985 if a:L =~# '^[[:alpha:]]'
2986 return s:Complete_edit(a:A,a:L,a:P)
2988 return s:Complete_find(a:A,a:L,a:P)
2992 function! s:readable_related(...) dict abort
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)
3000 let format = self.type_name('mailer') ? 'text' : 'html'
3002 if glob(self.app().path().'/'.root.'.'.format.'.*[^~]') != ''
3003 return root . '.' . format
3007 elseif f =~ '\<config/environments/'
3008 return "config/database.yml#". fnamemodify(f,':t:r')
3009 elseif f =~ '\<config/database\.yml$'
3011 return "config/environments/".lastmethod.".rb"
3013 return "config/application.rb\nconfig/environment.rb"
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,'#.{-}$',''))
3027 elseif self.app().has_file(s:sub(controller2,'#.{-}$',''))
3029 elseif self.app().has_file(s:sub(mailer,'#.{-}$',''))
3031 elseif self.app().has_file(s:sub(model,'#.{-}$','')) || model =~ '_mailer\.rb#'
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\+')
3043 let table_name = rails#pluralize(s:gsub(s:sub(fnamemodify(f,':r'),'.{-}<app/models/',''),'/','_'))
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)
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'
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"
3072 let candidates = filter(copy(migrations),'v:val > me')
3073 let migration = "db/migrate/".get(candidates,0,migrations[0]).".rb"
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)
3088 elseif self.app().has_file(spec2)
3090 elseif self.app().has_file(spec3)
3092 elseif self.app().has('spec')
3095 if self.type_name('view-layout')
3096 let dest = fnamemodify(f,':r:s?/layouts\>??').'/layout.'.fnamemodify(f,':e')
3100 return s:sub(s:sub(dest,'<app/views/','test/functional/'),'/[^/]*$','_controller_test.rb')
3102 elseif self.type_name('controller-api')
3103 let api = s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb')
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'
3110 elseif self.type_name('fixtures')
3111 let file = rails#singularize(fnamemodify(f,":t:r")).'_test.rb'
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')
3124 let file .= '_test.rb'
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/')
3142 return s:sub(file,'test/functional/','')
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/')
3155 return fnamemodify(file,':t')."\n".s:sub(s:sub(f,'\.rb$','_spec.rb'),'^app/','spec/')
3162 call s:add_methods('readable',['related'])
3165 " Partial Extraction {{{1
3167 function! s:Extract(bang,...) range abort
3168 if a:0 == 0 || a:0 > 1
3169 return s:error("Incorrect number of arguments")
3171 if a:1 =~ '[^a-z0-9_/.]'
3172 return s:error("Invalid partial name")
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'
3184 let file = "shared/" .file
3187 let curdir = s:sub(RailsFilePath(),'.*<app/views/layouts/(.*)%(\.\w*)$','app/views/\1')
3190 let curdir = fnamemodify(RailsFilePath(),':h')
3192 let curdir = rails_root."/".curdir
3193 let dir = fnamemodify(file,":h")
3194 let fname = fnamemodify(file,":t")
3195 if fnamemodify(fname,":e") == ""
3197 let fname .= ".".matchstr(expand("%:t"),'\.\zs.*')
3198 elseif fnamemodify(fname,":e") !~ '^'.s:viewspattern().'$'
3199 let name = fnamemodify(fname,":r")
3200 let fname .= ".".ext
3202 let name = fnamemodify(fname,":r:r")
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
3213 let out = (rails_root)."/app/views/".dir."/_".fname
3215 if filereadable(out) && !a:bang
3216 return s:error('E13: File exists (add ! to override)')
3218 if !isdirectory(fnamemodify(out,':h'))
3220 call mkdir(fnamemodify(out,':h'),'p')
3222 return s:error('No such directory')
3225 " No tabs, they'll just complicate things
3226 if ext =~? '^\%(rhtml\|erb\|dryml\)$'
3227 let erub1 = '\<\%\s*'
3228 let erub2 = '\s*-=\%\>'
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')
3242 let var = matchstr(collection,'^\k\+')
3243 let collection = s:sub(collection,'^\k+\>','')
3248 let fspaces = spaces
3250 let renderstr = "render :partial => '".fnamemodify(file,":r:r")."'"
3252 let renderstr .= ", :collection => ".collection
3253 elseif "@".name != var
3254 let renderstr .= ", :object => ".var
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(").")"
3261 let renderstr = "page << ".s:sub(renderstr,"render ","render(").")"
3262 elseif ext == "haml"
3263 let renderstr = "= ".renderstr
3265 let renderstr = "_".renderstr
3268 silent exe range."yank"
3274 silent exe "norm! :".first.",".last."change\<CR>".fspaces.renderstr."\<CR>.\<CR>"
3278 if renderstr =~ '<%'
3284 let shortout = fnamemodify(out,':.')
3285 silent split `=shortout`
3293 silent! exe '%substitute/^'.spaces.'//'
3295 silent! exe '%substitute?\%(\w\|[@:"'."'".'-]\)\@<!'.var.'\>?'.name.'?g'
3300 " Migration Inversion {{{1
3302 function! s:mkeep(str)
3303 " Things to keep (like comments) from a migration statement
3304 return matchstr(a:str,' #[^{].*')
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.'\}'),',$',')')
3311 return s:sub(s:sub(matchstr(a:str,'\w\+\>\zs\s*\%([^,){ ]*[, ]*\)\{,'.a:num.'\}'),'[, ]*$',''),'^\s+',' ')
3315 function! s:migspc(line)
3316 return matchstr(a:line,'^\s*')
3319 function! s:invertrange(beg,end)
3323 let line = getline(lnum)
3327 elseif line =~ '^\s*\(#[^{].*\)\=$'
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[^ ,)]*')
3342 let add = s:sub(add,'\)=$',', :name => '.mat.'&')
3344 let mat = matchstr(line,'\<add_index\>[^,]*,\s*\zs\%(\[[^]]*\]\|[:"'."'".']\w*["'."'".']\=\)')
3346 let add = s:sub(add,'\)=$',', :column => '.mat.'&')
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)
3372 let add = s:sub(line,'^\s*\zs.*','raise ActiveRecord::IrreversibleMigration')
3376 let str = add."\n".str
3379 let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversibleMigration\n)+','\1')
3383 function! s:Invert(bang)
3384 let err = "Could not parse method"
3387 let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
3388 let end = s:endof(beg)
3392 let beg = search('\%('.&l:define.'\).*'.src.'\>',"w")
3393 let end = s:endof(beg)
3398 let str = s:invertrange(beg+1,end-1)
3402 let beg = search('\%('.&l:define.'\).*'.dst.'\>',"w")
3403 let end = s:endof(beg)
3407 if foldclosed(beg) > 0
3411 exe (beg+1).",".(end-1)."delete _"
3422 let s:cache_prototype = {'dict': {}}
3424 function! s:cache_clear(...) dict
3427 elseif has_key(self,'dict') && has_key(self.dict,a:1)
3428 unlet! self.dict[a:1]
3432 function! rails#cache_clear(...)
3433 if exists('b:rails_root')
3434 return call(rails#app().cache.clear,a:000,rails#app().cache)
3438 function! s:cache_get(...) dict
3440 return self.dict[a:1]
3446 function! s:cache_has(key) dict
3447 return has_key(self.dict,a:key)
3450 function! s:cache_needs(key) dict
3451 return !has_key(self.dict,a:key)
3454 function! s:cache_set(key,value) dict
3455 let self.dict[a:key] = a:value
3458 call s:add_methods('cache', ['clear','needs','has','get','set'])
3460 let s:app_prototype.cache = s:cache_prototype
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
3475 function! s:helpermethods()
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 "
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 "
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')
3505 \ self.relglob("app/models/","**/*",".rb") +
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)
3512 return self.cache.get('user_classes')
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\\+")')
3522 call self.cache.set("user_assertions",assertions)
3524 return self.cache.get('user_assertions')
3527 call s:add_methods('app', ['user_classes','user_assertions'])
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'
3536 exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule"
3538 if buffer.type_name() == ''
3539 syn keyword rubyRailsMethod params request response session headers cookies flash
3541 if buffer.type_name('api')
3542 syn keyword rubyRailsAPIMethod api_method inflect_names
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
3555 if buffer.type_name('model-aro')
3556 syn keyword rubyRailsARMethod observe
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
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
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
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
3585 if buffer.type_name('test')
3586 if !empty(rails#app().user_assertions())
3587 exe "syn keyword rubyRailsUserMethod ".join(rails#app().user_assertions())
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
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
3608 if buffer.type_name('task')
3609 syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!'
3611 if buffer.type_name('model-awss')
3612 syn keyword rubyRailsMethod member
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
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
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
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
3637 unlet! g:html_no_rendering
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
3645 exe "syn sync minlines=" . g:ruby_minlines
3647 syn region rubyString matchgroup=rubyStringDelimiter start=+%Q\=<+ end=+>+ contains=@htmlTop,@rubyStringSpecial
3648 syn cluster htmlArgCluster add=@rubyStringSpecial
3649 syn cluster htmlPreProc add=@rubyStringSpecial
3651 elseif &syntax =~# '^eruby\>' || &syntax == 'haml'
3654 exe 'syn keyword '.&syntax.'RailsUserClass '.classes.' contained containedin=@'.&syntax.'RailsRegions'
3656 if &syntax == 'haml'
3657 exe 'syn cluster hamlRailsRegions contains=hamlRubyCodeIncluded,hamlRubyCode,hamlRubyHash,@hamlEmbeddedRuby,rubyInterpolation'
3659 exe 'syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation'
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'
3669 exe 'syn keyword '.&syntax.'RailsRenderMethod render contained containedin=@'.&syntax.'RailsRegions'
3670 exe 'syn case match'
3672 exe 'syn keyword javascriptRailsFunction contained '.s:javascript_functions
3673 exe 'syn cluster htmlJavaScript add=javascriptRailsFunction'
3674 elseif &syntax == "yaml"
3676 " Modeled after syntax/eruby.vim
3677 unlet! b:current_syntax
3678 let g:main_syntax = 'eruby'
3679 syn include @rubyTop syntax/ruby.vim
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
3688 exe "syn keyword yamlRailsUserClass ".classes." contained containedin=@yamlRailsRegions"
3690 let b:current_syntax = "yaml"
3691 elseif &syntax == "html"
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.
3700 exe "syn keyword javascriptRailsFunction ".s:javascript_functions
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
3754 function! rails#log_syntax()
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
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
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
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>
3812 if !hasmapto("<Plug>RailsFind")
3813 nmap <buffer> gf <Plug>RailsFind
3815 if !hasmapto("<Plug>RailsSplitFind")
3816 nmap <buffer> <C-W>f <Plug>RailsSplitFind
3818 if !hasmapto("<Plug>RailsTabFind")
3819 nmap <buffer> <C-W>gf <Plug>RailsTabFind
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
3827 " SelectBuf you're a dirty hack
3834 function! s:extractdbvar(str,arg)
3835 return matchstr("\n".a:str."\n",'\n'.a:arg.'=\zs.\{-\}\ze\n')
3838 function! s:app_dbext_settings(environment) dict
3839 if self.cache.needs('dbext_settings')
3840 call self.cache.set('dbext_settings',{})
3842 let cache = self.cache.get('dbext_settings')
3843 if !has_key(cache,a:environment)
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='
3858 let dict['extra'] = ''
3860 let dict['dbname'] = s:extractdbvar(out,'database')
3861 if dict['dbname'] == ''
3862 let dict['dbname'] = s:extractdbvar(out,'dbfile')
3864 if dict['dbname'] != '' && dict['dbname'] !~ '^:' && adapter =~? '^sqlite'
3865 let dict['dbname'] = self.path(dict['dbname'])
3867 let dict['profile'] = ''
3869 let dict['srvname'] = s:extractdbvar(out,'database')
3871 let dict['srvname'] = s:extractdbvar(out,'host')
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
3880 let dict['host'] = matchstr(dict['host'],'\c\<\%(Server\|Data Source\)\s*=\s*\zs[^;]*')
3882 call filter(dict,'v:val != ""')
3884 let cache[a:environment] = dict
3886 return cache[a:environment]
3889 function! s:BufDatabase(...)
3890 if exists("s:lock_database") || !exists('g:loaded_dbext') || !exists('b:rails_root')
3893 let self = rails#app()
3894 let s:lock_database = 1
3896 call self.cache.clear('dbext_settings')
3898 if (a:0 > 1 && a:2 != '')
3901 let env = s:environment()
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
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,'')
3911 if b:dbext_type == 'PGSQL'
3912 let $PGPASSWORD = b:dbext_passwd
3913 elseif exists('$PGPASSWORD')
3914 let $PGPASSWORD = ''
3916 unlet! s:lock_database
3919 call s:add_methods('app', ['dbext_settings'])
3922 " Abbreviations {{{1
3924 function! s:selectiveexpand(pat,good,default,...)
3930 let c = nr2char(getchar(0))
3933 return s:sub(good.(a:0 ? " ".a:1 : ''),'\s+$','')
3935 return good.(a:0 ? " ".a:1 : '')
3937 return good.c.(a:0 ? a:1 : '')
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')
3952 return s:selectiveexpand('..',':conditions => ',':c')
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>')
3960 exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).",\"".expn2."\")<CR>"
3962 exe "inoreabbrev <buffer> <silent> ".a:abbr." <C-R>=<SID>selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).")<CR>"
3966 function! s:AddTabExpand(abbr,expn)
3967 call s:AddSelectiveExpand(a:abbr,'..',a:expn)
3970 function! s:AddBracketExpand(abbr,expn)
3971 call s:AddSelectiveExpand(a:abbr,'[[.]',a:expn)
3974 function! s:AddColonExpand(abbr,expn)
3975 call s:AddSelectiveExpand(a:abbr,'[:.]',a:expn)
3978 function! s:AddParenExpand(abbr,expn,...)
3980 call s:AddSelectiveExpand(a:abbr,'(',a:expn,a:1)
3982 call s:AddSelectiveExpand(a:abbr,'(',a:expn,'')
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')
3995 Rabbrev rs[ response
3998 Rabbrev coo[ cookies
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\ =>\
4011 if buffer.type_name('view','helper')
4012 Rabbrev dotiw distance_of_time_in_words
4013 Rabbrev taiw time_ago_in_words
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
4021 if buffer.type_name() ==# 'model' || buffer.type_name('model-arb')
4022 Rabbrev bt( belongs_to
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
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
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
4056 Rabbrev :a :action\ =>\
4058 Rabbrev :c :co________\ =>\
4059 inoreabbrev <buffer> <silent> :c <C-R>=<SID>TheCWord()<CR>
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
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
4080 function! s:Abbrev(bang,...) abort
4081 if !exists("b:rails_abbreviations")
4082 let b:rails_abbreviations = {}
4084 if a:0 > 3 || (a:bang && (a:0 != 1))
4085 return s:error("Rabbrev: invalid arguments")
4088 for key in sort(keys(b:rails_abbreviations))
4089 echo key . join(b:rails_abbreviations[key],"\t")
4094 let root = s:sub(lhs,'%(::|\(|\[)$','')
4096 if has_key(b:rails_abbreviations,root)
4097 call remove(b:rails_abbreviations,root)
4099 exe "iunabbrev <buffer> ".root
4102 if a:0 > 3 || a:0 < 2
4103 return s:error("Rabbrev: invalid arguments")
4106 if has_key(b:rails_abbreviations,root)
4107 call remove(b:rails_abbreviations,root)
4110 let b:rails_abbreviations[root] = ["(", rhs . (a:0 > 2 ? "\t".a:3 : "")]
4112 call s:AddParenExpand(root,rhs,a:3)
4114 call s:AddParenExpand(root,rhs)
4119 return s:error("Rabbrev: invalid arguments")
4122 call s:AddColonExpand(root,rhs)
4124 call s:AddBracketExpand(root,rhs)
4126 call s:AddTabExpand(lhs,rhs)
4128 return s:error("Rabbrev: unimplemented")
4130 let b:rails_abbreviations[root] = [matchstr(lhs,'\W*$'),rhs]
4136 function! s:Set(bang,...)
4140 if arg =~? '^<[abgl]\=>$'
4141 let defscope = (matchstr(arg,'<\zs.*\ze>'))
4143 if defscope != '' && arg !~ '^\w:'
4144 let arg = defscope.':'.opt
4146 let val = s:getopt(arg)
4147 if val == '' && !has_key(s:opts(),arg)
4148 call s:error("No such rails.vim option: ".arg)
4153 let opt = matchstr(arg,'[^=]*')
4154 let val = s:sub(arg,'^[^=]*\=','')
4155 if defscope != '' && opt !~ '^\w:'
4156 let opt = defscope.':'.opt
4158 call s:setopt(opt,val)
4163 function! s:getopt(opt,...)
4164 let app = rails#app()
4168 elseif opt =~ '^[abgl]:'
4169 let scope = tolower(matchstr(opt,'^\w'))
4170 let opt = s:sub(opt,'^\w:','')
4174 let lnum = a:0 > 1 ? a:2 : line('.')
4175 if scope =~ 'l' && &filetype != 'ruby'
4176 let scope = s:sub(scope,'l','b')
4179 call s:LocalModelines(lnum)
4181 let var = s:sname().'_'.opt
4182 let lastmethod = s:lastmethod(lnum)
4183 if lastmethod == '' | let lastmethod = ' ' | endif
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 == ' '))
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)
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:','')
4207 let defscope = get(s:opts(),opt,'a')
4209 let scope = defscope
4211 if &filetype != 'ruby' && (scope ==# 'B' || scope ==# 'l')
4214 let var = s:sname().'_'.opt
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'
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'
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
4232 return s:error("Invalid scope for ".a:opt)
4237 return {'alternate': 'b', 'controller': 'b', 'gnu_screen': 'a', 'model': 'b', 'preview': 'l', 'task': 'b', 'related': 'l', 'root_url': 'a'}
4240 function! s:Complete_set(A,L,P)
4242 let opt = matchstr(a:A,'[^=]*')
4243 return [opt."=".s:getopt(opt)]
4245 let extra = matchstr(a:A,'^[abgl]:')
4246 return filter(sort(map(keys(s:opts()),'extra.v:val')),'s:startswith(v:val,a:A)')
4251 function! s:BufModelines()
4252 if !g:rails_modelines
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\|#{\@!\|%>\|-->\|$\)'
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,'\|','\\|')
4264 silent! exe "Rset <B> ".mat
4266 let mat = matchstr(lines,'\C\<Rset'.pat,matend)
4267 let matend = matchend(lines,'\C\<Rset'.pat,matend)
4272 function! s:LocalModelines(lnum)
4273 if !g:rails_modelines
4276 let lbeg = s:lastmethodline(a:lnum)
4277 let lend = s:endof(lbeg)
4278 if lbeg == 0 || lend == 0
4283 while lnum < lend && lnum < lbeg + 5
4284 let lines .= getline(lnum) . "\n"
4287 let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|%>\|-->\|$\)'
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,'\|','\\|')
4295 silent! exe "Rset <l> ".mat
4297 let mat = matchstr(lines,'\C\<rset'.pat,matend)
4298 let matend = matchend(lines,'\C\<rset'.pat,matend)
4306 function! s:app_source_callback(file) dict
4307 if self.cache.needs('existence')
4308 call self.cache.set('existence',{})
4310 let cache = self.cache.get('existence')
4311 if !has_key(cache,a:file)
4312 let cache[a:file] = self.has_file(a:file)
4315 sandbox source `=self.path(a:file)`
4319 call s:add_methods('app',['source_callback'])
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
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 = ""
4340 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
4342 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
4344 let path = fnamemodify(path,':p:~:h')
4345 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,path)
4347 let g:RAILS_HISTORY = s:scrub(g:RAILS_HISTORY,s:gsub(path,'\\','/'))
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.*','')
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
4358 " Activate custom syntax
4359 let &syntax = &syntax
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
4369 silent %s/\%(\e\[[0-9;]*m\|\r$\)//ge
4371 setlocal readonly nomodifiable
4374 call s:BufSettings()
4375 call s:BufCommands()
4376 call s:BufAbbreviations()
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"
4383 let t = rails#buffer().type_name()
4385 let f = '/'.RailsFilePath()
4389 runtime! macros/rails.vim
4390 silent doautocmd User Rails
4392 exe "silent doautocmd User Rails".s:gsub(t,'-','.')
4395 exe "silent doautocmd User Rails".f
4397 call app.source_callback("config/rails.vim")
4398 call s:BufModelines()
4399 call s:BufMappings()
4403 function! s:SetBasePath()
4404 let self = rails#buffer()
4405 if self.app().path() =~ '://'
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)')
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']
4417 if self.app().has('test')
4418 let path += ['test', 'test/unit', 'test/functional', 'test/integration']
4420 if self.app().has('spec')
4421 let path += ['spec', 'spec/models', 'spec/controllers', 'spec/helpers', 'spec/views', 'spec/lib', 'spec/requests', 'spec/integration']
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))
4428 function! s:BufSettings()
4429 if !exists('b:rails_root')
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'
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"
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')
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")
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)
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)
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)
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)
4495 if ft =~# '^eruby\>' || ft ==# 'yaml'
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
4504 if self.getvar('surround_45') == '' || self.getvar('surround_45') == "<% \r %>" " -
4505 call self.setvar('surround_45', "<% \r -%>")
4507 if self.getvar('surround_61') == '' " =
4508 call self.setvar('surround_61', "<%= \r %>")
4510 if self.getvar("surround_35") == '' " #
4511 call self.setvar('surround_35', "<%# \r %>")
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 -%>")
4525 augroup railsPluginAuto
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()
4545 " Initialization {{{1
4548 let s:sid = s:sub(maparg("<SID>xx"),'xx$','')
4550 let s:file = expand('<sfile>:p')
4552 if !exists('s:apps')
4558 let &cpo = s:cpo_save
4560 " vim:set sw=2 sts=2: