" rails.vim - Detect a rails application " Author: Tim Pope " GetLatestVimScripts: 1567 1 :AutoInstall: rails.vim " URL: http://rails.vim.tpope.net/ " $Id: rails.vim 222 2007-11-02 14:02:26Z tpope $ " See doc/rails.txt for details. Grab it from the URL above if you don't have it " To access it from Vim, see :help add-local-help (hint: :helptags ~/.vim/doc) " Afterwards, you should be able to do :help rails " ============================================================================ " Exit quickly when: " - this plugin was already loaded (or disabled) " - when 'compatible' is set if &cp || (exists("g:loaded_rails") && g:loaded_rails) && !(exists("g:rails_debug") && g:rails_debug) finish endif let g:loaded_rails = 1 let s:cpo_save = &cpo set cpo&vim " Utility Functions {{{1 function! s:sub(str,pat,rep) return substitute(a:str,'\v\C'.a:pat,a:rep,'') endfunction function! s:gsub(str,pat,rep) return substitute(a:str,'\v\C'.a:pat,a:rep,'g') endfunction function! s:string(str) if exists("*string") return string(a:str) else return "'" . s:gsub(a:str,"'","'.\"'\".'") . "'" endif endfunction function! s:compact(ary) return s:sub(s:sub(s:gsub(a:ary,'\n\n+','\n'),'\n$',''),'^\n','') endfunction function! s:escarg(p) return s:gsub(a:p,'[ !%#]','\\&') endfunction function! s:esccmd(p) return s:gsub(a:p,'[!%#]','\\&') endfunction function! s:ra() " Rails root, escaped for use as single argument return s:escarg(RailsRoot()) endfunction function! s:rc() " Rails root, escaped for use with a command (spaces not escaped) return s:esccmd(RailsRoot()) endfunction function! s:escvar(r) let r = fnamemodify(a:r,':~') let r = s:gsub(r,'\W','\="_".char2nr(submatch(0))."_"') let r = s:gsub(r,'^\d','_&') return r endfunction function! s:rv() " Rails root, escaped to be a variable name return s:escvar(RailsRoot()) endfunction function! s:rquote(str) " Imperfect but adequate for Ruby arguments if a:str =~ '^[A-Za-z0-9_/.:-]\+$' return a:str elseif &shell =~? 'cmd' return '"'.s:gsub(s:gsub(a:str,'\','\\'),'"','\\"').'"' else return "'".s:gsub(s:gsub(a:str,'\','\\'),"'","'\\\\''")."'" endif endfunction function! s:sname() return fnamemodify(s:file,':t:r') endfunction function! s:hasfile(file) return filereadable(RailsRoot().'/'.a:file) endfunction function! s:rubyexestr(cmd) if RailsRoot() =~ '://' return "ruby ".a:cmd else return "ruby -C ".s:rquote(RailsRoot())." ".a:cmd endif endfunction function! s:rubyexestrwithfork(cmd) if s:getopt("ruby_fork_port","ab") && executable("ruby_fork_client") return "ruby_fork_client -p ".s:getopt("ruby_fork_port","ab")." ".a:cmd else return s:rubyexestr(a:cmd) endif endfunction function! s:rubyexebg(cmd) if has("gui_win32") exe "!start ".s:esccmd(s:rubyexestr(a:cmd)) elseif exists("$STY") && !has("gui_running") && s:getopt("gnu_screen","abg") && executable("screen") silent exe "!screen -ln -fn -t ".s:sub(s:sub(a:cmd,'\s.*',''),'^%(script|-rcommand)/','rails-').' '.s:esccmd(s:rubyexestr(a:cmd)) else exe "!".s:esccmd(s:rubyexestr(a:cmd)) endif return v:shell_error endfunction function! s:rubyexe(cmd,...) if a:0 call s:rubyexebg(a:cmd) else exe "!".s:esccmd(s:rubyexestr(a:cmd)) endif return v:shell_error endfunction function! s:rubyeval(ruby,...) if a:0 > 0 let def = a:1 else let def = "" endif if !executable("ruby") return def endif let cmd = s:rubyexestr('-e '.s:rquote('begin; require %{rubygems}; rescue LoadError; end; begin; require %{active_support}; rescue LoadError; end; '.a:ruby)) "let g:rails_last_ruby_command = cmd " If the shell is messed up, this command could cause an error message silent! let results = system(cmd) "let g:rails_last_ruby_result = results if v:shell_error != 0 " results =~ '-e:\d' || results =~ 'ruby:.*(fatal)' return def else return results endif endfunction function! s:railseval(ruby) if a:0 > 0 let def = a:1 else let def = "" endif if !executable("ruby") return def endif let args = "-r./config/boot -r ".s:rquote(RailsRoot()."/config/environment")." -e ".s:rquote(a:ruby) let cmd = s:rubyexestrwithfork(args) " If the shell is messed up, this command could cause an error message silent! let results = system(cmd) if v:shell_error != 0 " results =~ '-e:\d' || results =~ 'ruby:.*(fatal)' return def else return results endif endfunction function! s:endof(lnum) if a:lnum == 0 return 0 endif if &ft == "yaml" || expand("%:e") == "yml" return -1 endif let cline = getline(a:lnum) let spc = matchstr(cline,'^\s*') let endpat = '\' if matchstr(getline(a:lnum+1),'^'.spc) && !matchstr(getline(a:lnum+1),'^'.spc.endpat) && matchstr(cline,endpat) return a:lnum endif let endl = a:lnum while endl <= line('$') let endl = endl + 1 if getline(endl) =~ '^'.spc.endpat return endl elseif getline(endl) =~ '^=begin\>' while getline(endl) !~ '^=end\>' && endl <= line('$') let endl = endl + 1 endwhile let endl = endl + 1 elseif getline(endl) !~ '^'.spc && getline(endl) !~ '^\s*\%(#.*\)\=$' return 0 endif endwhile return 0 endfunction function! s:lastmethodline(...) if a:0 let line = a:1 else let line = line(".") endif while line > 0 && getline(line) !~ &l:define let line = line - 1 endwhile let lend = s:endof(line) if lend < 0 || lend >= (a:0 ? a:1 : line(".")) return line else return 0 endif endfunction function! s:lastmethod() let line = s:lastmethodline() if line return s:sub(matchstr(getline(line),'\%('.&define.'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','') else return "" endif endfunction function! s:lastrespondtoline(...) let mline = s:lastmethodline() if a:0 let line = a:1 else let line = line(".") endif while line > mline && getline(line) !~ '\C^\s*respond_to\s*\%(\= (a:0 ? a:1 : line(".")) return line else return -1 endif endfunction function! s:lastformat() let rline = s:lastrespondtoline() if rline let variable = matchstr(getline(rline),'\C^\s*respond_to\s*\%(\ rline let match = matchstr(getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*') if match != '' return match endif let line = line - 1 endwhile endif return "" endfunction function! s:format(...) if RailsFileType() =~ '^view\>' let format = fnamemodify(RailsFilePath(),':r:e') else let format = s:lastformat() endif if format == '' if fnamemodify(RailsFilePath(),':e') == 'rhtml' let format = 'html' elseif fnamemodify(RailsFilePath(),':e') == 'rxml' let format = 'xml' elseif fnamemodify(RailsFilePath(),':e') == 'rjs' let format = 'js' elseif a:0 return a:1 endif endif return format endfunction function! s:viewspattern() return '\%('.s:gsub(s:view_types,',','\\|').'\)' endfunction function! s:controller(...) let t = RailsFileType() let f = RailsFilePath() let o = s:getopt("controller","lb") if o != "" return o elseif f =~ '\' return s:sub(f,'.*' let r = "controller-api" else let r = "controller" endif elseif f =~ '_api\.rb' let r = "api" elseif f =~ '\' let class = matchstr(top,'\') if class == "ActiveResoure::Base" let class = "ares" let r = "model-ares" elseif class != '' "let class = s:sub(class,'::Base$','') let class = tolower(s:gsub(class,'[^A-Z]','')) let r = "model-".s:sub(class,'^amb>','mailer') elseif f =~ '_mailer\.rb$' let r = "model-mailer" elseif top =~ '\<\%(validates_\w\+_of\|set_\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>' let r = "model-arb" else let r = "model" endif elseif f =~ '\.*\.' let r = "view-layout-" . e elseif f =~ '\<\%(app/views\|components\)/.*/_\k\+\.\k\+$' let r = "view-partial-" . e elseif f =~ '\.*\.' || f =~ '\' if e == "yml" let r = "fixtures-yaml" else let r = "fixtures" . (e == "" ? "" : "-" . e) endif elseif f =~ '\' || f=~ '\.*\.rb$' let r = "config-routes" elseif f =~ '\0,) Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:PreviewComplete Rpreview :call s:Preview(0,) Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:environments Rlog :call s:Log(0,) Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:SetComplete Rset :call s:Set(0,) command! -buffer -bar -nargs=0 Rtags :call s:Tags(0) " Embedding all this logic directly into the command makes the error " messages more concise. command! -buffer -bar -nargs=? -bang Rdoc : \ if 0 || =~ "^\\([:'-]\\|g:\\)" | call s:prephelp() | \ if =~ '^-\=$' | help rails | \ elseif =~ '^g:' | help | \ elseif =~ '^-' | help rails | \ else | help rails- | endif | \ else | call s:Doc(0,) | endif command! -buffer -bar -nargs=0 -bang Rrefresh :if 0|unlet! g:loaded_rails|source `=s:file`|endif|call s:Refresh(0) if exists(":Project") command! -buffer -bar -nargs=? -bang Rproject :call s:Project(0,) endif if exists("g:loaded_dbext") Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:environments Rdbext :call s:BufDatabase(2,,0) endif let ext = expand("%:e") if ext =~ s:viewspattern() " TODO: complete controller names with trailing slashes here Rcommand! -buffer -bar -nargs=? -range -complete=custom,s:controllerList Rextract :,call s:Extract(0,) command! -buffer -bar -nargs=? -range Rpartial :call s:warn("Warning: :Rpartial has been deprecated in favor of :Rextract") | ,Rextract endif if RailsFilePath() =~ '\0) endif endfunction function! s:Doc(bang, string) if a:string != "" if exists("g:rails_search_url") let query = substitute(a:string,'[^A-Za-z0-9_.~-]','\="%".printf("%02X",char2nr(submatch(0)))','g') let url = printf(g:rails_search_url, query) else return s:error("specify a g:rails_search_url with %s for a query placeholder") endif elseif isdirectory(RailsRoot()."/doc/api/classes") let url = RailsRoot()."/doc/api/index.html" elseif s:getpidfor("0.0.0.0","8808") > 0 let url = "http://localhost:8808" else let url = "http://api.rubyonrails.org" endif call s:initOpenURL() if exists(":OpenURL") exe "OpenURL ".s:escarg(url) else return s:error("No :OpenURL command found") endif endfunction function! s:Log(bang,arg) if a:arg == "" let lf = "log/".s:environment().".log" else let lf = "log/".a:arg.".log" endif let size = getfsize(RailsRoot()."/".lf) if size >= 1048576 call s:warn("Log file is ".((size+512)/1024)."KB. Consider :Rake log:clear") endif if a:bang exe "cgetfile ".lf clast else if exists(":Tail") " TODO: check if :Tail works with `=` exe "Tail ".s:ra().'/'.lf else "exe "pedit ".s:ra().'/'.lf pedit `=RailsRoot().'/'.lf` endif endif endfunction function! s:NewApp(bang,...) if a:0 == 0 if a:bang echo "rails.vim revision ".s:revision else !rails endif return endif let dir = "" if a:1 !~ '^-' let dir = a:1 elseif a:{a:0} =~ '[\/]' let dir = a:{a:0} else let dir = a:1 endif let str = "" let c = 1 while c <= a:0 let str = str . " " . s:rquote(expand(a:{c})) let c = c + 1 endwhile "let str = s:sub(str,'^ ','') let dir = expand(dir) if isdirectory(fnamemodify(dir,':h')."/.svn") && g:rails_subversion let append = " -c" else let append = "" endif if g:rails_default_database != "" && str !~ '-d \|--database=' let append = append." -d ".g:rails_default_database endif if a:bang let append = append." --force" endif exe "!rails".append.str if filereadable(dir."/".g:rails_default_file) "exe "edit ".s:escarg(dir)."/".g:rails_default_file edit `=dir.'/'.g:rails_default_file` endif endfunction function! s:Tags(bang) if exists("g:Tlist_Ctags_Cmd") let cmd = g:Tlist_Ctags_Cmd elseif executable("exuberant-ctags") let cmd = "exuberant-ctags" elseif executable("ctags-exuberant") let cmd = "ctags-exuberant" elseif executable("ctags") let cmd = "ctags" elseif executable("ctags.exe") let cmd = "ctags.exe" else return s:error("ctags not found") endif exe "!".cmd." -R ".s:ra() endfunction function! s:Refresh(bang) " What else? if a:bang unlet! s:rails_helper_methods endif if exists("g:rubycomplete_rails") && g:rubycomplete_rails && has("ruby") silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) silent! ruby Dependencies.clear if defined?(Dependencies) if a:bang silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) endif endif call s:cacheclear() call s:BufLeave() if a:bang && s:cacheworks() let s:cache = {} endif let i = 1 let max = bufnr('$') while i <= max let rr = getbufvar(i,"rails_root") if rr != "" unlet! s:user_classes_{s:escvar(rr)} unlet! s:dbext_type_{s:escvar(rr)} call setbufvar(i,"rails_refresh",1) endif let i = i + 1 endwhile call s:BufEnter() endfunction " }}}1 " Rake {{{1 " Depends: s:efm, s:rubyexestrwithfork, s:sub, s:lastmethodline, s:getopt, s;rquote, s:QuickFixCmdPre, ... let s:efm_backtrace='%D(in\ %f),' \.'%\\s%#from\ %f:%l:%m,' \.'%\\s#{RAILS_ROOT}/%f:%l:\ %#%m,' \.'%\\s%#[%f:%l:\ %#%m,' \.'%\\s%#%f:%l:\ %#%m' function! s:makewithruby(arg,...) if &efm == s:efm if a:0 ? a:1 : 1 setlocal efm=\%-E-e:%.%#,\%+E%f:%l:\ parse\ error,%W%f:%l:\ warning:\ %m,%E%f:%l:in\ %*[^:]:\ %m,%E%f:%l:\ %m,%-C%\tfrom\ %f:%l:in\ %.%#,%-Z%\tfrom\ %f:%l,%-Z%p^,%-G%.%# endif endif let old_make = &makeprg let &l:makeprg = s:rubyexestrwithfork(a:arg) make let &l:makeprg = old_make endfunction function! s:Rake(bang,arg) let oldefm = &efm if a:bang let &efm = s:efm_backtrace "errorformat=%*[^"]"%f"%*\D%l: %m,"%f"%*\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,"%f"\, line %l%*\D%c%*[^ ] %m,%D%*\a[%*\d]: Entering directory `%f',%X%*\a[%*\d]: Leaving directory `%f',%D%*\a: Entering directory `% endif let t = RailsFileType() let arg = a:arg if &filetype == "ruby" && arg == '' && g:rails_modelines let lnum = s:lastmethodline() let str = getline(lnum)."\n".getline(lnum+1)."\n".getline(lnum+2)."\n" let pat = '\s\+\zs.\{-\}\ze\%(\n\|\s\s\|#{\@!\|$\)' let mat = matchstr(str,'#\s*rake'.pat) let mat = s:sub(mat,'\s+$','') if mat != "" let arg = mat endif endif if arg == '' let opt = s:getopt('task','bl') if opt != '' let arg = opt endif endif let withrubyargs = '-r ./config/boot -r '.s:rquote(RailsRoot().'/config/environment').' -e "puts \%((in \#{Dir.getwd}))" ' if arg =~# '^\%(stats\|routes\|notes\|db:\%(charset\|collation\|version\)\)\%(:\|$\)' " So you can see the output even with an inadequate redirect call s:QuickFixCmdPre() exe "!".&makeprg." ".arg call s:QuickFixCmdPost() elseif arg =~ '^preview\>' exe 'R'.s:gsub(arg,':','/') elseif arg =~ '^runner:' let arg = s:sub(arg,'^runner:','') let root = matchstr(arg,'%\%(:\w\)*') let file = expand(root).matchstr(arg,'%\%(:\w\)*\zs.*') if file =~ '[@#].*$' let extra = " -- -n ".matchstr(file,'[@#]\zs.*') let file = s:sub(file,'[@#].*','') else let extra = '' endif if s:hasfile(file) || s:hasfile(file.'.rb') call s:makewithruby(withrubyargs.'-r"'.file.'"'.extra,file !~# '_\%(spec\|test\)\%(\.rb\)\=$') else call s:makewithruby(withrubyargs.'-e '.s:esccmd(s:rquote(arg))) endif elseif arg == 'run' || arg == 'runner' call s:makewithruby(withrubyargs.'-r"'.RailsFilePath().'"',RailsFilePath() !~# '_\%(spec\|test\)\%(\.rb\)\=$') elseif arg =~ '^run:' let arg = s:sub(arg,'^run:','') let arg = s:sub(arg,'^%:h',expand('%:h')) let arg = s:sub(arg,'^%(\%|$|[@#]@=)',expand('%')) let arg = s:sub(arg,'[@#](\w+)$',' -- -n\1') call s:makewithruby(withrubyargs.'-r'.arg,arg !~# '_\%(spec\|test\)\.rb$') elseif arg != '' exe 'make '.arg elseif t =~ '^task\>' let lnum = s:lastmethodline() let line = getline(lnum) " We can't grab the namespace so only run tasks at the start of the line if line =~ '^\%(task\|file\)\>' exe 'make '.s:lastmethod() else make endif elseif t =~ '^spec\>' if RailsFilePath() =~# '\' let meth = s:lastmethod() if meth =~ '^test_' let call = " -n".meth."" else let call = "" endif if t =~ '^test-\%(unit\|functional\|integration\)$' exe "make ".s:sub(s:gsub(t,'-',':'),'unit$|functional$','&s')." TEST=\"%:p\"".s:sub(call,'^ ',' TESTOPTS=') elseif RailsFilePath() =~# '\' && RailsFilePath() !~# '\' make test:units TEST="%:p:r:s?[\/]app[\/]apis[\/]?/test/functional/?_test.rb" elseif t=~ '^\<\%(controller\|helper\|view\)\>' if RailsFilePath() =~ '\ elseif has("gui_win32") command -bar -nargs=1 OpenURL :!start cmd /cstart /b elseif executable("sensible-browser") command -bar -nargs=1 OpenURL :!sensible-browser endif endif endfunction " This returns the URI with a trailing newline if it is found function! s:scanlineforuri(lnum) let line = getline(a:lnum) let url = matchstr(line,"\\v\\C%(%(GET|PUT|POST|DELETE)\\s+|\w+:/)/[^ \n\r\t<>\"]*[^] .,;\n\r\t<>\":]") if url =~ '\C^\u\+\s\+' let method = matchstr(url,'^\u\+') let url = matchstr(url,'\s\+\zs.*') if method !=? "GET" if url =~ '?' let url = url.'&' else let url = url.'?' endif let url = url.'_method='.tolower(method) endif endif if url != "" return s:sub(url,'^/','') . "\n" else return "" endif endfunction function! s:defaultpreview() let ret = '' if s:getopt('preview','l') != '' let uri = s:getopt('preview','l') elseif s:controller() != '' && s:controller() != 'application' && RailsFilePath() !~ '^public/' if RailsFileType() =~ '^controller\>' let start = s:lastmethodline() - 1 if start + 1 while getline(start) =~ '^\s*\%(#.*\)\=$' let ret = s:scanlineforuri(start).ret let start = start - 1 endwhile let ret = ret.s:controller().'/'.s:lastmethod().'/' else let ret = ret.s:controller().'/' endif elseif s:getopt('preview','b') != '' let ret = s:getopt('preview','b') elseif RailsFileType() =~ '^view\%(-partial\|-layout\)\@!' let ret = ret.expand('%:t:r').'/' endif elseif s:getopt('preview','b') != '' let uri = s:getopt('preview','b') elseif RailsFilePath() =~ '^public/' let ret = s:sub(RailsFilePath(),'^public/','') elseif s:getopt('preview','ag') != '' let ret = s:getopt('preview','ag') endif return ret endfunction function! s:Preview(bang,arg) let root = s:getopt("root_url") if root == '' let root = s:getopt("url") endif let root = s:sub(root,'/$','') if a:arg =~ '://' let uri = a:arg elseif a:arg != '' let uri = root.'/'.s:sub(a:arg,'^/','') else let uri = matchstr(s:defaultpreview(),'.\{-\}\%(\n\@=\|$\)') let uri = root.'/'.s:sub(s:sub(uri,'^/',''),'/$','') endif call s:initOpenURL() if exists(':OpenURL') && !a:bang exe 'OpenURL '.uri else " Work around bug where URLs ending in / get handled as FTP let url = uri.(uri =~ '/$' ? '?' : '') silent exe 'pedit '.url wincmd w if &filetype == '' if uri =~ '\.css$' setlocal filetype=css elseif uri =~ '\.js$' setlocal filetype=javascript elseif getline(1) =~ '^\s*<' setlocal filetype=xhtml endif endif call s:Detect(RailsRoot()) map q :bwipe wincmd p if !a:bang call s:warn("Define a :OpenURL command to use a browser") endif endif endfunction function! s:PreviewComplete(A,L,P) return s:defaultpreview() endfunction " }}}1 " Script Wrappers {{{1 " Depends: s:rquote, s:rubyexebg, s:rubyexe, s:rubyexestrwithfork, s:sub, s:getopt, s:usesubversion, s:user_classes_..., ..., s:pluginList, ... function! s:BufScriptWrappers() Rcommand! -buffer -bar -nargs=+ -complete=custom,s:ScriptComplete Rscript :call s:Script(0,) Rcommand! -buffer -bar -nargs=* -complete=custom,s:ConsoleComplete Rconsole :call s:Console(0,'console',) "Rcommand! -buffer -bar -nargs=* Rbreakpointer :call s:Console(0,'breakpointer',) Rcommand! -buffer -bar -nargs=* -complete=custom,s:GenerateComplete Rgenerate :call s:Generate(0,) Rcommand! -buffer -bar -nargs=* -complete=custom,s:DestroyComplete Rdestroy :call s:Destroy(0,) Rcommand! -buffer -bar -nargs=? -bang -complete=custom,s:ServerComplete Rserver :call s:Server(0,) Rcommand! -buffer -bang -nargs=1 -range=0 -complete=custom,s:RubyComplete Rrunner :call s:Runner(0 ? -2 : (==?:-1),) Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Rp :call s:Runner(==?:-1,'p begin '..' end') Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Rpp :call s:Runner(==?:-1,'require %{pp}; pp begin '..' end') Rcommand! -buffer -nargs=1 -range=0 -complete=custom,s:RubyComplete Ry :call s:Runner(==?:-1,'y begin '..' end') endfunction function! s:Script(bang,cmd,...) let str = "" let c = 1 while c <= a:0 let str = str . " " . s:rquote(a:{c}) let c = c + 1 endwhile if a:bang call s:rubyexebg(s:rquote("script/".a:cmd).str) else call s:rubyexe(s:rquote("script/".a:cmd).str) endif endfunction function! s:Runner(count,args) if a:count == -2 call s:Script(a:bang,"runner",a:args) else let str = s:rubyexestrwithfork('-r./config/boot -e "require '."'commands/runner'".'" '.s:rquote(a:args)) let res = s:sub(system(str),'\n$','') if a:count < 0 echo res else exe a:count.'put =res' endif endif endfunction function! s:Console(bang,cmd,...) let str = "" let c = 1 while c <= a:0 let str = str . " " . s:rquote(a:{c}) let c = c + 1 endwhile call s:rubyexebg(s:rquote("script/".a:cmd).str) endfunction function! s:getpidfor(bind,port) if has("win32") || has("win64") let netstat = system("netstat -anop tcp") let pid = matchstr(netstat,'\<'.a:bind.':'.a:port.'\>.\{-\}LISTENING\s\+\zs\d\+') elseif executable('lsof') let pid = system("lsof -i 4tcp@".a:bind.':'.a:port."|grep LISTEN|awk '{print $2}'") let pid = s:sub(pid,'\n','') else let pid = "" endif return pid endfunction function! s:Server(bang,arg) let port = matchstr(a:arg,'\%(-p\|--port=\=\)\s*\zs\d\+') if port == '' let port = "3000" endif " TODO: Extract bind argument let bind = "0.0.0.0" if a:bang && executable("ruby") let pid = s:getpidfor(bind,port) if pid =~ '^\d\+$' echo "Killing server with pid ".pid if !has("win32") call system("ruby -e 'Process.kill(:TERM,".pid.")'") sleep 100m endif call system("ruby -e 'Process.kill(9,".pid.")'") sleep 100m endif if a:arg == "-" return endif endif if has("win32") || has("win64") || (exists("$STY") && !has("gui_running") && s:getopt("gnu_screen","abg") && executable("screen")) call s:rubyexebg(s:rquote("script/server")." ".a:arg) else "--daemon would be more descriptive but lighttpd does not support it call s:rubyexe(s:rquote("script/server")." ".a:arg." -d") endif call s:setopt('a:root_url','http://'.(bind=='0.0.0.0'?'localhost': bind).':'.port.'/') endfunction function! s:Destroy(bang,...) if a:0 == 0 call s:rubyexe("script/destroy") return elseif a:0 == 1 call s:rubyexe("script/destroy ".s:rquote(a:1)) return endif let str = "" let c = 1 while c <= a:0 let str = str . " " . s:rquote(a:{c}) let c = c + 1 endwhile call s:rubyexe(s:rquote("script/destroy").str.(s:usesubversion()?' -c':'')) unlet! s:user_classes_{s:rv()} endfunction function! s:Generate(bang,...) if a:0 == 0 call s:rubyexe("script/generate") return elseif a:0 == 1 call s:rubyexe("script/generate ".s:rquote(a:1)) return endif let target = s:rquote(a:1) let str = "" let c = 2 while c <= a:0 let str = str . " " . s:rquote(a:{c}) let c = c + 1 endwhile if str !~ '-p\>' && str !~ '--pretend\>' let execstr = s:rubyexestr('-r./config/boot -e "require '."'commands/generate'".'" -- '.target." -p -f".str) let res = system(execstr) let file = matchstr(res,'\s\+\%(create\|force\)\s\+\zs\f\+\.rb\ze\n') if file == "" let file = matchstr(res,'\s\+\%(exists\)\s\+\zs\f\+\.rb\ze\n') endif "echo file else let file = "" endif if !s:rubyexe("script/generate ".target.(s:usesubversion()?' -c':'').str) && file != "" unlet! s:user_classes_{s:rv()} "exe "edit ".s:ra()."/".file edit `=RailsRoot().'/'.file` endif endfunction function! s:generators() return "controller\nintegration_test\nmailer\nmigration\nmodel\nobserver\nplugin\nresource\nscaffold\nsession_migration" endfunction function! s:ScriptComplete(ArgLead,CmdLine,P) let cmd = s:sub(a:CmdLine,'^\u\w*\s+','') let P = a:P - strlen(a:CmdLine)+strlen(cmd) if cmd !~ '^[ A-Za-z0-9_=-]*$' " You're on your own, bud return "" elseif cmd =~ '^\w*$' return "about\nconsole\ndestroy\ngenerate\nperformance/benchmarker\nperformance/profiler\nplugin\nproccess/reaper\nprocess/spawner\nrunner\nserver" elseif cmd =~ '^\%(plugin\)\s\+'.a:ArgLead.'$' return "discover\nlist\ninstall\nupdate\nremove\nsource\nunsource\nsources" elseif cmd =~ '\%(plugin\)\s\+\%(install\|remove\)\s\+'.a:ArgLead.'$' || cmd =~ '\%(generate\|destroy\)\s\+plugin\s\+'.a:ArgLead.'$' return s:pluginList(a:ArgLead,a:CmdLine,a:P) elseif cmd =~ '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$' return s:generators() elseif cmd =~ '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$' let target = matchstr(cmd,'^\w\+\s\+\zs\w\+\ze\s\+') let pattern = "" " TODO if target =~# '^\%(\w*_\)\=controller$' return s:sub(s:controllerList(pattern,"",""),'^application\n=','') elseif target =~# '^\%(\w*_\)\=model$' || target =~# '^scaffold\%(_resource\)\=$' || target == 'mailer' return s:modelList(pattern,"","") elseif target == 'migration' || target == 'session_migration' return s:migrationList(pattern,"","") elseif target == 'integration_test' return s:integrationtestList(pattern,"","") elseif target == 'observer' " script/generate observer is in Edge Rails let observers = s:observerList(pattern,"","") let models = s:modelList(pattern,"","") if cmd =~ '^destroy\>' let models = "" endif while strlen(models) > 0 let tmp = matchstr(models."\n",'.\{-\}\ze\n') let models = s:sub(models,'.{-}%(\n|$)','') if stridx("\n".observers."\n","\n".tmp."\n") == -1 && tmp !~'_observer$' let observers = observers."\n".tmp endif endwhile return s:sub(observers,'^\n','') elseif target == 'web_service' return s:apiList(pattern,"","") else return "" endif elseif cmd =~ '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$' return s:sub(s:controllerList("","",""),'^application\n=','') elseif cmd =~ '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$" return s:environments()."\n-s\n--sandbox" elseif cmd =~ '^\%(server\)\s\+.*-e\s\+'.a:ArgLead."$" return s:environments() elseif cmd =~ '^\%(server\)\s\+' return "-p\n-b\n-e\n-m\n-d\n-u\n-c\n-h\n--port=\n--binding=\n--environment=\n--mime-types=\n--daemon\n--debugger\n--charset=\n--help\n" endif return "" " return s:relglob(RailsRoot()."/script/",a:ArgLead."*") endfunction function! s:CustomComplete(A,L,P,cmd) let L = "Rscript ".a:cmd." ".s:sub(a:L,'^\h\w*\s+','') let P = a:P - strlen(a:L) + strlen(L) return s:ScriptComplete(a:A,L,P) endfunction function! s:ServerComplete(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"server") endfunction function! s:ConsoleComplete(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"console") endfunction function! s:GenerateComplete(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"generate") endfunction function! s:DestroyComplete(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"destroy") endfunction function! s:RubyComplete(A,L,R) return s:gsub(RailsUserClasses(),' ','\n')."\nActiveRecord::Base" endfunction " }}}1 " Navigation {{{1 function! s:BufNavCommands() " TODO: completion "silent exe "command! -bar -buffer -nargs=? Rcd :cd ".s:ra()."/" "silent exe "command! -bar -buffer -nargs=? Rlcd :lcd ".s:ra()."/" command! -buffer -bar -nargs=? Rcd :cd `=RailsRoot().'/'.` command! -buffer -bar -nargs=? Rlcd :lcd `=RailsRoot().'/'.` " Vim 6.2 chokes on script local completion functions (e.g., s:FindList). " :Rcommand! is a thin wrapper arround :command! which works around this Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rfind :call s:Find(0,,'' ,) Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList REfind :call s:Find(0,,'E',) Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RSfind :call s:Find(0,,'S',) Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RVfind :call s:Find(0,,'V',) Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList RTfind :call s:Find(0,,'T',) Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rsfind :RSfind Rcommand! -buffer -bar -nargs=* -count=1 -complete=custom,s:FindList Rtabfind :RTfind Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList Redit :call s:Edit(0,,'' ,) Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList REedit :call s:Edit(0,,'E',) Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RSedit :call s:Edit(0,,'S',) Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RVedit :call s:Edit(0,,'V',) Rcommand! -buffer -bar -nargs=* -bang -complete=custom,s:EditList RTedit :call s:Edit(0,,'T',) command! -buffer -bar -nargs=0 A :call s:Alternate(0,"") command! -buffer -bar -nargs=0 AE :call s:Alternate(0,"E") command! -buffer -bar -nargs=0 AS :call s:Alternate(0,"S") command! -buffer -bar -nargs=0 AV :call s:Alternate(0,"V") command! -buffer -bar -nargs=0 AT :call s:Alternate(0,"T") command! -buffer -bar -nargs=0 AN :call s:Related(0,"") command! -buffer -bar -nargs=0 R :call s:Related(0,"") command! -buffer -bar -nargs=0 RE :call s:Related(0,"E") command! -buffer -bar -nargs=0 RS :call s:Related(0,"S") command! -buffer -bar -nargs=0 RV :call s:Related(0,"V") command! -buffer -bar -nargs=0 RT :call s:Related(0,"T") "command! -buffer -bar -nargs=0 RN :call s:Alternate(0,"") endfunction function! s:djump(def) let def = s:sub(a:def,'^[@#]','') if def != '' let ext = matchstr(def,'\.\zs.*') let def = matchstr(def,'[^.]*') let v:errmsg = '' silent! exe "djump ".def if ext != '' && (v:errmsg == '' || v:errmsg =~ '^E387') let rpat = '\C^\s*respond_to\s*\%(\ 0 "call cursor(rline,1) let variable = matchstr(getline(rline),rpat) let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end) if !success silent! exe "djump ".def endif endif endif endif endfunction function! s:Find(bang,count,arg,...) let cmd = a:arg . (a:bang ? '!' : '') let str = "" if a:0 let i = 1 while i < a:0 let str = str . s:escarg(a:{i}) . " " let i = i + 1 endwhile let file = a:{i} let tail = matchstr(file,'[@#].*$') if tail != "" let file = s:sub(file,'[@#].*$','') endif if file != "" let file = s:RailsIncludefind(file) endif else let file = s:RailsFind() let tail = "" endif if file =~ '^\%(app\|config\|db\|public\|spec\|test\|vendor\)/.*\.' || !a:0 || 1 call s:findedit((a:count==1?'' : a:count).cmd,file.tail,str) else " Old way let fcmd = (a:count==1?'' : a:count).s:findcmdfor(cmd) let fcmd = s:sub(fcmd,'(\d+)vert ','vert \1') if file != "" exe fcmd.' '.str.s:escarg(file) endif call s:djump(tail) endif endfunction function! s:Edit(bang,count,arg,...) let cmd = a:arg . (a:bang ? '!' : '') if a:0 let str = "" let i = 1 while i < a:0 "let str = str . s:escarg(a:{i}) . " " let str = str . "`=a:".i."` " let i = i + 1 endwhile let file = a:{i} call s:findedit(s:editcmdfor(cmd),file,str) else exe s:editcmdfor(cmd) endif endfunction function! s:FindList(ArgLead, CmdLine, CursorPos) if exists("*UserFileComplete") " genutils.vim return UserFileComplete(s:RailsIncludefind(a:ArgLead), a:CmdLine, a:CursorPos, 1, &path) else return "" endif endfunction function! s:EditList(ArgLead, CmdLine, CursorPos) return s:relglob("",a:ArgLead."*[^~]") endfunction function! RailsIncludeexpr() " Is this foolproof? if mode() =~ '[iR]' || expand("") != v:fname return s:RailsIncludefind(v:fname) else return s:RailsIncludefind(v:fname,1) endif endfunction function! s:linepeak() let line = getline(line(".")) let line = s:sub(line,'^(.{'.col(".").'}).*','\1') let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','') return line endfunction function! s:matchcursor(pat) let line = getline(".") let lastend = 0 while lastend >= 0 let beg = match(line,'\C'.a:pat,lastend) let end = matchend(line,'\C'.a:pat,lastend) if beg < col(".") && end >= col(".") return matchstr(line,'\C'.a:pat,lastend) endif let lastend = end endwhile return "" endfunction function! s:findit(pat,repl) let res = s:matchcursor(a:pat) if res != "" return substitute(res,'\C'.a:pat,a:repl,'') else return "" endif endfunction function! s:findamethod(func,repl) return s:findit('\s*\<\%('.a:func.'\)\s*(\=\s*[:'."'".'"]\(\f\+\)\>.\=',a:repl) endfunction function! s:findasymbol(sym,repl) return s:findit('\s*:\%('.a:sym.'\)\s*=>\s*(\=\s*[:'."'".'"]\(\f\+\)\>.\=',a:repl) endfunction function! s:findfromview(func,repl) return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl) endfunction function! s:RailsFind() " UGH let format = s:format('html') let res = s:findit('\v\s*.=',expand('%:h').'/\1') if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif let res = s:findit('\v['."'".'"]=',expand('%:h').'\1') if res != ""|return res|endif let res = s:findamethod('require','\1') if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif let res = s:findamethod('belongs_to\|has_one\|composed_of\|validates_associated\|scaffold','app/models/\1.rb') if res != ""|return res|endif let res = s:singularize(s:findamethod('has_many\|has_and_belongs_to_many','app/models/\1')) if res != ""|return res.".rb"|endif let res = s:singularize(s:findamethod('create_table\|drop_table\|add_column\|rename_column\|remove_column\|add_index','app/models/\1')) if res != ""|return res.".rb"|endif let res = s:singularize(s:findasymbol('through','app/models/\1')) if res != ""|return res.".rb"|endif let res = s:findamethod('fixtures','fixtures/\1') if res != "" return RailsFilePath() =~ '\\s*','\1'),'^/',''),'\k+$','_&') if res != ""|return res."\n".s:findview(res)|endif let res = s:findamethod('render\s*:\%(template\|action\)\s\+=>\s*','\1.'.format.'\n\1') if res != ""|return res|endif let res = s:findamethod('redirect_to\s*(\=\s*:action\s\+=>\s*','\1') if res != ""|return res|endif let res = s:findfromview('stylesheet_link_tag','public/stylesheets/\1.css') if res != ""|return res|endif let res = s:sub(s:findfromview('javascript_include_tag','public/javascripts/\1.js'),'/defaults>','/application') if res != ""|return res|endif if RailsFileType() =~ '^controller\>' let res = s:findit('\s*\(\=',s:sub(s:sub(RailsFilePath(),'/controllers/','/views/'),'_controller\.rb$','').'/\1') if res != ""|return res|endif endif let isf_keep = &isfname set isfname=@,48-57,/,-,_,: ",\",' " TODO: grab visual selection in visual mode let cfile = expand("") let res = s:RailsIncludefind(cfile,1) let &isfname = isf_keep return res endfunction function! s:initnamedroutes() if s:cacheneeds("named_routes") let exec = "ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} app/controllers/#{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}" let string = s:railseval(exec) let routes = {} let list = split(string,"\n") let i = 0 " If we use for, Vim 6.2 dumbly treats endfor like endfunction while i < len(list) let route = split(list[i]," ") let name = route[0] let routes[name] = route[1] let i = i + 1 endwhile call s:cacheset("named_routes",routes) endif endfunction function! s:namedroutefile(route) call s:initnamedroutes() if s:cachehas("named_routes") && has_key(s:cache("named_routes"),a:route) return s:cache("named_routes")[a:route] endif return "" endfunction function! RailsNamedRoutes() call s:initnamedroutes() if s:cachehas("named_routes") return keys(s:cache("named_routes")) else " Dead code if s:cacheneeds("route_names") let lines = readfile(RailsRoot()."/config/routes.rb") let plurals = map(filter(copy(lines),'v:val =~# "^ map\\.resources\\s\\+:\\w"'),'matchstr(v:val,"^ map\\.resources\\=\\s\\+:\\zs\\w\\+")') let singulars = map(copy(plurals),'s:singularize(v:val)') let extras = map(copy(singulars),'"new_".v:val')+map(copy(singulars),'"edit_".v:val') let all = plurals + singulars + extras let named = map(filter(copy(lines),'v:val =~# "^ map\\.\\%(connect\\>\\|resources\\=\\>\\)\\@!\\w\\+"'),'matchstr(v:val,"^ map\\.\\zs\\w\\+")') call s:cacheset("route_names",named+all+map(copy(all),'"formatted_".v:val')) endif if s:cachehas("route_names") return s:cache("route_names") endif endif endfunction function! s:RailsIncludefind(str,...) if a:str == "ApplicationController" return "app/controllers/application.rb" elseif a:str == "Test::Unit::TestCase" return "test/unit/testcase.rb" elseif a:str == "<%=" " Probably a silly idea return "action_view.rb" endif let str = a:str if a:0 == 1 " Get the text before the filename under the cursor. " We'll cheat and peak at this in a bit let line = s:linepeak() let line = s:sub(line,'([:"'."'".']|\%[qQ]=[[({<])=\f*$','') else let line = "" endif let str = s:sub(str,'^\s*','') let str = s:sub(str,'\s*$','') let str = s:sub(str,'^[:@]','') "let str = s:sub(str,"\\([\"']\\)\\(.*\\)\\1",'\2') let str = s:sub(str,':0x\x+$','') " For # style output let str = s:gsub(str,"[\"']",'') if line =~ '\<\(require\|load\)\s*(\s*$' return str endif let str = s:underscore(str) let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*' if a:str =~ '\u' " Classes should always be in .rb files let str = str . '.rb' elseif line =~ '\(:partial\|"partial"\|'."'partial'".'\)\s*=>\s*' let str = s:sub(str,'([^/]+)$','_\1') if str =~ '^\./' elseif str =~ '/' let str = 'app/views/' . s:sub(str,'^/','') else let str = 'app/views/' . s:controller() . str endif elseif line =~ '\\s*' let str = s:sub(str,'^/=','views/layouts/') elseif line =~ '\(:controller\|"controller"\|'."'controller'".'\)\s*=>\s*' let str = 'app/controllers/'.str.'_controller.rb' elseif line =~ '\0,"'.cmd.'",)' let cmd = strpart(cmds,0,1) let cmds = strpart(cmds,1) endwhile endfunction function! s:BufFinderCommands() command! -buffer -bar -bang -nargs=+ Rcommand :call s:Command(0,) call s:addfilecmds("model") call s:addfilecmds("view") call s:addfilecmds("controller") call s:addfilecmds("migration") call s:addfilecmds("observer") call s:addfilecmds("helper") call s:addfilecmds("api") call s:addfilecmds("layout") call s:addfilecmds("fixtures") call s:addfilecmds("unittest") call s:addfilecmds("functionaltest") call s:addfilecmds("integrationtest") call s:addfilecmds("stylesheet") call s:addfilecmds("javascript") call s:addfilecmds("task") call s:addfilecmds("lib") call s:addfilecmds("plugin") endfunction function! s:autocamelize(files,test) if a:test =~# '^\u' return s:camelize(a:files) else return a:files endif endfunction function! RailsUserClasses() if !exists("b:rails_root") return "" elseif s:getopt('classes','ab') != '' return s:getopt('classes','ab') endif let var = "user_classes_".s:rv() if !exists("s:".var) let s:{var} = s:sub(s:sub(s:gsub(s:camelize( \ s:relglob("app/models/","**/*",".rb") . "\n" . \ s:sub(s:relglob("app/controllers/","**/*",".rb"),'','&_controller') . "\n" . \ s:relglob("app/helpers/","**/*",".rb") . "\n" . \ s:relglob("lib/","**/*",".rb") . "\n" . \ ""),'\n+',' '),'^\s+',''),'\s+$','') endif return s:{var} endfunction function! s:relglob(path,glob,...) " How could such a simple operation be so complicated? if exists("+shellslash") && ! &shellslash let old_ss = &shellslash let &shellslash = 1 endif if a:path =~ '[\/]$' let path = a:path else let path = a:path . '' endif if path !~ '^/' && path !~ '^\w:' && RailsRoot() != '' let path = RailsRoot() . '/' . path endif let suffix = a:0 ? a:1 : '' let badres = glob(path.a:glob.suffix)."\n" if v:version <= 602 " Nasty Vim bug in version 6.2 let badres = glob(path.a:glob.suffix)."\n" endif let goodres = "" let striplen = strlen(path) let stripend = strlen(suffix) while strlen(badres) > 0 let idx = stridx(badres,"\n") "if idx == -1 "let idx = strlen(badres) "endif let tmp = strpart(badres,0,idx) let badres = strpart(badres,idx+1) let goodres = goodres.strpart(tmp,striplen,strlen(tmp)-striplen-stripend) if suffix == '' && isdirectory(tmp) && goodres !~ '/$' let goodres = goodres."/" endif let goodres = goodres."\n" endwhile "let goodres = s:gsub("\n".goodres,'\n.{-}\~\n','\n') if exists("old_ss") let &shellslash = old_ss endif return s:compact(goodres) endfunction if v:version <= 602 " Yet another Vim 6.2 limitation let s:recurse = "*" else let s:recurse = "**/*" endif function! s:helperList(A,L,P) return s:autocamelize(s:relglob("app/helpers/",s:recurse,"_helper.rb"),a:A) endfunction function! s:controllerList(A,L,P) let con = s:relglob("app/controllers/",s:recurse,"_controller.rb") if con != '' return s:autocamelize("application\n".con,a:A) else return s:autocamelize("application",a:A) endif endfunction function! s:viewList(A,L,P) let c = s:controller(1) let top = s:relglob("app/views/",a:A."*[^~]") if c != '' let local = s:relglob("app/views/".c."/",a:A."*.*[^~]") if local != '' return local."\n".top endif endif return top endfunction function! s:layoutList(A,L,P) return s:relglob("app/views/layouts/","*") endfunction function! s:stylesheetList(A,L,P) return s:relglob("public/stylesheets/",s:recurse,".css") endfunction function! s:javascriptList(A,L,P) return s:relglob("public/javascripts/",s:recurse,".js") endfunction function! s:modelList(A,L,P) let models = s:relglob("app/models/",s:recurse,".rb")."\n" " . matches everything, and no good way to exclude newline. Lame. let models = s:gsub(models,'[ -~]*_observer\n',"") let models = s:compact(models) return s:autocamelize(models,a:A) endfunction function! s:observerList(A,L,P) return s:autocamelize(s:relglob("app/models/",s:recurse,"_observer.rb"),a:A) endfunction function! s:fixturesList(A,L,P) return s:compact(s:relglob("test/fixtures/",s:recurse)."\n".s:relglob("spec/fixtures/",s:recurse)) endfunction function! s:migrationList(A,L,P) return s:autocamelize(s:relglob("db/migrate/???_",a:A."*",".rb"),a:A) endfunction function! s:apiList(A,L,P) return s:autocamelize(s:relglob("app/apis/",s:recurse,"_api.rb"),a:A) endfunction function! s:unittestList(A,L,P) return s:autocamelize(s:relglob("test/unit/",s:recurse,"_test.rb"),a:A) endfunction function! s:functionaltestList(A,L,P) return s:autocamelize(s:relglob("test/functional/",s:recurse,"_test.rb"),a:A) endfunction function! s:integrationtestList(A,L,P) return s:autocamelize(s:relglob("test/integration/",s:recurse,"_test.rb"),a:A) endfunction function! s:pluginList(A,L,P) if a:A =~ '/' return s:relglob('vendor/plugins/',matchstr(a:A,'.\{-\}/').'**/*') else return s:relglob('vendor/plugins/',"*","/init.rb") endif endfunction " Task files, not actual rake tasks function! s:taskList(A,L,P) let top = s:relglob("lib/tasks/",s:recurse,".rake") if RailsFilePath() =~ '\0,'".cmd."','".name."',\"".prefix."\",".s:string(suffix).",".s:string(filter).",".s:string(default).",)" let cmd = strpart(cmds,0,1) let cmds = strpart(cmds,1) endwhile endfunction function! s:CommandList(A,L,P) let cmd = matchstr(a:L,'\CR[A-Z]\=\w\+') exe cmd." &" let lp = s:last_prefix . "\n" let res = "" while lp != "" let p = matchstr(lp,'.\{-\}\ze\n') let lp = s:sub(lp,'.{-}\n','') let res = res . s:relglob(p,s:last_filter,s:last_suffix)."\n" endwhile let res = s:compact(res) if s:last_camelize return s:autocamelize(res,a:A) else return res endif endfunction function! s:CommandEdit(bang,cmd,name,prefix,suffix,filter,default,...) if a:0 && a:1 == "&" let s:last_prefix = a:prefix let s:last_suffix = a:suffix let s:last_filter = a:filter let s:last_camelize = (a:suffix =~# '\.rb$') else if a:default == "both()" if s:model() != "" let default = s:model() else let default = s:controller() endif elseif a:default == "model()" let default = s:model(1) elseif a:default == "controller()" let default = s:controller(1) else let default = a:default endif call s:EditSimpleRb(a:bang,a:cmd,a:name,a:0 ? a:1 : default,a:prefix,a:suffix) endif endfunction function! s:EditSimpleRb(bang,cmd,name,target,prefix,suffix) let cmd = s:findcmdfor(a:cmd.(a:bang?'!':'')) if a:target == "" " Good idea to emulate error numbers like this? return s:error("E471: Argument required") " : R',a:name) "else "let g:target = a:target endif let f = s:underscore(a:target) let jump = matchstr(f,'[@#].*') let f = s:sub(f,'[@#].*','') if f == '.' let f = s:sub(f,'\.$','') else let f = f.a:suffix.jump if a:suffix !~ '\.' "let f = f.".rb" endif endif let f = s:gsub(a:prefix,'\n',f.'\n').f return s:findedit(cmd,f) endfunction function! s:migrationfor(file) let tryagain = 0 let arg = a:file if arg =~ '^\d$' let glob = '00'.arg.'_*.rb' elseif arg =~ '^\d\d$' let glob = '0'.arg.'_*.rb' elseif arg =~ '^\d\d\d$' let glob = ''.arg.'_*.rb' elseif arg == '' if s:model(1) != '' let glob = '*_'.s:pluralize(s:model(1)).'.rb' let tryagain = 1 else let glob = '*.rb' endif else let glob = '*'.arg.'*rb' endif let migr = s:sub(glob(RailsRoot().'/db/migrate/'.glob),'.*\n','') if migr == '' && tryagain let migr = s:sub(glob(RailsRoot().'/db/migrate/*.rb'),'.*\n','') endif if strpart(migr,0,strlen(RailsRoot())) == RailsRoot() let migr = strpart(migr,1+strlen(RailsRoot())) endif return migr endfunction function! s:migrationEdit(bang,cmd,...) let cmd = s:findcmdfor(a:cmd.(a:bang?'!':'')) let arg = a:0 ? a:1 : '' let migr = arg == "." ? "db/migrate" : s:migrationfor(arg) if migr != '' call s:findedit(cmd,migr) else return s:error("Migration not found".(arg=='' ? '' : ': '.arg)) endif endfunction function! s:fixturesEdit(bang,cmd,...) if a:0 let c = s:underscore(a:1) else let c = s:pluralize(s:model(1)) endif if c == "" return s:error("E471: Argument required") endif let e = fnamemodify(c,':e') let e = e == '' ? e : '.'.e let c = fnamemodify(c,':r') let file = 'test/fixtures/'.c.e if file =~ '\.\w\+$' && !s:hasfile("spec/fixtures/".c.e) call s:edit(a:cmd.(a:bang?'!':''),file) else call s:findedit(a:cmd.(a:bang?'!':''),file."\nspec/fixtures/".c.e) endif endfunction function! s:modelEdit(bang,cmd,...) call s:EditSimpleRb(a:bang,a:cmd,"model",a:0? a:1 : s:model(1),"app/models/",".rb") endfunction function! s:observerEdit(bang,cmd,...) call s:EditSimpleRb(a:bang,a:cmd,"observer",a:0? a:1 : s:model(1),"app/models/","_observer.rb") endfunction function! s:viewEdit(bang,cmd,...) if a:0 let view = a:1 elseif RailsFileType() == 'controller' let view = s:lastmethod() else let view = '' endif if view == '' return s:error("No view name given") elseif view == '.' return s:edit(a:cmd.(a:bang?'!':''),'app/views') elseif view !~ '/' && s:controller(1) != '' let view = s:controller(1) . '/' . view endif if view !~ '/' return s:error("Cannot find view without controller") endif let file = "app/views/".view let found = s:findview(view) if found != '' call s:edit(a:cmd.(a:bang?'!':''),found) elseif file =~ '\.\w\+\.\w\+$' || file =~ '\.'.s:viewspattern().'$' call s:edit(a:cmd.(a:bang?'!':''),file) elseif file =~ '\.\w\+$' call s:findedit(a:cmd.(a:bang?'!':''),file) else let format = s:format('html') if glob(RailsRoot().'/'.file.'.'.format.'.*[^~]') != '' let file = file . '.' . format endif call s:findedit(a:cmd.(a:bang?'!':''),file) endif endfunction function! s:findview(name) " TODO: full support of nested extensions let c = a:name let pre = "app/views/" let file = "" if c !~ '/' let controller = s:controller(1) if controller != '' let c = controller.'/'.c endif endif if c =~ '\.\w\+\.\w\+$' || c =~ '\.'.s:viewspattern().'$' return pre.c elseif s:hasfile(pre.c.".rhtml") let file = pre.c.".rhtml" elseif s:hasfile(pre.c.".rxml") let file = pre.c.".rxml" else let format = "." . s:format('html') let vt = s:view_types."," while 1 while vt != "" let t = matchstr(vt,'[^,]*') let vt = s:sub(vt,'[^,]*,','') if s:hasfile(pre.c.format.".".t) let file = pre.c.format.".".t break endif endwhile if format == '' || file != '' break else let format = '' endif endwhile endif return file endfunction function! s:findlayout(name) return s:findview("layouts/".a:name) endfunction function! s:layoutEdit(bang,cmd,...) if a:0 let c = s:underscore(a:1) else let c = s:controller(1) endif if c == "" let c = "application" endif let file = s:findlayout(c) if file == "" let file = s:findlayout("application") endif if file == "" let file = "app/views/layouts/application.rhtml" endif call s:edit(a:cmd.(a:bang?'!':''),s:sub(file,'^/','')) endfunction function! s:controllerEdit(bang,cmd,...) let controller = a:0 ? a:1 : s:controller(1) return s:EditSimpleRb(a:bang,a:cmd,"controller",controller,"app/controllers/",controller == "application" ? "" : "_controller") endfunction function! s:helperEdit(bang,cmd,...) return s:EditSimpleRb(a:bang,a:cmd,"helper",a:0? a:1 : s:controller(1),"app/helpers/","_helper.rb") endfunction function! s:apiEdit(bang,cmd,...) return s:EditSimpleRb(a:bang,a:cmd,"api",a:0 ? a:1 : s:controller(1),"app/apis/","_api.rb") endfunction function! s:stylesheetEdit(bang,cmd,...) return s:EditSimpleRb(a:bang,a:cmd,"stylesheet",a:0? a:1 : s:controller(1),"public/stylesheets/",".css") endfunction function! s:javascriptEdit(bang,cmd,...) return s:EditSimpleRb(a:bang,a:cmd,"javascript",a:0? a:1 : "application","public/javascripts/",".js") endfunction function! s:unittestEdit(bang,cmd,...) let f = a:0 ? a:1 : s:model(1) if !a:0 && RailsFileType() =~ '^model-aro\>' && f != '' && f !~ '_observer$' if s:hasfile("test/unit/".f."_observer.rb") || !s:hasfile("test/unit/".f.".rb") let f = f . "_observer" endif endif return s:EditSimpleRb(a:bang,a:cmd,"unittest",f,"test/unit/","_test.rb") endfunction function! s:functionaltestEdit(bang,cmd,...) if a:0 let f = a:1 else let f = s:controller() endif if f != '' && !s:hasfile("test/functional/".f."_test.rb") if s:hasfile("test/functional/".f."_controller_test.rb") let f = f . "_controller" elseif s:hasfile("test/functional/".f."_api_test.rb") let f = f . "_api" endif endif return s:EditSimpleRb(a:bang,a:cmd,"functionaltest",f,"test/functional/","_test.rb") endfunction function! s:integrationtestEdit(bang,cmd,...) if a:0 let f = a:1 elseif s:model() != '' let f = s:model() else let f = s:controller() endif return s:EditSimpleRb(a:bang,a:cmd,"integrationtest",f,"test/integration/","_test.rb") endfunction function! s:pluginEdit(bang,cmd,...) let cmd = s:findcmdfor(a:cmd.(a:bang?'!':'')) let plugin = "" let extra = "" if RailsFilePath() =~ '\','split') let cmd = s:sub(cmd,'find>','edit') return cmd endfunction function! s:try(cmd) abort if !exists(":try") " I've seen at least one weird setup without :try exe a:cmd else try exe a:cmd catch call s:error(s:sub(v:exception,'^.{-}:\zeE','')) return 0 endtry endif return 1 endfunction function! s:findedit(cmd,file,...) abort let cmd = s:findcmdfor(a:cmd) if a:file =~ '\n' let filelist = a:file . "\n" let file = '' while file == '' && filelist != '' let maybe = matchstr(filelist,'^.\{-\}\ze\n') let filelist = s:sub(filelist,'^.{-}\n','') if s:hasfile(s:sub(maybe,'[@#].*','')) let file = maybe endif endwhile if file == '' let file = matchstr(a:file."\n",'^.\{-\}\ze\n') endif else let file = a:file endif if file =~ '[@#]' let djump = matchstr(file,'[@#]\zs.*') let file = matchstr(file,'.\{-\}\ze[@#]') else let djump = '' endif if file == '' let testcmd = "edit" elseif RailsRoot() =~ '://' || cmd =~ 'edit' || cmd =~ 'split' if file !~ '^/' && file !~ '^\w:' && file !~ '://' let file = s:ra().'/'.file endif let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file elseif isdirectory(RailsRoot().'/'.file) let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:ra().'/'.file exe testcmd return else let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file endif if s:try(testcmd) " Shorten the file name (I don't fully understand how Vim decides when to " use a relative/absolute path for the file name, so lets blindly force it " to be as short as possible) "silent! file %:~:. "silent! lcd . call s:djump(djump) endif endfunction function! s:edit(cmd,file,...) let cmd = s:editcmdfor(a:cmd) let cmd = cmd.' '.(a:0 ? a:1 . ' ' : '') let file = a:file if file !~ '^/' && file !~ '^\w:' && file !~ '://' "let file = s:ra().'/'.file exe cmd."`=RailsRoot().'/'.file`" else exe cmd.file endif "exe cmd.file endfunction function! s:Alternate(bang,cmd) let cmd = a:cmd.(a:bang?"!":"") let file = s:AlternateFile() if file != "" call s:findedit(cmd,file) else call s:warn("No alternate file is defined") endif endfunction function! s:AlternateFile() let f = RailsFilePath() let t = RailsFileType() let altopt = s:getopt("alternate","bl") if altopt != "" return altopt elseif f =~ '\' return "public/javascripts/application.js" elseif f =~ '\' if t =~ '\' let dest = fnamemodify(f,':r:s?/layouts\>??').'/layout.'.fnamemodify(f,':e') else let dest = f endif " Go to the (r)spec, helper, controller, or (mailer) model let spec = fnamemodify(dest,':r:s?\' let api = s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb') return api elseif t =~ '^helper\>' let controller = s:sub(s:sub(f,'/helpers/','/controllers/'),'_helper\.rb$','_controller.rb') let controller = s:sub(controller,'application_controller','application') let spec = s:sub(s:sub(f,'' && f =~ '\' let file = s:singularize(expand("%:t:r")).'_test.rb' " .expand('%:e') return file elseif f == '' call s:warn("No filename present") elseif f =~ '\' return s:sub(s:sub(f,'' return s:sub(file,'app/models/','test/unit/')."\n".s:sub(s:sub(file,'_test\.rb$','_spec.rb'),'app/models/','spec/models/') elseif t =~ '^controller\>' "return s:sub(file,'app/controllers/','test/functional/') return s:sub(file,'' return s:sub(file,'test/unit/','app/models/') elseif t =~ '^test-functional\>' if file =~ '_api\.rb' return s:sub(file,'test/functional/','app/apis/') elseif file =~ '_controller\.rb' return s:sub(file,'test/functional/','app/controllers/') else return s:sub(file,'test/functional/','') endif elseif t =~ '^spec\>' return s:sub(file,'' && lastmethod != "" let root = s:sub(s:sub(s:sub(f,'/application\.rb$','/shared_controller.rb'),'/%(controllers|models)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod) let format = s:format('html') if glob(RailsRoot().'/'.root.'.'.format.'.*[^~]') != '' return root . '.' . format else return root endif elseif s:getopt("related","b") != "" return s:getopt("related","b") elseif f =~ '\' && f =~ '\' return "public/javascripts/application.js" elseif t =~ '^view-layout\>' return s:sub(s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb'),'' "call s:warn("No related file is defined") elseif t =~ '^view\>' let controller = s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1') let model = s:sub(s:sub(f,'/views/','/models/'),'/(\k+)\..*$','.rb#\1') if filereadable(s:sub(controller,'#.{-}$','')) return controller elseif filereadable(s:sub(model,'#.{-}$','')) || model =~ '_mailer\.rb#' return model else return controller endif elseif t =~ '^controller-api\>' return s:sub(s:sub(f,'/controllers/','/apis/'),'_controller\.rb$','_api.rb') elseif t =~ '^controller\>' return s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb') elseif t=~ '^helper\>' return s:sub(s:sub(f,'/helpers/','/views/layouts/'),'%(_helper)=\.rb$','') elseif t =~ '^model-arb\>' "call s:migrationEdit(0,cmd,'create_'.s:pluralize(expand('%:t:r'))) return s:migrationfor('create_'.s:pluralize(expand('%:t:r'))) elseif t =~ '^model-aro\>' return s:sub(f,'_observer\.rb$','.rb') elseif t =~ '^api\>' return s:sub(s:sub(f,'/apis/','/controllers/'),'_api\.rb$','_controller.rb') elseif f =~ '\ 1 return s:error("Incorrect number of arguments") endif if a:1 =~ '[^a-z0-9_/.]' return s:error("Invalid partial name") endif let ext = expand("%:e") let file = a:1 let first = a:firstline let last = a:lastline let range = first.",".last if RailsFileType() =~ '^view-layout\>' if RailsFilePath() =~ '\' let curdir = 'app/views/shared' if file !~ '/' let file = "shared/" .file endif else let curdir = s:sub(RailsFilePath(),'.* 0 if bufloaded(out) return s:error("Partial already open in buffer ".bufnr(out)) else exe "bwipeout ".bufnr(out) endif endif " No tabs, they'll just complicate things if ext =~? '^\%(rhtml\|erb\|dryml\)$' let erub1 = '\<\%\s*' let erub2 = '\s*-=\%\>' else let erub1 = '' let erub2 = '' endif let spaces = matchstr(getline(first),"^ *") if getline(last+1) =~ '\v^\s*'.erub1.'end'.erub2.'\s*$' let fspaces = matchstr(getline(last+1),"^ *") if getline(first-1) =~ '\v^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ %>]+)'.erub2.'\s*$' let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'for\s+(\k+)\s+in\s+([^ >]+)'.erub2.'\s*$','\1>\2') elseif getline(first-1) =~ '\v^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$' let collection = s:sub(getline(first-1),'^'.fspaces.erub1.'([^ %>]+)\.each\s+do\s+\|\s*(\k+)\s*\|'.erub2.'\s*$','\2>\1') endif if collection != '' let var = matchstr(collection,'^\k\+') let collection = s:sub(collection,'^\k+\>','') let first = first - 1 let last = last + 1 endif else let fspaces = spaces endif "silent exe range."write ".out let renderstr = "render :partial => '".fnamemodify(file,":r:r")."'" if collection != "" let renderstr = renderstr.", :collection => ".collection elseif "@".name != var let renderstr = renderstr.", :object => ".var endif if ext =~? '^\%(rhtml\|erb\|dryml\)$' let renderstr = "<%= ".renderstr." %>" elseif ext == "rxml" || ext == "builder" let renderstr = "xml << ".s:sub(renderstr,"render ","render(").")" elseif ext == "rjs" let renderstr = "page << ".s:sub(renderstr,"render ","render(").")" elseif ext == "haml" let renderstr = "= ".renderstr endif let buf = @@ silent exe range."yank" let partial = @@ let @@ = buf let ai = &ai let &ai = 0 silent exe "norm! :".first.",".last."change\".fspaces.renderstr."\.\" let &ai = ai if renderstr =~ '<%' norm ^6w else norm ^5w endif let ft = &ft if &hidden enew else new endif let shortout = fnamemodify(out,':~:.') "exe "silent file ".s:escarg(shortout) silent file `=shortout` let &ft = ft let @@ = partial silent put 0delete let @@ = buf if spaces != "" silent! exe '%substitute/^'.spaces.'//' endif silent! exe '%substitute?\%(\w\|[@:"'."'".'-]\)\@?'.name.'?g' 1 call s:Detect(out) if exists("l:partial_warn") call s:warn("Warning: partial exists!") endif endfunction " }}}1 " Migration Inversion {{{1 " Depends: s:sub, s:endof, s:gsub, s:error function! s:mkeep(str) " Things to keep (like comments) from a migration statement return matchstr(a:str,' #[^{].*') endfunction function! s:mextargs(str,num) if a:str =~ '^\s*\w\+\s*(' return s:sub(matchstr(a:str,'^\s*\w\+\s*\zs(\%([^,)]\+[,)]\)\{,'.a:num.'\}'),',$',')') else return s:sub(s:sub(matchstr(a:str,'\w\+\>\zs\s*\%([^,){ ]*[, ]*\)\{,'.a:num.'\}'),'[, ]*$',''),'^\s+',' ') endif endfunction function! s:migspc(line) return matchstr(a:line,'^\s*') endfunction function! s:invertrange(beg,end) let str = "" let lnum = a:beg while lnum <= a:end let line = getline(lnum) let add = "" if line == '' let add = ' ' elseif line =~ '^\s*\(#[^{].*\)\=$' let add = line elseif line =~ '\' let add = s:migspc(line)."drop_table".s:mextargs(line,1).s:mkeep(line) let lnum = s:endof(lnum) elseif line =~ '\' let add = s:sub(line,'\s*\(=\s*([^,){ ]*).*','create_table \1 do |t|'."\n".matchstr(line,'^\s*').'end').s:mkeep(line) elseif line =~ '\' let add = s:migspc(line).'remove_column'.s:mextargs(line,2).s:mkeep(line) elseif line =~ '\' let add = s:sub(line,'','add_column') elseif line =~ '\' let add = s:migspc(line).'remove_index'.s:mextargs(line,1) let mat = matchstr(line,':name\s*=>\s*\zs[^ ,)]*') if mat != '' let add = s:sub(add,'\)=$',', :name => '.mat.'&') else let mat = matchstr(line,'\[^,]*,\s*\zs\%(\[[^]]*\]\|[:"'."'".']\w*["'."'".']\=\)') if mat != '' let add = s:sub(add,'\)=$',', :column => '.mat.'&') endif endif let add = add.s:mkeep(line) elseif line =~ '\' let add = s:sub(s:sub(line,'\s*','') elseif line =~ '\' let add = s:sub(line,'' let add = s:migspc(line).'change_column'.s:mextargs(line,2).s:mkeep(line) elseif line =~ '\' let add = s:migspc(line).'change_column_default'.s:mextargs(line,2).s:mkeep(line) elseif line =~ '\.update_all(\(["'."'".']\).*\1)$' || line =~ '\.update_all \(["'."'".']\).*\1$' " .update_all('a = b') => .update_all('b = a') let pre = matchstr(line,'^.*\.update_all[( ][}'."'".'"]') let post = matchstr(line,'["'."'".'])\=$') let mat = strpart(line,strlen(pre),strlen(line)-strlen(pre)-strlen(post)) let mat = s:gsub(','.mat.',','%(,\s*)@<=([^ ,=]{-})(\s*\=\s*)([^,=]{-})%(\s*,)@=','\3\2\1') let add = pre.s:sub(s:sub(mat,'^,',''),',$','').post elseif line =~ '^s\*\%(if\|unless\|while\|until\|for\)\>' let lnum = s:endof(lnum) endif if lnum == 0 return -1 endif if add == "" let add = s:sub(line,'^\s*\zs.*','raise ActiveRecord::IrreversableMigration') elseif add == " " let add = "" endif let str = add."\n".str let lnum = lnum + 1 endwhile let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversableMigration\n)+','\1') return str endfunction function! s:Invert(bang) let err = "Could not parse method" let src = "up" let dst = "down" let beg = search('\%('.&l:define.'\).*'.src.'\>',"w") let end = s:endof(beg) if beg + 1 == end let src = "down" let dst = "up" let beg = search('\%('.&l:define.'\).*'.src.'\>',"w") let end = s:endof(beg) endif if !beg || !end return s:error(err) endif let str = s:invertrange(beg+1,end-1) if str == -1 return s:error(err) endif let beg = search('\%('.&l:define.'\).*'.dst.'\>',"w") let end = s:endof(beg) if !beg || !end return s:error(err) endif if beg + 1 < end exe (beg+1).",".(end-1)."delete _" endif if str != "" let reg_keep = @" let @" = str exe beg."put" exe 1+beg let @" = reg_keep endif endfunction " }}}1 " Cache {{{1 function! s:cacheworks() if v:version < 700 return 0 endif if !exists("s:cache") let s:cache = {} endif if !has_key(s:cache,RailsRoot()) let s:cache[RailsRoot()] = {} endif return 1 endfunction function! s:cacheclear(...) if RailsRoot() == "" | return "" | endif if !s:cacheworks() | return "" | endif if a:0 == 1 if s:cachehas(a:1) unlet! s:cache[RailsRoot()][a:1] endif else let s:cache[RailsRoot()] = {} endif endfunction function! s:cache(...) if !s:cacheworks() | return "" | endif if a:0 == 1 return s:cache[RailsRoot()][a:1] else return s:cache[RailsRoot()] endif endfunction "function! RailsCache(...) "if !s:cacheworks() | return "" | endif "if a:0 == 1 "if s:cachehas(a:1) "return s:cache(a:1) "else "return "" "endif "else "return s:cache() "endif "endfunction function! s:cachehas(key) if !s:cacheworks() | return "" | endif return has_key(s:cache(),a:key) endfunction function! s:cacheneeds(key) if !s:cacheworks() | return "" | endif return !has_key(s:cache(),a:key) endfunction function! s:cacheset(key,value) if !s:cacheworks() | return "" | endif let s:cache[RailsRoot()][a:key] = a:value endfunction " }}}1 " Syntax {{{1 " Depends: s:rubyeval, s:gsub, cache functions function! s:helpermethods() let s:rails_helper_methods = "" \."auto_complete_field auto_complete_result auto_discovery_link_tag auto_link " \."benchmark button_to button_to_function " \."cache capture cdata_section check_box check_box_tag collection_select concat content_for content_tag country_options_for_select country_select cycle " \."date_select datetime_select debug define_javascript_functions distance_of_time_in_words distance_of_time_in_words_to_now draggable_element draggable_element_js drop_receiving_element drop_receiving_element_js " \."error_message_on error_messages_for escape_javascript escape_once evaluate_remote_response excerpt " \."fields_for file_field file_field_tag form form_for form_remote_for form_remote_tag form_tag " \."hidden_field hidden_field_tag highlight " \."image_path image_submit_tag image_tag in_place_editor in_place_editor_field input " \."javascript_cdata_section javascript_include_tag javascript_path javascript_tag " \."link_to link_to_function link_to_if link_to_remote link_to_unless link_to_unless_current " \."mail_to markdown " \."number_to_currency number_to_human_size number_to_percentage number_to_phone number_with_delimiter number_with_precision " \."observe_field observe_form option_groups_from_collection_for_select options_for_select options_from_collection_for_select " \."pagination_links pagination_links_each password_field password_field_tag periodically_call_remote pluralize " \."radio_button radio_button_tag remote_form_for remote_function reset_cycle " \."sanitize select select_date select_datetime select_day select_hour select_minute select_month select_second select_tag select_time select_year simple_format sortable_element sortable_element_js strip_links strip_tags stylesheet_link_tag stylesheet_path submit_tag submit_to_remote " \."tag text_area text_area_tag text_field text_field_tag text_field_with_auto_complete textilize textilize_without_paragraph time_ago_in_words time_select time_zone_options_for_select time_zone_select truncate " \."update_page update_page_tag url_for " \."visual_effect " \."word_wrap" " The list of helper methods used to be derived automatically. Let's keep " this code around in case it's needed again. if !exists("s:rails_helper_methods") if g:rails_expensive let s:rails_helper_methods = "" if has("ruby") " && (has("win32") || has("win32unix")) ruby begin; require 'rubygems'; rescue LoadError; end if exists("g:rubycomplete_rails") && g:rubycomplete_rails ruby begin; require VIM::evaluate('RailsRoot()')+'/config/environment'; rescue Exception; end else ruby begin; require 'active_support'; require 'action_controller'; require 'action_view'; rescue LoadError; end end ruby begin; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?("#{e}_without_deprecation")}}.flatten.sort.uniq.reject {|m| m =~ /[=?]$/}; VIM::command('let s:rails_helper_methods = "%s"' % h.join(" ")); rescue Exception; end endif if s:rails_helper_methods == "" let s:rails_helper_methods = s:rubyeval('require %{action_controller}; require %{action_view}; h = ActionView::Helpers.constants.grep(/Helper$/).collect {|c|ActionView::Helpers.const_get c}.collect {|c| c.public_instance_methods(false)}.collect {|es| es.reject {|e| e =~ /_with(out)?_deprecation$/ || es.include?(%{#{e}_without_deprecation})}}.flatten.sort.uniq.reject {|m| m =~ /[=?]$/}; puts h.join(%{ })',"link_to") endif else let s:rails_helper_methods = "link_to" endif endif "let g:rails_helper_methods = s:rails_helper_methods return s:rails_helper_methods endfunction function! s:BufSyntax() if (!exists("g:rails_syntax") || g:rails_syntax) let t = RailsFileType() let s:prototype_functions = "$ $$ $A $F $H $R $w" " From the Prototype bundle for TextMate let s:prototype_classes = "Prototype Class Abstract Try PeriodicalExecuter Enumerable Hash ObjectRange Element Ajax Responders Base Request Updater PeriodicalUpdater Toggle Insertion Before Top Bottom After ClassNames Form Serializers TimedObserver Observer EventObserver Event Position Effect Effect2 Transitions ScopedQueue Queues DefaultOptions Parallel Opacity Move MoveBy Scale Highlight ScrollTo Fade Appear Puff BlindUp BlindDown SwitchOff DropOut Shake SlideDown SlideUp Squish Grow Shrink Pulsate Fold" let rails_helper_methods = '+\.\@+' let classes = s:gsub(RailsUserClasses(),'::',' ') if &syntax == 'ruby' if classes != '' exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule" endif if t == '' syn keyword rubyRailsMethod params request response session headers cookies flash endif if t =~ '^api\>' syn keyword rubyRailsAPIMethod api_method inflect_names endif if t =~ '^model$' || t =~ '^model-arb\>' syn keyword rubyRailsARMethod acts_as_list acts_as_nested_set acts_as_tree composed_of serialize syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many "syn match rubyRailsARCallbackMethod '\<\(before\|after\)_\(create\|destroy\|save\|update\|validation\|validation_on_create\|validation_on_update\)\>' syn keyword rubyRailsARCallbackMethod before_create before_destroy before_save before_update before_validation before_validation_on_create before_validation_on_update syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update syn keyword rubyRailsARClassMethod attr_accessible attr_protected establish_connection set_inheritance_column set_locking_column set_primary_key set_sequence_name set_table_name "syn keyword rubyRailsARCallbackMethod after_find after_initialize syn keyword rubyRailsARValidationMethod validate 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 syn keyword rubyRailsMethod logger endif if t =~ '^model-aro\>' syn keyword rubyRailsARMethod observe endif if t =~ '^model-mailer\>' syn keyword rubyRailsMethod logger " Misnomer but who cares syn keyword rubyRailsControllerMethod helper helper_attr helper_method endif if t =~ '^controller\>' || t =~ '^view\>' || t=~ '^helper\>' syn keyword rubyRailsMethod params request response session headers cookies flash syn match rubyRailsError '[@:]\@' syn match rubyRailsError '\<\%(render_partial\|puts\)\>' syn keyword rubyRailsRenderMethod render syn keyword rubyRailsMethod logger endif if t =~ '^helper\>' || t=~ '^view\>' "exe "syn match rubyRailsHelperMethod ".rails_helper_methods exe "syn keyword rubyRailsHelperMethod ".s:sub(s:helpermethods(),'\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!' syn match rubyRailsViewMethod '\.\@' if t =~ '\' syn keyword rubyRailsMethod local_assigns endif "syn keyword rubyRailsDeprecatedMethod start_form_tag end_form_tag link_to_image human_size update_element_function elseif t =~ '^controller\>' syn keyword rubyRailsControllerMethod helper helper_attr helper_method filter layout url_for serialize exempt_from_layout filter_parameter_logging hide_action cache_sweeper syn match rubyRailsDeprecatedMethod '\' syn keyword rubyRailsRenderMethod render_to_string redirect_to head syn match rubyRailsRenderMethod '\?\@!' 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 syn keyword rubyRailsFilterMethod verify endif if t =~ '^\%(db-\)\=\%(migration\|schema\)\>' syn keyword rubyRailsMigrationMethod create_table drop_table rename_table add_column rename_column change_column change_column_default remove_column add_index remove_index endif if t =~ '^test\>' if s:cacheneeds("user_asserts") && filereadable(RailsRoot()."/test/test_helper.rb") call s:cacheset("user_asserts",map(filter(readfile(RailsRoot()."/test/test_helper.rb"),'v:val =~ "^ def assert_"'),'matchstr(v:val,"^ def \\zsassert_\\w\\+")')) endif if s:cachehas("user_asserts") && !empty(s:cache("user_asserts")) exe "syn keyword rubyRailsUserMethod ".join(s:cache("user_asserts")) endif 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 if t !~ '^test-unit\>' syn match rubyRailsTestControllerMethod '\.\@' syn keyword rubyRailsTestControllerMethod assert_response assert_redirected_to assert_template assert_recognizes assert_generates assert_routing assert_dom_equal assert_dom_not_equal assert_valid assert_select assert_select_rjs assert_select_encoded assert_select_email endif elseif t=~ '^spec\>' syn keyword rubyRailsTestMethod describe context it specify it_should_behave_like before after fixtures controller_name helper_name syn keyword rubyRailsTestMethod violated pending if t !~ '^spec-model\>' syn match rubyRailsTestControllerMethod '\.\@' syn keyword rubyRailsMethod params request response session flash endif endif if t =~ '^task\>' syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!' endif if t =~ '^model-awss\>' syn keyword rubyRailsMethod member endif if t =~ '^config-routes\>' syn match rubyRailsMethod '\.\zs\%(connect\|resources\=\|root\|named_route\)\>' endif syn keyword rubyRailsMethod debugger 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 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 syn keyword rubyRailsInclude require_dependency gem syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsOrderSpecial syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:order\s*=>\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsOrderSpecial syn match railsOrderSpecial +\c\<\%(DE\|A\)SC\>+ contained syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<="+ skip=+\\\\\|\\"+ end=+"+ contains=@rubyStringSpecial,railsConditionsSpecial syn region rubyString matchgroup=rubyStringDelimiter start=+\%(:conditions\s*=>\s*\[\s*\)\@<='+ skip=+\\\\\|\\'+ end=+'+ contains=@rubyStringSpecial,railsConditionsSpecial syn match railsConditionsSpecial +?\|:\h\w*+ contained syn cluster rubyNotTop add=railsOrderSpecial,railsConditionsSpecial " XHTML highlighting inside %Q<> unlet! b:current_syntax let removenorend = !exists("g:html_no_rendering") let g:html_no_rendering = 1 syn include @htmlTop syntax/xhtml.vim if removenorend unlet! g:html_no_rendering endif let b:current_syntax = "ruby" " Restore syn sync, as best we can if !exists("g:ruby_minlines") let g:ruby_minlines = 50 endif syn sync fromstart exe "syn sync minlines=" . g:ruby_minlines syn case match syn region rubyString matchgroup=rubyStringDelimiter start=+%Q\=<+ end=+>+ contains=@htmlTop,@rubyStringSpecial "syn region rubyString matchgroup=rubyStringDelimiter start=+%q<+ end=+>+ contains=@htmlTop syn cluster htmlArgCluster add=@rubyStringSpecial syn cluster htmlPreProc add=@rubyStringSpecial elseif &syntax == "eruby" || &syntax == "haml" " && t =~ '^view\>' syn case match if classes != '' exe "syn keyword erubyRailsUserClass ".classes." contained containedin=@erubyRailsRegions" endif if &syntax == "haml" syn cluster erubyRailsRegions contains=hamlRubyCode,hamlRubyHash,rubyInterpolation else syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation endif syn match rubyRailsError '[@:]\@' contained containedin=@erubyRailsRegions,@rubyTop "exe "syn match erubyRailsHelperMethod ".rails_helper_methods." contained containedin=@erubyRailsRegions" exe "syn keyword erubyRailsHelperMethod ".s:sub(s:helpermethods(),'\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!' contained containedin=@erubyRailsRegions syn keyword erubyRailsMethod debugger logger contained containedin=@erubyRailsRegions syn keyword erubyRailsMethod params request response session headers cookies flash contained containedin=@erubyRailsRegions syn match erubyRailsViewMethod '\.\@' contained containedin=@erubyRailsRegions if t =~ '\' syn keyword erubyRailsMethod local_assigns contained containedin=@erubyRailsRegions endif syn keyword erubyRailsRenderMethod render contained containedin=@erubyRailsRegions syn match rubyRailsError '[^@:]\@' contained containedin=@erubyRailsRegions syn match rubyRailsError '\<\%(render_partial\|puts\)\>' contained containedin=@erubyRailsRegions syn case match set isk+=$ exe "syn keyword javascriptRailsClass contained ".s:prototype_classes exe "syn keyword javascriptRailsFunction contained ".s:prototype_functions syn cluster htmlJavaScript add=javascriptRailsClass,javascriptRailsFunction elseif &syntax == "yaml" syn case match " Modeled after syntax/eruby.vim unlet! b:current_syntax let g:main_syntax = 'eruby' syn include @rubyTop syntax/ruby.vim unlet g:main_syntax syn cluster yamlRailsRegions contains=yamlRailsOneLiner,yamlRailsBlock,yamlRailsExpression syn region yamlRailsOneLiner matchgroup=yamlRailsDelimiter start="^%%\@!" end="$" contains=@rubyRailsTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend oneline syn region yamlRailsBlock matchgroup=yamlRailsDelimiter start="<%%\@!" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment syn region yamlRailsExpression matchgroup=yamlRailsDelimiter start="<%=" end="%>" contains=@rubyTop containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment syn region yamlRailsComment matchgroup=yamlRailsDelimiter start="<%#" end="%>" contains=rubyTodo,@Spell containedin=ALLBUT,@yamlRailsRegions,yamlRailsComment keepend syn match yamlRailsMethod '\.\@' contained containedin=@yamlRailsRegions if classes != '' exe "syn keyword yamlRailsUserClass ".classes." contained containedin=@yamlRailsRegions" endif let b:current_syntax = "yaml" elseif &syntax == "html" syn case match set isk+=$ exe "syn keyword javascriptRailsClass contained ".s:prototype_classes exe "syn keyword javascriptRailsFunction contained ".s:prototype_functions syn cluster htmlJavaScript add=javascriptRailsClass,javascriptRailsFunction elseif &syntax == "javascript" " The syntax file included with Vim incorrectly sets syn case ignore. syn case match set isk+=$ exe "syn keyword javascriptRailsClass ".s:prototype_classes exe "syn keyword javascriptRailsFunction ".s:prototype_functions endif endif call s:HiDefaults() endfunction function! s:HiDefaults() hi def link rubyRailsAPIMethod rubyRailsMethod hi def link rubyRailsARAssociationMethod rubyRailsARMethod hi def link rubyRailsARCallbackMethod rubyRailsARMethod hi def link rubyRailsARClassMethod rubyRailsARMethod hi def link rubyRailsARValidationMethod rubyRailsARMethod hi def link rubyRailsARMethod rubyRailsMethod hi def link rubyRailsRenderMethod rubyRailsMethod hi def link rubyRailsHelperMethod rubyRailsMethod hi def link rubyRailsViewMethod rubyRailsMethod hi def link rubyRailsMigrationMethod rubyRailsMethod hi def link rubyRailsControllerMethod rubyRailsMethod hi def link rubyRailsDeprecatedMethod rubyRailsError hi def link rubyRailsFilterMethod rubyRailsMethod hi def link rubyRailsTestControllerMethod rubyRailsTestMethod hi def link rubyRailsTestMethod rubyRailsMethod hi def link rubyRailsRakeMethod rubyRailsMethod hi def link rubyRailsMethod railsMethod hi def link rubyRailsError rubyError hi def link rubyRailsInclude rubyInclude hi def link rubyRailsUserClass railsUserClass hi def link rubyRailsUserMethod railsUserMethod hi def link erubyRailsHelperMethod erubyRailsMethod hi def link erubyRailsViewMethod erubyRailsMethod hi def link erubyRailsRenderMethod erubyRailsMethod hi def link erubyRailsMethod railsMethod hi def link erubyRailsUserMethod railsUserMethod hi def link railsUserMethod railsMethod hi def link erubyRailsUserClass railsUserClass hi def link yamlRailsDelimiter Delimiter hi def link yamlRailsMethod railsMethod hi def link yamlRailsComment Comment hi def link yamlRailsUserClass railsUserClass hi def link yamlRailsUserMethod railsUserMethod hi def link javascriptRailsFunction railsMethod hi def link javascriptRailsClass railsClass hi def link railsUserClass railsClass hi def link railsMethod Function hi def link railsClass Type hi def link railsOrderSpecial railsStringSpecial hi def link railsConditionsSpecial railsStringSpecial hi def link railsStringSpecial Identifier endfunction function! s:RailslogSyntax() syn match railslogRender '^\s*\<\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\>' syn match railslogComment '^\s*# .*' syn match railslogModel '^\s*\u\%(\w\|:\)* \%(Load\%( Including Associations\| IDs For Limited Eager Loading\)\=\|Columns\|Count\|Update\|Destroy\|Delete all\)\>' skipwhite nextgroup=railslogModelNum syn match railslogModel '^\s*SQL\>' skipwhite nextgroup=railslogModelNum syn region railslogModelNum start='(' end=')' contains=railslogNumber contained skipwhite nextgroup=railslogSQL syn match railslogSQL '\u.*$' contained " Destroy generates multiline SQL, ugh syn match railslogSQL '^ \%(FROM\|WHERE\|ON\|AND\|OR\|ORDER\) .*$' syn match railslogNumber '\<\d\+\>%' syn match railslogNumber '[ (]\@<=\<\d\+\.\d\+\>' syn region railslogString start='"' skip='\\"' end='"' oneline contained syn region railslogHash start='{' end='}' oneline contains=railslogHash,railslogString syn match railslogIP '\<\d\{1,3\}\%(\.\d\{1,3}\)\{3\}\>' syn match railslogTimestamp '\<\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\>' syn match railslogSessionID '\<\x\{32\}\>' syn match railslogIdentifier '^\s*\%(Session ID\|Parameters\)\ze:' syn match railslogSuccess '\<2\d\d \u[A-Za-z0-9 ]*\>' syn match railslogRedirect '\<3\d\d \u[A-Za-z0-9 ]*\>' syn match railslogError '\<[45]\d\d \u[A-Za-z0-9 ]*\>' syn match railslogError '^DEPRECATION WARNING\>' syn keyword railslogHTTP OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT syn region railslogStackTrace start=":\d\+:in `\w\+'$" end="^\s*$" keepend fold hi def link railslogComment Comment hi def link railslogRender Keyword hi def link railslogModel Type hi def link railslogSQL PreProc hi def link railslogNumber Number hi def link railslogString String hi def link railslogSessionID Constant hi def link railslogIdentifier Identifier hi def link railslogRedirect railslogSuccess hi def link railslogSuccess Special hi def link railslogError Error hi def link railslogHTTP Special endfunction " }}}1 " Statusline {{{1 " Depends: nothing! " Provides: s:BufInitStatusline function! s:addtostatus(letter,status) let status = a:status if status !~ 'Rails' && g:rails_statusline let status=substitute(status,'\C%'.tolower(a:letter),'%'.tolower(a:letter).'%{RailsStatusline()}','') if status !~ 'Rails' let status=substitute(status,'\C%'.toupper(a:letter),'%'.toupper(a:letter).'%{RailsSTATUSLINE()}','') endif endif return status endfunction function! s:BufInitStatusline() if g:rails_statusline if &l:statusline == '' let &l:statusline = &g:statusline endif if &l:statusline == '' let &l:statusline='%<%f %h%m%r%=' if &ruler let &l:statusline = &l:statusline . '%-16( %l,%c-%v %)%P' endif endif let &l:statusline = s:InjectIntoStatusline(&l:statusline) endif endfunction function! s:InitStatusline() if g:rails_statusline if &g:statusline == '' let &g:statusline='%<%f %h%m%r%=' if &ruler let &g:statusline = &g:statusline . '%-16( %l,%c-%v %)%P' endif endif let &g:statusline = s:InjectIntoStatusline(&g:statusline) endif endfunction function! s:InjectIntoStatusline(status) let status = a:status if status !~ 'Rails' let status = s:addtostatus('y',status) let status = s:addtostatus('r',status) let status = s:addtostatus('m',status) let status = s:addtostatus('w',status) let status = s:addtostatus('h',status) if status !~ 'Rails' let status=substitute(status,'%=','%{RailsStatusline()}%=','') endif if status !~ 'Rails' && status != '' let status=status.'%{RailsStatusline()}' endif endif return status endfunction function! RailsStatusline() if exists("b:rails_root") let t = RailsFileType() if t != "" return "[Rails-".t."]" else return "[Rails]" endif else return "" endif endfunction function! RailsSTATUSLINE() if exists("b:rails_root") let t = RailsFileType() if t != "" return ",RAILS-".toupper(t) else return ",RAILS" endif else return "" endif endfunction " }}}1 " Mappings {{{1 " Depends: nothing! " Exports: s:BufMappings function! s:BufMappings() map RailsAlternate :A map RailsRelated :R map RailsFind :REfind map RailsSplitFind :RSfind map RailsVSplitFind :RVfind map RailsTabFind :RTfind if g:rails_mappings if !hasmapto("RailsFind") nmap gf RailsFind endif if !hasmapto("RailsSplitFind") nmap f RailsSplitFind endif if !hasmapto("RailsTabFind") nmap gf RailsTabFind endif if !hasmapto("RailsAlternate") nmap [f RailsAlternate endif if !hasmapto("RailsRelated") nmap ]f RailsRelated endif if exists("$CREAM") imap RailsFind " Are these a good idea? imap RailsAlternate imap RailsRelated endif endif " SelectBuf you're a dirty hack let v:errmsg = "" endfunction " }}}1 " Menus {{{1 " Depends: s:gsub, s:sub, s:raketasks, s:generators, s:error " Provides: s:prephelp function! s:CreateMenus() abort if exists("g:rails_installed_menu") && g:rails_installed_menu != "" exe "aunmenu ".s:gsub(g:rails_installed_menu,'\&','') unlet g:rails_installed_menu endif if has("menu") && (exists("g:did_install_default_menus") || exists("$CREAM")) && g:rails_menu if g:rails_menu > 1 let g:rails_installed_menu = '&Rails' else let g:rails_installed_menu = '&Plugin.&Rails' endif if exists("$CREAM") let menucmd = '87anoremenu