" autoload/rails.vim " Author: Tim Pope " Install this file as autoload/rails.vim. if exists('g:autoloaded_rails') || &cp finish endif let g:autoloaded_rails = '5.0' " Utility Functions {{{1 let s:app_prototype = {} let s:file_prototype = {} let s:buffer_prototype = {} let s:readable_prototype = {} function! s:add_methods(namespace, method_names) for name in a:method_names let s:{a:namespace}_prototype[name] = s:function('s:'.a:namespace.'_'.name) endfor endfunction function! s:function(name) return function(substitute(a:name,'^s:',matchstr(expand(''), '\d\+_'),'')) endfunction 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:startswith(string,prefix) return strpart(a:string, 0, strlen(a:prefix)) ==# a:prefix endfunction function! s:endswith(string,suffix) return strpart(a:string, len(a:string) - len(a:suffix), len(a:suffix)) ==# a:suffix endfunction function! s:uniq(list) abort let i = 0 let seen = {} while i < len(a:list) if (a:list[i] ==# '' && exists('empty')) || has_key(seen,a:list[i]) call remove(a:list,i) elseif a:list[i] ==# '' let i += 1 let empty = 1 else let seen[a:list[i]] = 1 let i += 1 endif endwhile return a:list endfunction function! s:getlist(arg, key) let value = get(a:arg, a:key, []) return type(value) == type([]) ? copy(value) : [value] endfunction function! s:split(arg, ...) return type(a:arg) == type([]) ? copy(a:arg) : split(a:arg, a:0 ? a:1 : "\n") endfunction function! rails#lencmp(i1, i2) abort return len(a:i1) - len(a:i2) endfunc function! s:escarg(p) return s:gsub(a:p,'[ !%#]','\\&') endfunction function! s:esccmd(p) return s:gsub(a:p,'[!%#]','\\&') endfunction function! s:rquote(str) if a:str =~ '^[A-Za-z0-9_/.:-]\+$' return a:str elseif &shell =~? 'cmd' return '"'.s:gsub(s:gsub(a:str, '"', '""'), '\%', '"%"').'"' else return shellescape(a:str) endif endfunction function! s:fnameescape(file) abort if exists('*fnameescape') return fnameescape(a:file) else return escape(a:file," \t\n*?[{`$\\%#'\"|!<") endif endfunction function! s:sname() return fnamemodify(s:file,':t:r') endfunction function! s:pop_command() if exists("s:command_stack") && len(s:command_stack) > 0 exe remove(s:command_stack,-1) endif endfunction function! s:push_chdir(...) if !exists("s:command_stack") | let s:command_stack = [] | endif if exists("b:rails_root") && (a:0 ? getcwd() !=# rails#app().path() : !s:startswith(getcwd(), rails#app().path())) let chdir = exists("*haslocaldir") && haslocaldir() ? "lchdir " : "chdir " call add(s:command_stack,chdir.s:escarg(getcwd())) exe chdir.s:escarg(rails#app().path()) else call add(s:command_stack,"") endif endfunction function! s:app_path(...) dict return join([self.root]+a:000,'/') endfunction function! s:app_has_path(path) dict return getftime(self.path(a:path)) != -1 endfunction function! s:app_has_file(file) dict return filereadable(self.path(a:file)) endfunction function! s:app_find_file(name, ...) dict abort let trim = strlen(self.path())+1 if a:0 let path = s:pathjoin(map(s:pathsplit(a:1),'self.path(v:val)')) else let path = s:pathjoin([self.path()]) endif let suffixesadd = s:pathjoin(get(a:000,1,&suffixesadd)) let default = get(a:000,2,'') let oldsuffixesadd = &l:suffixesadd try let &suffixesadd = suffixesadd " Versions before 7.1.256 returned directories from findfile if type(default) == type(0) && (v:version < 702 || default == -1) let all = findfile(a:name,path,-1) if v:version < 702 call filter(all,'!isdirectory(v:val)') endif call map(all,'s:gsub(strpart(fnamemodify(v:val,":p"),trim),"\\\\","/")') return default < 0 ? all : get(all,default-1,'') elseif type(default) == type(0) let found = findfile(a:name,path,default) else let i = 1 let found = findfile(a:name,path) while v:version < 702 && found != "" && isdirectory(found) let i += 1 let found = findfile(a:name,path,i) endwhile endif return found == "" ? default : s:gsub(strpart(fnamemodify(found,':p'),trim),'\\','/') finally let &l:suffixesadd = oldsuffixesadd endtry endfunction call s:add_methods('app',['path','has_path','has_file','find_file']) " Split a path into a list. From pathogen.vim function! s:pathsplit(path) abort if type(a:path) == type([]) | return copy(a:path) | endif let split = split(a:path,'\\\@' if matchstr(self.getline(a:lnum+1),'^'.spc) && !matchstr(self.getline(a:lnum+1),'^'.spc.endpat) && matchstr(cline,endpat) return a:lnum endif let endl = a:lnum while endl <= self.line_count() let endl += 1 if self.getline(endl) =~ '^'.spc.endpat return endl elseif self.getline(endl) =~ '^=begin\>' while self.getline(endl) !~ '^=end\>' && endl <= self.line_count() let endl += 1 endwhile let endl += 1 elseif self.getline(endl) !~ '^'.spc && self.getline(endl) !~ '^\s*\%(#.*\)\=$' return 0 endif endwhile return 0 endfunction function! s:endof(lnum) return rails#buffer().end_of(a:lnum) endfunction function! s:readable_last_opening_line(start,pattern,limit) dict abort let line = a:start while line > a:limit && self.getline(line) !~ a:pattern let line -= 1 endwhile let lend = self.end_of(line) if line > a:limit && (lend < 0 || lend >= a:start) return line else return -1 endif endfunction function! s:lastopeningline(pattern,limit,start) return rails#buffer().last_opening_line(a:start,a:pattern,a:limit) endfunction function! s:readable_define_pattern() dict abort if self.name() =~ '\.yml$' return '^\%(\h\k*:\)\@=' endif let define = '^\s*def\s\+\(self\.\)\=' if self.name() =~# '\.rake$' let define .= "\\\|^\\s*\\%(task\\\|file\\)\\s\\+[:'\"]" endif if self.name() =~# '/schema\.rb$' let define .= "\\\|^\\s*create_table\\s\\+[:'\"]" endif if self.type_name('test') let define .= '\|^\s*test\s*[''"]' endif return define endfunction function! s:readable_last_method_line(start) dict abort return self.last_opening_line(a:start,self.define_pattern(),0) endfunction function! s:lastmethodline(start) return rails#buffer().last_method_line(a:start) endfunction function! s:readable_last_method(start) dict abort let lnum = self.last_method_line(a:start) let line = self.getline(lnum) if line =~# '^\s*test\s*\([''"]\).*\1' let string = matchstr(line,'^\s*\w\+\s*\([''"]\)\zs.*\ze\1') return 'test_'.s:gsub(string,' +','_') elseif lnum return s:sub(matchstr(line,'\%('.self.define_pattern().'\)\zs\h\%(\k\|[:.]\)*[?!=]\='),':$','') else return "" endif endfunction function! s:lastmethod(...) return rails#buffer().last_method(a:0 ? a:1 : line(".")) endfunction function! s:readable_format(start) dict abort let format = matchstr(self.getline(a:start), '\%(:formats *=>\|\.*\|respond_to\)\s*\%(\.*\|respond_to\)\s*\%(\ rline let match = matchstr(self.getline(line),'\C^\s*'.variable.'\s*\.\s*\zs\h\k*') if match != '' return match endif let line -= 1 endwhile endif return self.type_name('mailer') ? 'text' : 'html' endfunction function! s:format() return rails#buffer().format(line('.')) endfunction call s:add_methods('readable',['end_of','last_opening_line','last_method_line','last_method','format','define_pattern']) function! s:readable_find_affinity() dict abort let f = self.name() let all = self.app().projections() for pattern in reverse(sort(filter(keys(all), 'v:val =~# "^[^*]*\\*[^*]*$"'), s:function('rails#lencmp'))) if !has_key(all[pattern], 'affinity') continue endif let [prefix, suffix; _] = split(pattern, '\*', 1) if s:startswith(f, prefix) && s:endswith(f, suffix) let root = f[strlen(prefix) : -strlen(suffix)-1] return [all[pattern].affinity, root] endif endfor return ['', ''] endfunction function! s:controller(...) return rails#buffer().controller_name(a:0 ? a:1 : 0) endfunction function! s:readable_controller_name(...) dict abort let f = self.name() if has_key(self,'getvar') && self.getvar('rails_controller') != '' return self.getvar('rails_controller') endif let [affinity, root] = self.find_affinity() if affinity ==# 'controller' return root elseif affinity ==# 'resource' return rails#pluralize(root) endif if f =~ '\ get(self,'last_lines_ftime',0) let self.last_lines = s:readfile(self.path()) let self.last_lines_ftime = ftime endif return get(self,'last_lines',[]) endfunction function! s:file_getline(lnum,...) dict abort if a:0 return self.lines()[a:lnum-1 : a:1-1] else return self.lines()[a:lnum-1] endif endfunction function! s:buffer_lines() dict abort return self.getline(1,'$') endfunction function! s:buffer_getline(...) dict abort if a:0 == 1 return get(call('getbufline',[self.number()]+a:000),0,'') else return call('getbufline',[self.number()]+a:000) endif endfunction function! s:readable_line_count() dict abort return len(self.lines()) endfunction function! s:environment() if exists('$RAILS_ENV') return $RAILS_ENV elseif exists('$RACK_ENV') return $RACK_ENV else return "development" endif endfunction function! s:Complete_environments(...) return s:completion_filter(rails#app().environments(),a:0 ? a:1 : "") endfunction function! s:warn(str) echohl WarningMsg echomsg a:str echohl None " Sometimes required to flush output echo "" let v:warningmsg = a:str endfunction function! s:error(str) echohl ErrorMsg echomsg a:str echohl None let v:errmsg = a:str endfunction function! s:debug(str) if exists("g:rails_debug") && g:rails_debug echohl Debug echomsg a:str echohl None endif endfunction function! s:buffer_getvar(varname) dict abort return getbufvar(self.number(),a:varname) endfunction function! s:buffer_setvar(varname, val) dict abort return setbufvar(self.number(),a:varname,a:val) endfunction call s:add_methods('buffer',['getvar','setvar']) " }}}1 " Public Interface {{{1 function! rails#underscore(str) let str = s:gsub(a:str,'::','/') let str = s:gsub(str,'(\u+)(\u\l)','\1_\2') let str = s:gsub(str,'(\l|\d)(\u)','\1_\2') let str = tolower(str) return str endfunction function! rails#camelize(str) let str = s:gsub(a:str,'/(.=)','::\u\1') let str = s:gsub(str,'%([_-]|<)(.)','\u\1') return str endfunction function! rails#singularize(word) " Probably not worth it to be as comprehensive as Rails but we can " still hit the common cases. let word = a:word if word =~? '\.js$' || word == '' return word endif let word = s:sub(word,'eople$','ersons') let word = s:sub(word,'%([Mm]ov|[aeio])@ 0 && getbufvar(nr,'rails_file_type') != '' return getbufvar(nr,'rails_file_type') elseif f =~ '_controller\.rb$' || f =~ '\' let r = "controller-api" else let r = "controller" endif elseif f =~ '_api\.rb' let r = "api" elseif f =~ '\') if class == "ActiveResource::Base" let class = "ares" let r = "model-ares" elseif class == 'ActionMailer::Base' let r = "mailer" elseif class != '' let class = tolower(s:gsub(class,'[^A-Z]','')) let r = "model-".class elseif f =~ '_mailer\.rb$' let r = "mailer" elseif top =~ '\<\%(self\.\%(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 =~ '\.*\.' let r = "view-" . e elseif f =~ '\' if e == "yml" let r = "fixtures-yaml" else let r = "fixtures" . (e == "" ? "" : "-" . e) endif elseif f =~ '\<\%(test\|spec\)/\%(factories\|fabricators\)\>' let r = "fixtures-replacement" elseif f =~ '\' let r = "db-migration" elseif f=~ '\.*\.rb$' let r = "config-routes" elseif f =~ '\' return 'zeus '.a:cmd elseif self.has_path('script/rails') let cmd = 'script/rails '.a:cmd elseif self.has_path('script/' . matchstr(a:cmd, '\w\+')) let cmd = 'script/'.a:cmd elseif self.has_path('bin/rails') let cmd = 'bin/rails '.a:cmd elseif self.has('bundler') return 'bundle exec rails ' . a:cmd else return 'rails '.a:cmd endif return self.ruby_script_command(cmd) endfunction function! s:app_start_rails_command(cmd, ...) dict abort let cmd = s:esccmd(self.prepare_rails_command(a:cmd)) let title = s:sub(a:cmd, '\s.*', '') let title = get({ \ 'g': 'generate', \ 'd': 'destroy', \ 'c': 'console', \ 'db': 'dbconsole', \ 's': 'server', \ 'r': 'runner', \ }, title, title) call s:push_chdir(1) try if exists(':Start') exe 'Start'.(a:0 && a:1 ? '!' : '').' -title=rails\ '.title.' '.cmd elseif has("win32") exe "!start ".cmd else exe "!".cmd endif finally call s:pop_command() endtry return '' endfunction function! s:app_execute_rails_command(cmd) dict abort call s:push_chdir(1) try exe '!'.s:esccmd(self.prepare_rails_command(a:cmd)) finally call s:pop_command() endtry return '' endfunction function! s:app_lightweight_ruby_eval(ruby,...) dict abort let def = a:0 ? a:1 : "" if !executable("ruby") return def endif let args = '-e '.s:rquote(a:ruby) let cmd = self.ruby_command(args) silent! let results = system(cmd) return v:shell_error == 0 ? results : def endfunction function! s:app_eval(ruby,...) dict abort let def = a:0 ? a:1 : "" if !executable("ruby") return def endif let args = "-r./config/boot -r ".s:rquote(self.path("config/environment"))." -e ".s:rquote(a:ruby) let cmd = self.ruby_command(args) call s:push_chdir(1) try silent! let results = system(cmd) finally call s:pop_command() endtry return v:shell_error == 0 ? results : def endfunction call s:add_methods('app', ['ruby_command','ruby_script_command','prepare_rails_command','execute_rails_command','start_rails_command','lightweight_ruby_eval','eval']) " }}}1 " Commands {{{1 function! s:BufCommands() call s:BufNavCommands() call s:BufScriptWrappers() command! -buffer -bar -nargs=+ Rnavcommand :call s:Navcommand(0,) command! -buffer -bar -nargs=* -bang Rabbrev :call s:Abbrev(0,) command! -buffer -bar -nargs=? -bang -count -complete=customlist,rails#complete_rake Rake :call s:Rake(0,! && ? -1 : ,) command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rpreview :call s:Preview(0,,) command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_environments Rlog :call s:Log(0,) command! -buffer -bar -nargs=* -bang Rset :call s:Set(0,) command! -buffer -bar -nargs=0 Rtags :execute rails#app().tags_command() command! -buffer -bar -nargs=0 Ctags :execute rails#app().tags_command() command! -buffer -bar -nargs=0 -bang Rrefresh :if 0|unlet! g:autoloaded_rails|source `=s:file`|endif|call s:Refresh(0) if exists("g:loaded_dbext") command! -buffer -bar -nargs=? -complete=customlist,s:Complete_environments Rdbext :call s:BufDatabase(2,)|let b:dbext_buffer_defaulted = 1 endif let ext = expand("%:e") if RailsFilePath() =~ '\,call s:Extract(0,) elseif rails#buffer().name() =~# '^app/helpers/.*\.rb$' command! -buffer -bar -bang -nargs=1 -range Rextract :,call s:RubyExtract(0, 'app/helpers', [], s:sub(, '_helper$|Helper$|$', '_helper')) elseif rails#buffer().name() =~# '^app/\w\+/.*\.rb$' command! -buffer -bar -bang -nargs=1 -range Rextract :,call s:RubyExtract(0, matchstr(rails#buffer().name(), '^app/\w\+/').'concerns', [' extend ActiveSupport::Concern', ''], ) endif if RailsFilePath() =~ '\0) 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(rails#app().path(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") Tail `=rails#app().path(lf)` else pedit `=rails#app().path(lf)` endif endif endfunction function! rails#new_app_command(bang,...) abort if !a:0 && a:bang echo "rails.vim ".g:autoloaded_rails elseif !a:0 || a:1 !=# 'new' return 'echoerr '.string('Usage: rails new ') endif let args = copy(a:000) if a:bang let args += ['--force'] endif if &shellpipe !~# 'tee' && index(args, '--skip') < 0 && index(args, '--force') < 0 let args += ['--skip'] endif let temp = tempname() try if &shellpipe =~# '%s' let pipe = s:sub(&shellpipe, '%s', temp, 'g') else let pipe = &shellpipe . ' ' . temp endif exe '!rails' join(map(copy(args),'s:rquote(v:val)'),' ') pipe catch /^Vim:Interrupt/ endtry if isdirectory(expand(args[1])) let old_errorformat = &l:errorformat let chdir = exists("*haslocaldir") && haslocaldir() ? 'lchdir' : 'chdir' let cwd = getcwd() try exe chdir s:fnameescape(expand(args[1])) let &l:errorformat = s:efm_generate exe 'cgetfile' temp return 'cfirst' finally let &l:errorformat = old_errorformat exe chdir s:fnameescape(cwd) endtry endif return '' endfunction function! s:app_tags_command() dict 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("exctags") let cmd = "exctags" elseif executable("ctags") let cmd = "ctags" elseif executable("ctags.exe") let cmd = "ctags.exe" else call s:error("ctags not found") return '' endif let args = s:split(get(g:, 'rails_ctags_arguments', '--languages=-javascript')) exe '!'.cmd.' -f '.s:escarg(self.path("tags")).' -R --langmap="ruby:+.rake.builder.jbuilder.rjs" '.join(args,' ').' '.s:escarg(self.path()) return '' endfunction call s:add_methods('app',['tags_command']) function! s:Refresh(bang) if exists("g:rubycomplete_rails") && g:rubycomplete_rails && has("ruby") && exists('g:rubycomplete_completions') silent! ruby ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) silent! ruby if defined?(ActiveSupport::Dependencies); ActiveSupport::Dependencies.clear; elsif defined?(Dependencies); Dependencies.clear; end if a:bang silent! ruby ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) endif endif let _ = rails#app().cache.clear() silent doautocmd User BufLeaveRails if a:bang for key in keys(s:apps) if type(s:apps[key]) == type({}) call s:apps[key].cache.clear() endif call extend(s:apps[key],filter(copy(s:app_prototype),'type(v:val) == type(function("tr"))'),'force') endfor endif let i = 1 let max = bufnr('$') while i <= max let rr = getbufvar(i,"rails_root") if rr != "" call setbufvar(i,"rails_refresh",1) endif let i += 1 endwhile silent doautocmd User BufEnterRails endfunction function! s:RefreshBuffer() if exists("b:rails_refresh") && b:rails_refresh let oldroot = b:rails_root unlet! b:rails_root let b:rails_refresh = 0 call RailsBufInit(oldroot) unlet! b:rails_refresh endif endfunction " }}}1 " Rake {{{1 function! s:app_rake_tasks() dict if self.cache.needs('rake_tasks') call s:push_chdir() try let output = system(self.has_path('bin/rake') ? self.ruby_script_command('bin/rake -T') : 'rake -T') let lines = split(output, "\n") finally call s:pop_command() endtry if v:shell_error != 0 return [] endif call map(lines,'matchstr(v:val,"^rake\\s\\+\\zs\\S*")') call filter(lines,'v:val != ""') call self.cache.set('rake_tasks',lines) endif return self.cache.get('rake_tasks') endfunction call s:add_methods('app', ['rake_tasks']) let g:rails#rake_errorformat = '%D(in\ %f),' \.'%\\s%#from\ %f:%l:%m,' \.'%\\s%#from\ %f:%l:,' \.'%\\s#{RAILS_ROOT}/%f:%l:\ %#%m,' \.'%\\s%##\ %f:%l:%m,' \.'%\\s%##\ %f:%l,' \.'%\\s%#[%f:%l:\ %#%m,' \.'%\\s%#%f:%l:\ %#%m,' \.'%\\s%#%f:%l:,' \.'%m\ [%f:%l]:' function! s:make(bang, args, ...) if exists(':Make') exe 'Make'.(a:bang ? '! ' : ' ').a:args else exe 'make! '.a:args if !a:bang exe (a:0 ? a:1 : 'cwindow') endif endif endfunction function! s:Rake(bang,lnum,arg) let self = rails#app() let lnum = a:lnum < 0 ? 0 : a:lnum let old_makeprg = &l:makeprg let old_errorformat = &l:errorformat let old_compiler = get(b:, 'current_compiler', '') try call s:push_chdir(1) let b:current_compiler = 'rake' if rails#app().has_path('.zeus.sock') && executable('zeus') let &l:makeprg = 'zeus rake' elseif rails#app().has_path('bin/rake') let &l:makeprg = rails#app().ruby_script_command('bin/rake') elseif rails#app().has('bundler') let &l:makeprg = 'bundle exec rake' else let &l:makeprg = 'rake' endif let &l:errorformat = g:rails#rake_errorformat let arg = a:arg if &filetype =~# '^ruby\>' && arg == '' let mnum = s:lastmethodline(lnum) let str = getline(mnum)."\n".getline(mnum+1)."\n".getline(mnum+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 arg = rails#buffer().default_rake_task(lnum) endif if !has_key(self,'options') | let self.options = {} | endif if arg == '-' let arg = get(self.options,'last_rake_task','') endif let self.options['last_rake_task'] = arg let withrubyargs = '-r ./config/boot -r '.s:rquote(self.path('config/environment')).' -e "puts \%((in \#{Dir.getwd}))" ' if arg =~# '^notes\>' let &l:errorformat = '%-P%f:,\ \ *\ [%*[\ ]%l]\ [%t%*[^]]] %m,\ \ *\ [%*[\ ]%l] %m,%-Q' call s:make(a:bang, arg) elseif arg =~# '^\%(stats\|routes\|secret\|time:zones\|db:\%(charset\|collation\|fixtures:identify\>.*\|migrate:status\|version\)\)\%([: ]\|$\)' let &l:errorformat = '%D(in\ %f),%+G%.%#' call s:make(a:bang, arg, 'copen') else call s:make(a:bang, arg) endif finally let &l:errorformat = old_errorformat let &l:makeprg = old_makeprg let b:current_compiler = old_compiler if empty(b:current_compiler) unlet b:current_compiler endif call s:pop_command() endtry endfunction function! s:readable_test_file_candidates() dict abort let f = self.name() let projected = self.projected('test') if self.type_name('view') let tests = [ \ fnamemodify(f,':s?\\)' || f =~# '\%(\')] elseif self.type_name('test', 'spec', 'cucumber') let tests = [f] else let tests = [] endif if !self.app().has('test') call filter(tests, 'v:val !~# "^test/"') endif if !self.app().has('spec') call filter(tests, 'v:val !~# "^spec/"') endif if !self.app().has('cucumber') call filter(tests, 'v:val !~# "^cucumber/"') endif return projected + tests endfunction function! s:readable_test_file() dict abort let candidates = self.test_file_candidates() for file in candidates if self.app().has_path(file) return file endif endfor return get(candidates, 0, '') endfunction function! s:readable_default_rake_task(...) dict abort let app = self.app() let lnum = a:0 ? (a:1 < 0 ? 0 : a:1) : 0 if self.getvar('&buftype') == 'quickfix' return '-' elseif self.getline(lnum) =~# '# rake ' return matchstr(self.getline(lnum),'\C# rake \zs.*') elseif self.getline(self.last_method_line(lnum)-1) =~# '# rake ' return matchstr(self.getline(self.last_method_line(lnum)-1),'\C# rake \zs.*') elseif self.getline(self.last_method_line(lnum)) =~# '# rake ' return matchstr(self.getline(self.last_method_line(lnum)),'\C# rake \zs.*') elseif self.getline(1) =~# '# rake ' && !lnum return matchstr(self.getline(1),'\C# rake \zs.*') endif let placeholders = {} if lnum let placeholders.l = lnum let last = self.last_method(lnum) if !empty(last) let placeholders.d = last endif endif let tasks = self.projected('task', placeholders) if !empty(tasks) return tasks[0] endif if self.type_name('config-routes') return 'routes' elseif self.type_name('fixtures-yaml') && lnum return "db:fixtures:identify LABEL=".self.last_method(lnum) elseif self.type_name('fixtures') && lnum == 0 return "db:fixtures:load FIXTURES=".s:sub(fnamemodify(self.name(),':r'),'^.{-}/fixtures/','') elseif self.type_name('task') let mnum = self.last_method_line(lnum) let line = getline(mnum) " We can't grab the namespace so only run tasks at the start of the line if line =~# '^\%(task\|file\)\>' return self.last_method(a:lnum) else return matchstr(self.getline(1),'\C# rake \zs.*') endif elseif self.type_name('db-migration') let ver = matchstr(self.name(),'\' let opts = '' if test ==# self.name() let method = self.app().file(test).last_method(lnum) if method =~ '^test_' let opts = ' TESTOPTS=-n'.method endif endif if test =~# '^test/unit\>' return 'test:units TEST='.s:rquote(test).opts elseif test =~# '^test/functional\>' return 'test:functionals TEST='.s:rquote(test).opts elseif test =~# '^test/integration\>' return 'test:integration TEST='.s:rquote(test).opts elseif test ==# 'test' return 'test' else return 'test:recent TEST='.s:rquote(test).opts endif elseif test =~# '^spec\>' return 'spec SPEC='.s:rquote(with_line) elseif test =~# '^features\>' return 'cucumber FEATURE='.s:rquote(with_line) else let task = matchstr(test, '^\w*') return task . ' ' . toupper(task) . '=' . s:rquote(with_line) endif endif endfunction function! rails#complete_rake(A,L,P) return s:completion_filter(rails#app().rake_tasks(),a:A) endfunction call s:add_methods('readable', ['test_file_candidates', 'test_file', 'default_rake_task']) " }}}1 " Preview {{{1 function! s:initOpenURL() if !exists(":OpenURL") if has("gui_mac") || has("gui_macvim") || exists("$SECURITYSESSIONID") command -bar -nargs=1 OpenURL :!open elseif has("gui_win32") command -bar -nargs=1 OpenURL :!start cmd /cstart /b elseif executable("xdg-open") command -bar -nargs=1 OpenURL :!xdg-open elseif executable("sensible-browser") command -bar -nargs=1 OpenURL :!sensible-browser elseif executable('launchy') command -bar -nargs=1 OpenURL :!launchy elseif executable('git') command -bar -nargs=1 OpenURL :!git web--browse endif endif endfunction function! s:scanlineforuris(line) let url = matchstr(a: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" let url .= (url =~ '?' ? '&' : '?') . '_method='.tolower(method) endif endif if url != "" return [url] else return [] endif endfunction function! s:readable_preview_urls(lnum) dict abort let urls = [] let start = self.last_method_line(a:lnum) - 1 while start > 0 && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$' let urls = s:scanlineforuris(self.getline(start)) + urls let start -= 1 endwhile let start = 1 while start < self.line_count() && self.getline(start) =~ '^\s*\%(\%(-\=\|<%\)#.*\)\=$' let urls += s:scanlineforuris(self.getline(start)) let start += 1 endwhile if has_key(self,'getvar') && self.getvar('rails_preview') != '' let url += [self.getvar('rails_preview')] endif if self.name() =~ '^public/stylesheets/sass/' let urls = urls + [s:sub(s:sub(self.name(),'^public/stylesheets/sass/','/stylesheets/'),'\.s[ac]ss$','.css')] elseif self.name() =~ '^public/' let urls = urls + [s:sub(self.name(),'^public','')] elseif self.name() =~ '^app/assets/stylesheets/' let urls = urls + ['/assets/application.css'] elseif self.name() =~ '^app/assets/javascripts/' let urls = urls + ['/assets/application.js'] elseif self.name() =~ '^app/stylesheets/' let urls = urls + [s:sub(s:sub(self.name(),'^app/stylesheets/','/stylesheets/'),'\.less$','.css')] elseif self.name() =~ '^app/scripts/' let urls = urls + [s:sub(s:sub(self.name(),'^app/scripts/','/javascripts/'),'\.coffee$','.js')] elseif self.controller_name() != '' && self.controller_name() != 'application' if self.type_name('controller') && self.last_method(a:lnum) != '' let urls += ['/'.self.controller_name().'/'.self.last_method(a:lnum).'/'] elseif self.type_name('controller','view-layout','view-partial') let urls += ['/'.self.controller_name().'/'] elseif self.type_name('view') let urls += ['/'.s:controller().'/'.fnamemodify(self.name(),':t:r:r').'/'] endif endif return urls endfunction call s:add_methods('readable', ['preview_urls']) function! s:app_server_binding() dict abort let pidfile = self.path('tmp/pids/server.pid') let pid = get(readfile(pidfile, 'b', 1), 0, 0) if pid if self.cache.has('server') let old = self.cache.get('server') else let old = {'pid': 0, 'binding': ''} endif if !empty(old.binding) && pid == old.pid return old.binding endif let binding = rails#get_binding_for(pid) call self.cache.set('server', {'pid': pid, 'binding': binding}) if !empty(binding) return binding endif endif for app in s:split(glob("~/.pow/*")) if resolve(app) ==# resolve(self.path()) return fnamemodify(app, ':t').'.dev' endif endfor return '' endfunction call s:add_methods('app', ['server_binding']) function! s:Preview(bang, lnum, uri) abort let binding = rails#app().server_binding() if empty(binding) let binding = '0.0.0.0:3000' endif let binding = s:sub(binding, '^0\.0\.0\.0>|^127\.0\.0\.1>', 'localhost') let uri = empty(a:uri) ? get(rails#buffer().preview_urls(a:lnum),0,'') : a:uri if uri =~ '://' " elseif uri =~# '^[[:alnum:]-]\+\.' let uri = 'http://'.s:sub(uri, '^[^/]*\zs', matchstr(root, ':\d\+$')) elseif uri =~# '^[[:alnum:]-]\+\%(/\|$\)' let domain = s:sub(binding, '^localhost>', 'lvh.me') let uri = 'http://'.s:sub(uri, '^[^/]*\zs', '.'.domain) else let uri = 'http://'.binding.'/'.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 RailsBufInit(rails#app().path()) map q :bwipe wincmd p if !a:bang call s:warn("Define a :OpenURL command to use a browser") endif endif endfunction function! s:Complete_preview(A,L,P) return rails#buffer().preview_urls(a:L =~ '^\d' ? matchstr(a:L,'^\d\+') : line('.')) endfunction " }}}1 " Script Wrappers {{{1 function! s:BufScriptWrappers() command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_script Rscript :execute empty() ? rails#app().script_command(0, 'console') ? rails#app().script_command(0,) command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_script Rails :execute rails#app().script_command(0,) command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_generate Rgenerate :execute rails#app().generator_command(0,'generate',) command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Rdestroy :execute rails#app().generator_command(1,'destroy',) command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Rserver :execute rails#app().server_command(0,) command! -buffer -bang -nargs=? -range=0 -complete=customlist,s:Complete_edit Rrunner :execute rails#buffer().runner_command(0, ?:0, ) command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rp :execute rails#app().output_command(==?:-1, 'p begin '..' end') command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rpp :execute rails#app().output_command(==?:-1, 'require %{pp}; pp begin '..' end') endfunction function! s:app_generators() dict abort if self.cache.needs('generators') let paths = [self.path('vendor/plugins/*'), self.path('lib'), expand("~/.rails")] if !empty(self.gems()) let gems = values(self.gems()) let paths += map(gems, 'v:val . "/lib/rails"') let paths += map(gems, 'v:val . "/lib"') let builtin = [] else let builtin = ['assets', 'controller', 'generator', 'helper', 'integration_test', 'jbuilder', 'jbuilder_scaffold_controller', 'mailer', 'migration', 'model', 'resource', 'scaffold', 'scaffold_controller', 'task'] endif let generators = s:split(globpath(s:pathjoin(paths), 'generators/**/*_generator.rb')) call map(generators, 's:sub(v:val,"^.*[\\\\/]generators[\\\\/]\\ze.","")') call map(generators, 's:sub(v:val,"[\\\\/][^\\\\/]*_generator\.rb$","")') call map(generators, 'tr(v:val, "/", ":")') let builtin += map(filter(copy(generators), 'v:val =~# "^rails:"'), 'v:val[6:-1]') call filter(generators,'v:val !~# "^rails:"') call self.cache.set('generators',s:uniq(builtin + generators)) endif return self.cache.get('generators') endfunction function! s:app_script_command(bang,...) dict let msg = "rails.vim ".g:autoloaded_rails if a:0 == 0 && a:bang && rails#buffer().type_name() == '' echo msg." (Rails)" return elseif a:0 == 0 && a:bang echo msg." (Rails-".rails#buffer().type_name().")" return endif let str = join(map(copy(a:000), 's:rquote(v:val)'), ' ') if a:bang || str =~# '^\%(c\|console\|db\|dbconsole\|s\|server\)\>' return self.start_rails_command(str, a:bang) else return self.execute_rails_command(str) endif endfunction function! s:readable_runner_command(bang, count, arg) dict abort let old_makeprg = &l:makeprg let old_errorformat = &l:errorformat let old_compiler = get(b:, 'current_compiler', '') call s:push_chdir(1) try if !empty(a:arg) let arg = a:arg elseif a:count let arg = self.name() else let arg = self.test_file() if empty(arg) let arg = a:arg endif endif let extra = '' if a:count > 0 let extra = ':'.a:count endif let file = arg ==# self.name() ? self : self.app().file(arg) if arg =~# '^test/.*_test\.rb$' let compiler = 'rubyunit' if a:count > 0 let method = file.last_method(lnum) if method =~ '^test_' let extra = ' -n'.method else let extra = '' endif endif elseif arg =~# '^spec/.*\%(_spec\.rb\|\.feature\)$' let compiler = 'rspec' elseif arg =~# '^features/.*\.feature$' let compiler = 'cucumber' else let compiler = 'ruby' endif let compiler = get(file.projected('compiler'), 0, compiler) if compiler ==# 'testrb' || compiler ==# 'minitest' let compiler = 'rubyunit' elseif empty(findfile('compiler/'.compiler.'.vim', escape(&rtp, ' '))) let compiler = 'ruby' endif execute 'compiler '.compiler if compiler ==# 'ruby' let &l:makeprg = self.app().prepare_rails_command('runner') let extra = '' elseif &makeprg =~# '^\%(testrb\|rspec\|cucumber\)\>' && self.app().has_path('.zeus.sock') let &l:makeprg = 'zeus ' . &l:makeprg elseif compiler ==# 'rubyunit' let &l:makeprg = 'ruby -Itest' elseif self.app().has_path('bin/' . &l:makeprg) let &l:makeprg = self.app().ruby_script_command('bin/' . &l:makeprg) elseif &l:makeprg !~# '^bundle\>' && self.app().has('bundler') let &l:makeprg = 'bundle exec ' . &l:makeprg endif call s:make(a:bang, arg . extra) return '' finally call s:pop_command() let &l:errorformat = old_errorformat let &l:makeprg = old_makeprg let b:current_compiler = old_compiler if empty(b:current_compiler) unlet b:current_compiler endif endtry return '' endfunction call s:add_methods('readable', ['runner_command']) function! s:app_output_command(count, code) dict let str = self.prepare_rails_command('runner '.s:rquote(a:code)) call s:push_chdir(1) try let res = s:sub(system(str),'\n$','') finally call s:pop_command() endtry if a:count < 0 echo res else exe a:count.'put =res' endif return '' endfunction function! rails#get_binding_for(pid) if empty(a:pid) return '' endif if has('win32') let output = system('netstat -anop tcp') return matchstr(output, '\n\s*TCP\s\+\zs\S\+\ze\s\+\S\+\s\+LISTENING\s\+'.a:pid.'\>') endif if executable('lsof') let lsof = 'lsof' elseif executable('/usr/sbin/lsof') let lsof = '/usr/sbin/lsof' endif if exists('lsof') let output = system(lsof.' -an -itcp -sTCP:LISTEN -p'.a:pid) let binding = matchstr(output, '\S\+:\d\+\ze\s\+(LISTEN)\n') return s:sub(binding, '^\*', '0.0.0.0') endif if executable('netstat') let output = system('netstat -antp') return matchstr(output, '\S\+:\d\+\ze\s\+\S\+\s\+LISTEN\s\+'.a:pid.'/') return binding endif return '' endfunction function! s:app_server_command(bang,arg) dict if a:arg =~# '--help' call self.execute_rails_command('server '.a:arg) return '' endif let pidfile = self.path('tmp/pids/server.pid') if a:bang && executable("ruby") let pid = get(s:readfile(pidfile), 0, 0) if pid 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 exists(':Start') || has('win32') call self.start_rails_command('server '.a:arg, 1) else call self.execute_rails_command('server '.a:arg.' -d') endif return '' endfunction function! s:color_efm(pre, before, after) return a:pre . '%\S%# %#' . a:before . "\e[0m %#" . a:after . ',' . \ a:pre . ' %#'.a:before.' %#'.a:after . ',' endfunction let s:efm_generate = \ s:color_efm('%-G', 'invoke', '%f') . \ s:color_efm('%-G', 'conflict', '%f') . \ s:color_efm('%-G', 'run', '%f') . \ s:color_efm('%-G', 'create', ' ') . \ s:color_efm('%-G', 'exist', ' ') . \ s:color_efm('Overwrite%.%#', '%m', '%f') . \ s:color_efm('', '%m', '%f') . \ '%-G%.%#' function! s:app_generator_command(bang,...) dict call self.cache.clear('user_classes') call self.cache.clear('features') let cmd = join(map(copy(a:000),'s:rquote(v:val)'),' ') let old_makeprg = &l:makeprg let old_errorformat = &l:errorformat try let &l:makeprg = self.prepare_rails_command(cmd) let &l:errorformat = s:efm_generate call s:push_chdir(1) if a:bang make! else make endif finally call s:pop_command() let &l:errorformat = old_errorformat let &l:makeprg = old_makeprg endtry return '' endfunction call s:add_methods('app', ['generators','script_command','output_command','server_command','generator_command']) function! s:Complete_script(ArgLead,CmdLine,P) let cmd = s:sub(a:CmdLine,'^\u\w*\s+','') if cmd !~ '^[ A-Za-z0-9_=:-]*$' return [] elseif cmd =~# '^\w*$' return s:completion_filter(['generate', 'console', 'server', 'dbconsole', 'application', 'destroy', 'plugin', 'runner'],a:ArgLead) elseif cmd =~# '^\%(generate\|destroy\)\s\+'.a:ArgLead.'$' return s:completion_filter(rails#app().generators(),a:ArgLead) elseif cmd =~# '^\%(generate\|destroy\)\s\+\w\+\s\+'.a:ArgLead.'$' let target = matchstr(cmd,'^\w\+\s\+\%(\w\+:\)\=\zs\w\+\ze\s\+') if target =~# '^\w*controller$' return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"') elseif target ==# 'generator' return s:completion_filter(map(rails#app().relglob('lib/generators/','*'),'s:sub(v:val,"/$","")'), a:ArgLead) elseif target ==# 'helper' return s:autocamelize(rails#app().relglob('app/helpers/','**/*','_helper.rb'),a:ArgLead) elseif target ==# 'integration_test' || target ==# 'integration_spec' || target ==# 'feature' return s:autocamelize( \ rails#app().relglob('test/integration/','**/*','_test.rb') + \ rails#app().relglob('spec/features/', '**/*', '_spec.rb') + \ rails#app().relglob('spec/requests/', '**/*', '_spec.rb') + \ rails#app().relglob('features/', '**/*', '.feature'), a:ArgLead) elseif target ==# 'migration' || target ==# 'session_migration' return s:migrationList(a:ArgLead,"","") elseif target ==# 'mailer' return s:mailerList(a:ArgLead,"","") return s:completion_filter(rails#app().relglob("app/mailers/","**/*",".rb"),a:ArgLead) elseif target =~# '^\w*\%(model\|resource\)$' || target =~# '\w*scaffold\%(_controller\)\=$' return s:completion_filter(rails#app().relglob('app/models/','**/*','.rb'), a:ArgLead) else return [] endif elseif cmd =~# '^\%(generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$' return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"') return s:completion_filter(rails#app().environments()) elseif cmd =~# '^\%(console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$" return s:completion_filter(rails#app().environments()+["-s","--sandbox"],a:ArgLead) elseif cmd =~# '^\%(server\)\s\+.*-e\s\+'.a:ArgLead."$" return s:completion_filter(rails#app().environments(),a:ArgLead) elseif cmd =~# '^\%(server\)\s\+' if a:ArgLead =~# '^--environment=' return s:completion_filter(map(copy(rails#app().environments()),'"--environment=".v:val'),a:ArgLead) else return filter(["-p","-b","-c","-d","-u","-e","-P","-h","--port=","--binding=","--config=","--daemon","--debugger","--environment=","--pid=","--help"],'s:startswith(v:val,a:ArgLead)') endif endif return "" 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:Complete_script(a:A,L,P) endfunction function! s:Complete_server(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"server") endfunction function! s:Complete_console(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"console") endfunction function! s:Complete_generate(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"generate") endfunction function! s:Complete_destroy(A,L,P) return s:CustomComplete(a:A,a:L,a:P,"destroy") endfunction function! s:Complete_ruby(A,L,P) return s:completion_filter(rails#app().user_classes()+["ActiveRecord::Base"],a:A) endfunction " }}}1 " Navigation {{{1 function! s:BufNavCommands() command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Cd :cd `=rails#app().path()` command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Lcd :lcd `=rails#app().path()` command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rcd :cd `=rails#app().path()` command! -buffer -bar -nargs=? -complete=customlist,s:Complete_cd Rlcd :lcd `=rails#app().path()` command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related A :call s:Alternate('', ,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AE :call s:Alternate('E',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AS :call s:Alternate('S',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AV :call s:Alternate('V',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AT :call s:Alternate('T',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AD :call s:Alternate('D',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related AR :call s:Alternate('D',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related R :call s:Related('' ,,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RE :call s:Related('E',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RS :call s:Related('S',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RV :call s:Related('V',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RT :call s:Related('T',,,,) command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RD :call s:Related('D',,,,) endfunction function! s:djump(def) let def = s:sub(a:def,'^[#:]','') if def =~ '^\d\+$' exe def elseif def =~ '^!' if expand('%') !~ '://' && !isdirectory(expand('%:p:h')) call mkdir(expand('%:p:h'),'p') endif elseif 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*\%(mail\>.*\|respond_to\)\s*\%(\ 0 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 return '' endfunction function! s:Find(count,cmd,...) let str = "" if a:0 let i = 1 while i < a:0 let str .= s:escarg(a:{i}) . " " let i += 1 endwhile let file = a:{i} let tail = matchstr(file,'[#!].*$\|:\d*\%(:in\>.*\)\=$') if tail != "" let file = s:sub(file,'[#!].*$|:\d*%(:in>.*)=$','') endif if file != "" let file = s:RailsIncludefind(file) endif else let file = s:RailsFind() let tail = "" endif call s:findedit((a:count==1?'' : a:count).a:cmd,file.tail,str) endfunction function! s:Edit(count,cmd,...) if a:0 let str = "" let i = 1 while i < a:0 let str .= s:escarg(a:{i}) . " " let i += 1 endwhile let file = a:{i} call s:findedit(s:editcmdfor(a:cmd),file,str) else exe s:editcmdfor(a:cmd) endif endfunction function! s:fuzzyglob(arg) return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*') endfunction function! s:Complete_find(ArgLead, CmdLine, CursorPos) let paths = s:pathsplit(&l:path) let seen = {} for path in paths if s:startswith(path,rails#app().path()) && path !~ '[][*]' let path = path[strlen(rails#app().path()) + 1 : ] for file in rails#app().relglob(path == '' ? '' : path.'/',s:fuzzyglob(rails#underscore(a:ArgLead)), a:ArgLead =~# '\u' ? '.rb' : '') let seen[file] = 1 endfor endif endfor return s:autocamelize(sort(keys(seen)),a:ArgLead) endfunction function! s:Complete_edit(ArgLead, CmdLine, CursorPos) return s:completion_filter(rails#app().relglob("",s:fuzzyglob(a:ArgLead)),a:ArgLead) endfunction function! s:Complete_cd(ArgLead, CmdLine, CursorPos) let all = rails#app().relglob("",a:ArgLead."*") call filter(all,'v:val =~ "/$"') return filter(all,'s:startswith(v:val,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*=>\|\<'.a:sym.':\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=',a:repl) endfunction function! s:findfromview(func,repl) " ( ) ( ) ( \1 ) ( ) return s:findit('\s*\%(<%\)\==\=\s*\<\%('.a:func.'\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>['."'".'"]\=\s*\%(%>\s*\)\=',a:repl) endfunction function! s:RailsFind() if filereadable(expand("")) return expand("") endif " UGH let buffer = rails#buffer() let format = s:format() 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 = rails#underscore(s:findit('\v\s*<%(include|extend)\(=\s*<([[:alnum:]_:]+)>','\1')) if res != ""|return res.".rb"|endif let res = s:findamethod('require','\1') if res != ""|return res.(fnamemodify(res,':e') == '' ? '.rb' : '')|endif let res = s:findamethod('belongs_to\|has_one\|embedded_in\|embeds_one\|composed_of\|validates_associated\|scaffold','\1.rb') if res != ""|return res|endif let res = rails#singularize(s:findamethod('has_many\|has_and_belongs_to_many\|embeds_many','\1')) if res != ""|return res.".rb"|endif let res = rails#singularize(s:findamethod('create_table\|change_table\|drop_table\|rename_table\|\%(add\|remove\)_\%(column\|index\|timestamps\|reference\|belongs_to\)\|rename_column\|remove_columns\|rename_index','\1')) if res != ""|return res.".rb"|endif let res = rails#singularize(s:findasymbol('through','\1')) if res != ""|return res.".rb"|endif let res = s:findamethod('fixtures','fixtures/\1') if res != "" return RailsFilePath() =~ '\\|\\|,\s*to:\)\s*','\1') if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif if !buffer.type_name('controller', 'mailer') let res = s:sub(s:sub(s:findasymbol('layout','\1'),'^/',''),'[^/]+$','_&') if res != ""|return res."\n".s:findview(res)|endif let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:layout\s\+=>\|layout:\)\s*','\1'),'^/',''),'[^/]+$','_&') if res != ""|return res."\n".s:findview(res)|endif endif let res = s:findamethod('layout','\=s:findlayout(submatch(1))') if res != ""|return res|endif let res = s:findasymbol('layout','\=s:findlayout(submatch(1))') if res != ""|return res|endif let res = s:findamethod('helper','\1_helper.rb') if res != ""|return res|endif let res = s:findasymbol('controller','\1_controller.rb') if res != ""|return res|endif let res = s:findasymbol('action','\1') if res != ""|return s:findview(res)|endif let res = s:findasymbol('template','\1') if res != ""|return s:findview(res)|endif let res = s:sub(s:sub(s:findasymbol('partial','\1'),'^/',''),'[^/]+$','_&') if res != ""|return res."\n".s:findview(res)|endif let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:partial\s\+=>\|partial:\)\s*','\1'),'^/',''),'[^/]+$','_&') if res != ""|return res."\n".s:findview(res)|endif let res = s:findamethod('render\>\s*\%(:\%(template\|action\)\s\+=>\|template:\|action:\)\s*','\1') if res != ""|return s:findview(res)|endif let res = s:sub(s:findfromview('render','\1'),'^/','') if !buffer.type_name('controller', 'mailer') let res = s:sub(res,'[^/]+$','_&') endif if res != ""|return res."\n".s:findview(res)|endif let res = s:findamethod('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\','/application') if res != '' && fnamemodify(res, ':e') == '' " Append the default extension iff the filename doesn't already contains an extension let res .= '.js' endif if res != ""|return res|endif if buffer.type_name('controller', 'mailer') let contr = s:controller() let view = s:findit('\s*\(\=','/\1') if view !=# '' let res = s:findview(contr.'/'.view) if res != ""|return res|endif endif endif let old_isfname = &isfname try set isfname=@,48-57,/,-,_,:,# " TODO: grab visual selection in visual mode let cfile = expand("") finally let &isfname = old_isfname endtry let res = s:RailsIncludefind(cfile,1) return res endfunction function! s:app_named_route_file(route) dict call self.route_names() if self.cache.has("named_routes") && has_key(self.cache.get("named_routes"),a:route) return self.cache.get("named_routes")[a:route] endif return "" endfunction function! s:app_route_names() dict if self.cache.needs("named_routes") let exec = "ActionController::Routing::Routes.named_routes.each {|n,r| puts %{#{n} #{r.requirements[:controller]}_controller.rb##{r.requirements[:action]}}}" let string = self.eval(exec) let routes = {} for line in split(string,"\n") let route = split(line," ") let name = route[0] let routes[name] = route[1] endfor call self.cache.set("named_routes",routes) endif return keys(self.cache.get("named_routes")) endfunction call s:add_methods('app', ['route_names','named_route_file']) function! RailsNamedRoutes() return rails#app().route_names() endfunction function! s:RailsIncludefind(str,...) if a:str ==# "ApplicationController" return "application_controller.rb\napp/controllers/application.rb" elseif a:str ==# "Test::Unit::TestCase" return "test/unit/testcase.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,':0x\x+$','') " For # style output let str = s:gsub(str,"[\"']",'') if line =~# '\<\(require\|load\)\s*(\s*$' return str elseif str =~# '^\l\w*#\w\+$' return s:sub(str,'#','_controller.rb#') endif let str = rails#underscore(str) let fpat = '\(\s*\%("\f*"\|:\f*\|'."'\\f*'".'\)\s*,\s*\)*' if a:str =~# '\u' " Classes should always be in .rb files let str .= '.rb' elseif line =~# ':partial\s*=>\s*' || (line =~# ':layout\s*=>\s*' && !rails#buffer().type_name('controller', 'mailer')) let str = s:sub(str,'[^/]+$','_&') let str = s:findview(str) elseif line =~# '\\s*' let str = s:findview(s:sub(str,'^/=','layouts/')) elseif line =~# ':controller\s*=>\s*' let str = str.'_controller.rb' elseif line =~# '\\s*$' && rails#buffer().type_name('config-routes')) if line !~# ':as\s*=>\s*$' let str = s:sub(str,'_%(path|url)$','') let str = s:sub(str,'^hash_for_','') endif let file = rails#app().named_route_file(str) if file == "" let str = s:sub(str,'^formatted_','') if str =~# '^\%(new\|edit\)_' let str = s:sub(rails#pluralize(str),'^(new|edit)_(.*)','\2_controller.rb#\1') elseif str ==# rails#singularize(str) " If the word can't be singularized, it's probably a link to the show " method. We should verify by checking for an argument, but that's " difficult the way things here are currently structured. let str = rails#pluralize(str).'_controller.rb#show' else let str = str.'_controller.rb#index' endif else let str = file endif elseif str !~ '/' " If we made it this far, we'll risk making it singular. let str = rails#singularize(str) let str = s:sub(str,'_id$','') endif if str =~ '^/' && !filereadable(str) let str = s:sub(str,'^/','') endif if str =~# '^lib/' && !filereadable(str) let str = s:sub(str,'^lib/','') endif return str endfunction " }}}1 " Projection Commands {{{1 function! s:app_commands() dict abort let commands = {} let commands.environment = [ \ {'pattern': 'config/environments/*.rb'}, \ {'pattern': 'config/application.rb'}, \ {'pattern': 'config/environment.rb'}] let commands.helper = [{ \ 'pattern': 'app/helpers/*_helper.rb', \ 'template': "module %SHelper\nend", \ 'affinity': 'controller'}] let commands.initializer = [ \ {'pattern': 'config/initializers/*.rb'}, \ {'pattern': 'config/routes.rb'}] let commands.lib = [ \ {'pattern': 'lib/*.rb'}, \ {'pattern': 'Gemfile'}] let commands.mailer = [ \ {'pattern': 'app/mailers/*.rb', 'template': "class %S < ActionMailer::Base\nend", 'affinity': 'controller'}, \ {'pattern': 'app/models/*.rb', 'template': "class %S < ActionMailer::Base\nend", 'affinity': 'controller', 'complete': 0}] let commands.model = [{ \ 'pattern': 'app/models/*.rb', \ 'template': "class %S\nend", \ 'affinity': 'model'}] let commands.task = [ \ {'pattern': 'lib/tasks/*.rake'}, \ {'pattern': 'Rakefile'}] let commands['unit test'] = map(filter([ \ ['test', 'test/unit/*_test.rb', "require 'test_helper'\n\nclass %STest < ActiveSupport::TestCase\nend", 'model', 1], \ ['test', 'test/models/*_test.rb', "require 'test_helper'\n\nclass %STest < ActiveSupport::TestCase\nend", 'model', 1], \ ['test', 'test/helpers/*_test.rb', "require 'test_helper'\n\nclass %STest < ActionView::TestCase\nend", '', 1], \ ['test', 'test/helpers/*_helper_test.rb', "require 'test_helper'\n\nclass %SHelperTest < ActionView::TestCase\nend", 'controller', 0], \ ['spec', 'spec/models/*_spec.rb', "require 'spec_helper'\n\ndescribe %S do\nend", 'model', 1], \ ['spec', 'spec/helpers/*_spec.rb', "require 'spec_helper'\n\ndescribe %S do\nend", '', 1], \ ['spec', 'spec/helpers/*_helper_spec.rb', "require 'spec_helper'\n\ndescribe %SHelper do\nend", 'controller', 0]], \ 'rails#app().has(v:val[0])'), \ '{"pattern": v:val[1], "template": v:val[2], "affinity": v:val[3], "complete": v:val[4]}') let commands['functional test'] = map(filter([ \ ['test', 'test/functional/*_test.rb', "require 'test_helper'\n\nclass %STest < ActionController::TestCase\nend", '', 1], \ ['test', 'test/functional/*_controller_test.rb', "require 'test_helper'\n\nclass %SControllerTest < ActionController::TestCase\nend", 'controller', 0], \ ['test', 'test/controllers/*_test.rb', "require 'test_helper'\n\nclass %STest < ActionController::TestCase\nend", '', 1], \ ['test', 'test/controllers/*_controller_test.rb', "require 'test_helper'\n\nclass %SControllerTest < ActionController::TestCase\nend", 'controller', 0], \ ['test', 'test/mailers/', "require 'test_helper'\n\nclass %STest < ActionMailer::TestCase\nend", 'model', 1], \ ['spec', 'spec/controllers/*_spec.rb', "require 'spec_helper'\n\ndescribe %S do\nend", '', 1], \ ['spec', 'spec/controllers/*_controller_spec.rb', "require 'spec_helper'\n\ndescribe %SController do\nend", 'controller', 0], \ ['spec', 'spec/mailers/*_spec.rb', "require 'spec_helper'\n\ndescribe %S do\nend", 'controller', 0]], \ 'rails#app().has(v:val[0])'), \ '{"pattern": v:val[1], "template": v:val[2], "affinity": v:val[3], "complete": v:val[4]}') let commands['integration test'] = map(filter([ \ ['test', 'test/integration/*_test.rb', "require 'test_helper'\n\nclass %STest < ActionDispatch::IntegrationTest\nend"], \ ['spec', 'spec/features/*_spec.rb', "require 'spec_helper'\n\ndescribe \"%h\" do\nend"], \ ['spec', 'spec/requests/*_spec.rb', "require 'spec_helper'\n\ndescribe \"%h\" do\nend"], \ ['spec', 'spec/integration/*_spec.rb', "require 'spec_helper'\n\ndescribe \"%h\" do\nend"], \ ['cucumber', 'features/*.feature', "Feature: %h"], \ ['turnip', 'spec/acceptance/*.feature', "Feature: %h"], \ ['test', 'test/test_helper.rb', ""], \ ['cucumber', 'features/support/env.rb', ""], \ ['spec', 'spec/spec_helper.rb', ""]], \ 'rails#app().has(v:val[0])'), \ '{"pattern": v:val[1], "template": v:val[2]}') let all = self.projections() for pattern in reverse(sort(keys(all), function('rails#lencmp'))) let projection = all[pattern] for name in s:split(get(projection, 'command', get(projection, 'label', get(projection, 'name', get(projection, 'description', ''))))) let command = { \ 'pattern': pattern, \ 'affinity': get(projection, 'affinity', '')} if !has_key(commands, name) let commands[name] = [] endif call extend(commands[name], [command]) endfor endfor call filter(commands, '!empty(v:val)') return commands endfunction call s:add_methods('app', ['commands']) function! s:addfilecmds(type) let l = s:sub(a:type,'^.','\l&') for prefix in ['E', 'S', 'V', 'T', 'D', 'R', 'RE', 'RS', 'RV', 'RT', 'RD'] let cplt = " -complete=customlist,".s:sid.l."List" exe "command! -buffer -bar ".(prefix =~# 'D' ? '-range=0 ' : '')."-nargs=*".cplt." ".prefix.l." :execute s:".l.'Edit("'.(prefix =~# 'D' ? '' : '').s:sub(prefix, '^R', '').'",)' endfor endfunction function! s:BufProjectionCommands() call s:addfilecmds("view") call s:addfilecmds("controller") call s:addfilecmds("migration") call s:addfilecmds("schema") call s:addfilecmds("layout") call s:addfilecmds("fixtures") call s:addfilecmds("locale") if rails#app().has('spec') call s:addfilecmds("spec") endif call s:addfilecmds("stylesheet") call s:addfilecmds("javascript") for [name, command] in items(rails#app().commands()) call s:define_navcommand(name, command) endfor endfunction function! s:completion_filter(results,A) let results = sort(type(a:results) == type("") ? split(a:results,"\n") : copy(a:results)) call filter(results,'v:val !~# "\\~$"') let filtered = filter(copy(results),'s:startswith(v:val,a:A)') if !empty(filtered) | return filtered | endif let prefix = s:sub(a:A,'(.*[/]|^)','&_') let filtered = filter(copy(results),"s:startswith(v:val,prefix)") if !empty(filtered) | return filtered | endif let regex = s:gsub(a:A,'[^/]','[&].*') let filtered = filter(copy(results),'v:val =~# "^".regex') if !empty(filtered) | return filtered | endif let regex = s:gsub(a:A,'.','[&].*') let filtered = filter(copy(results),'v:val =~# regex') return filtered endfunction function! s:autocamelize(files,test) if a:test =~# '^\u' return s:completion_filter(map(copy(a:files),'rails#camelize(v:val)'),a:test) else return s:completion_filter(a:files,a:test) endif endfunction function! s:app_relglob(path,glob,...) dict if exists("+shellslash") && ! &shellslash let old_ss = &shellslash let &shellslash = 1 endif let path = a:path if path !~ '^/' && path !~ '^\w:' let path = self.path(path) endif let suffix = a:0 ? a:1 : '' let full_paths = split(glob(path.a:glob.suffix),"\n") let relative_paths = [] for entry in full_paths if suffix == '' && isdirectory(entry) && entry !~ '/$' let entry .= '/' endif let relative_paths += [entry[strlen(path) : -strlen(suffix)-1]] endfor if exists("old_ss") let &shellslash = old_ss endif return relative_paths endfunction call s:add_methods('app', ['relglob']) function! s:relglob(...) return join(call(rails#app().relglob,a:000,rails#app()),"\n") endfunction function! s:controllerList(A,L,P) let con = rails#app().relglob("app/controllers/","**/*",".rb") call map(con,'s:sub(v:val,"_controller$","")') return s:autocamelize(con,a:A) endfunction function! s:viewList(A,L,P) let c = s:controller(1) let top = rails#app().relglob("app/views/",s:fuzzyglob(a:A)) call filter(top,'v:val !~# "\\~$"') if c != '' && a:A !~ '/' let local = rails#app().relglob("app/views/".c."/","*.*[^~]") return s:completion_filter(local+top,a:A) endif return s:completion_filter(top,a:A) endfunction function! s:layoutList(A,L,P) return s:completion_filter(rails#app().relglob("app/views/layouts/","*"),a:A) endfunction function! s:stylesheetList(A,L,P) let list = rails#app().relglob('app/assets/stylesheets/','**/*.*','') call map(list,'s:sub(v:val,"\\..*$","")') let list += rails#app().relglob('public/stylesheets/','**/*','.css') if rails#app().has('sass') call extend(list,rails#app().relglob('public/stylesheets/sass/','**/*','.s?ss')) call s:uniq(list) endif return s:completion_filter(list,a:A) endfunction function! s:javascriptList(A,L,P) let list = rails#app().relglob('app/assets/javascripts/','**/*.*','') call map(list,'s:sub(v:val,"\\.js\\..*|\\.\\w+$","")') let list += rails#app().relglob("public/javascripts/","**/*",".js") return s:completion_filter(list,a:A) endfunction function! s:fixturesList(A,L,P) return s:completion_filter( \ rails#app().relglob('test/fixtures/', '**/*') + \ rails#app().relglob('spec/fixtures/', '**/*') + \ rails#app().relglob('test/factories/', '**/*') + \ rails#app().relglob('spec/factories/', '**/*'), \ a:A) endfunction function! s:localeList(A,L,P) return s:completion_filter(rails#app().relglob("config/locales/","**/*"),a:A) endfunction function! s:migrationList(A,L,P) if a:A =~ '^\d' let migrations = rails#app().relglob("db/migrate/",a:A."[0-9_]*",".rb") return map(migrations,'matchstr(v:val,"^[0-9]*")') else let migrations = rails#app().relglob("db/migrate/","[0-9]*[0-9]_*",".rb") call map(migrations,'s:sub(v:val,"^[0-9]*_","")') return s:autocamelize(migrations,a:A) endif endfunction function! s:schemaList(A,L,P) let tables = s:readfile(rails#app().path('db/schema.rb')) let table_re = '^\s\+create_table\s["'':]\zs[^"'',]*\ze' call map(tables,'matchstr(v:val, table_re)') call filter(tables,'strlen(v:val)') return s:autocamelize(tables, a:A) endfunction function! s:specList(A,L,P) return s:completion_filter(rails#app().relglob("spec/","**/*","_spec.rb"),a:A) endfunction function! s:Navcommand(bang,...) let prefixes = [] let suffix = '.rb' let affinity = '' for arg in a:000 if arg =~# '^[a-z]\+$' for prefix in ['E', 'S', 'V', 'T', 'D', 'R', 'RE', 'RS', 'RV', 'RT', 'RD'] exe 'command! -buffer -bar -bang -nargs=* ' . \ (prefix =~# 'D' ? '-range=0 ' : '') . \ prefix . arg . ' :echoerr ' . \ string(':Rnavcommand has been removed. See :help rails-projections') endfor break endif endfor endfunction function! s:define_navcommand(name, projection, ...) abort if empty(a:projection) return endif let name = s:gsub(a:name, '[[:punct:][:space:]]', '') if name !~# '^[a-z]\+$' return s:error("E182: Invalid command name ".name) endif for prefix in ['E', 'S', 'V', 'T', 'D', 'R', 'RE', 'RS', 'RV', 'RT', 'RD'] exe 'command! -buffer -bar -bang -nargs=* ' . \ (prefix =~# 'D' ? '-range=0 ' : '') . \ '-complete=customlist,'.s:sid.'CommandList ' . \ prefix . name . ' :execute s:CommandEdit(' . \ string((prefix =~# 'D' ? '' : '') . s:sub(prefix, '^R', '') . "") . ',' . \ string(a:name) . ',' . string(a:projection) . ',)' . \ (a:0 ? '|' . a:1 : '') endfor endfunction function! s:CommandList(A,L,P) let cmd = matchstr(a:L,'\C[A-Z]\w\+') exe cmd." &" let matches = [] for projection in s:last_projections if projection.pattern !~# '\*' || !get(projection, 'complete', 1) continue endif let [prefix, suffix; _] = split(projection.pattern, '\*', 1) let results = rails#app().relglob(prefix, '**/*', suffix) if suffix =~# '\.rb$' && a:A =~# '^\u' let matches += map(results, 'rails#camelize(v:val)') else let matches += results endif endfor return s:completion_filter(matches, a:A) endfunction function! s:CommandEdit(cmd, name, projections, ...) if a:0 && a:1 == "&" let s:last_projections = a:projections return '' else return rails#buffer().open_command(a:cmd, a:0 ? a:1 : '', a:name, a:projections) endif endfunction function! s:LegacyCommandEdit(cmd,name,target,prefix,suffix) let cmd = s:findcmdfor(a:cmd) if a:target == "" return s:error("E471: Argument required") endif let jump = matchstr(a:target, '[#!].*\|:\d*\%(:in\)\=$') let f = s:sub(a:target, '[#!].*|:\d*%(:in)=$', '') if jump =~ '^!' let cmd = s:editcmdfor(cmd) endif if f == '.' let f = s:sub(f,'\.$','') else let f .= a:suffix.jump endif let f = a:prefix.f return s:findedit(cmd,f) endfunction function! s:app_migration(file) dict let arg = a:file if arg =~ '^0$\|^0\=[#:]' let suffix = s:sub(arg,'^0*','') if self.has_file('db/seeds.rb') && suffix ==# '' return 'db/seeds.rb' elseif self.has_file('db/schema.rb') return 'db/schema.rb'.suffix elseif self.has_file('db/'.s:environment().'_structure.sql') return 'db/'.s:environment().'_structure.sql'.suffix elseif suffix ==# '' return 'db/seeds.rb' else return 'db/schema.rb'.suffix endif elseif 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 == '' let glob = '*.rb' else let glob = '*'.rails#underscore(arg).'*rb' endif let files = split(glob(self.path('db/migrate/').glob),"\n") call map(files,'strpart(v:val,1+strlen(self.path()))') if arg ==# '' return get(files,-1,'') endif let keep = get(files,0,'') if glob =~# '^\*.*\*rb' let pattern = glob[1:-4] call filter(files,'v:val =~# ''db/migrate/\d\+_''.pattern.''\.rb''') let keep = get(files,0,keep) endif return keep endfunction call s:add_methods('app', ['migration']) function! s:migrationEdit(cmd,...) let cmd = s:findcmdfor(a:cmd) let arg = a:0 ? a:1 : '' if arg =~# '!' " This will totally miss the mark if we cross into or out of DST. let ts = localtime() let local = strftime('%H', ts) * 3600 + strftime('%M', ts) * 60 + strftime('%S') let offset = local - ts % 86400 if offset <= -12 * 60 * 60 let offset += 86400 endif let template = 'class ' . rails#camelize(matchstr(arg, '[^!]*')) . " < ActiveRecord::Migration\nend" return rails#buffer().open_command(a:cmd, strftime('%Y%m%d%H%M%S', ts - offset).'_'.arg, 'migration', \ [{'pattern': 'db/migrate/*.rb', 'template': template}]) endif let migr = arg == "." ? "db/migrate" : rails#app().migration(arg) if migr != '' return s:findedit(cmd,migr) else return s:error("Migration not found".(arg=='' ? '' : ': '.arg)) endif endfunction function! s:schemaEdit(cmd,...) let cmd = s:findcmdfor(a:cmd) let schema = 'db/'.s:environment().'_structure.sql' if rails#app().has_file('db/schema.rb') || !rails#app().has_file(schema) let schema = 'db/schema.rb' endif return s:findedit(cmd,schema.(a:0 ? '#'.a:1 : '')) endfunction function! s:fixturesEdit(cmd,...) if a:0 let c = rails#underscore(a:1) else let c = rails#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 dirs = ['test/fixtures', 'spec/fixtures', 'test/factories', 'spec/factories'] let file = get(filter(copy(dirs), 'isdirectory(rails#app().path(v:val))'), 0, dirs[0]).'/'.c.e if file =~ '\.\w\+$' && rails#app().find_file(c.e, dirs) ==# '' return s:edit(a:cmd,file) else return s:findedit(a:cmd, rails#app().find_file(c.e, dirs, ['.yml', '.csv', '.rb'], file)) endif endfunction function! s:localeEdit(cmd,...) let c = a:0 ? a:1 : rails#app().default_locale() if c =~# '\.' return s:edit(a:cmd,rails#app().find_file(c,'config/locales',[],'config/locales/'.c)) else return rails#buffer().open_command(a:cmd, c, 'locale', \ [{'pattern': 'config/locales/*.yml'}, {'pattern': 'config/locales/*.rb'}]) endif endfunction function! s:dotcmp(i1, i2) return strlen(s:gsub(a:i1,'[^.]', '')) - strlen(s:gsub(a:i2,'[^.]', '')) endfunc let s:view_types = split('rhtml,erb,rxml,builder,rjs,haml',',') function! s:readable_resolve_view(name,...) dict abort let name = a:name let pre = 'app/views/' if name !~# '/' let controller = self.controller_name(1) if controller != '' let name = controller.'/'.name endif endif if name =~# '\.\w\+\.\w\+$' || name =~# '\.\%('.join(s:view_types,'\|').'\)$' return pre.name else for format in ['.'.self.format(a:0 ? a:1 : 0), ''] let found = self.app().relglob('', 'app/views/'.name.format.'.*') call sort(found, s:function('s:dotcmp')) if !empty(found) return found[0] endif endfor endif return '' endfunction function! s:readable_resolve_layout(name, ...) dict abort let name = a:name if name ==# '' let name = self.controller_name(1) endif let name = 'layouts/'.name let view = self.resolve_view(name, a:0 ? a:1 : 0) if view ==# '' && a:name ==# '' let view = self.resolve_view('layouts/application', a:0 ? a:1 : 0) endif return view endfunction call s:add_methods('readable', ['resolve_view', 'resolve_layout']) function! s:findview(name) return rails#buffer().resolve_view(a:name, line('.')) endfunction function! s:findlayout(name) return rails#buffer().resolve_layout(a:name, line('.')) endfunction function! s:viewEdit(cmd,...) if a:0 && a:1 =~ '^[^!#:]' let view = matchstr(a:1,'[^!#:]*') elseif rails#buffer().type_name('controller','mailer') let view = s:lastmethod(line('.')) else let view = '' endif if view == '' return s:error("No view name given") elseif view == '.' return s:edit(a:cmd,'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 found = rails#buffer().resolve_view(view, line('.')) let djump = a:0 ? matchstr(a:1,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$') : '' if found != '' call s:edit(a:cmd,found) call s:djump(djump) return '' elseif a:0 && a:1 =~# '!' call s:edit(a:cmd,'app/views/'.view) call s:djump(djump) return '' else return s:findedit(a:cmd,view) endif endfunction function! s:layoutEdit(cmd,...) if a:0 return s:viewEdit(a:cmd,"layouts/".a:1) endif let file = s:findlayout('') if file ==# "" let file = "app/views/layouts/application.html.erb" endif return s:edit(a:cmd,s:sub(file,'^/','')) endfunction function! s:controllerEdit(cmd,...) let suffix = '.rb' let template = "class %S < ApplicationController\nend" if a:0 == 0 let controller = s:controller(1) if rails#buffer().type_name() =~# '^view\%(-layout\|-partial\)\@!' let jump = '#'.expand('%:t:r') else let jump = '' endif else let controller = matchstr(a:1, '[^#!]*') let jump = matchstr(a:1, '[#!].*') endif if rails#app().has_file("app/controllers/".controller."_controller.rb") || !rails#app().has_file("app/controllers/".controller.".rb") let template = "class %SController < ApplicationController\nend" let suffix = "_controller".suffix endif return rails#buffer().open_command(a:cmd, controller . jump, 'controller', \ [{'template': template, 'pattern': 'app/controllers/*'.suffix}]) endfunction function! s:stylesheetEdit(cmd,...) let name = a:0 ? a:1 : s:controller(1) if rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.sass') return s:LegacyCommandEdit(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".sass") elseif rails#app().has('sass') && rails#app().has_file('public/stylesheets/sass/'.name.'.scss') return s:LegacyCommandEdit(a:cmd,"stylesheet",name,"public/stylesheets/sass/",".scss") elseif rails#app().has('lesscss') && rails#app().has_file('app/stylesheets/'.name.'.less') return s:LegacyCommandEdit(a:cmd,"stylesheet",name,"app/stylesheets/",".less") else let types = rails#app().relglob('app/assets/stylesheets/'.name,'.*','') if !empty(types) return s:LegacyCommandEdit(a:cmd,'stylesheet',name,'app/assets/stylesheets/',types[0]) else return s:LegacyCommandEdit(a:cmd,'stylesheet',name,'public/stylesheets/','.css') endif endif endfunction function! s:javascriptEdit(cmd,...) let name = a:0 ? a:1 : s:controller(1) if rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.coffee') return s:LegacyCommandEdit(a:cmd,'javascript',name,'app/scripts/','.coffee') elseif rails#app().has('coffee') && rails#app().has_file('app/scripts/'.name.'.js') return s:LegacyCommandEdit(a:cmd,'javascript',name,'app/scripts/','.js') else let types = rails#app().relglob('app/assets/javascripts/'.name,'.*','') if !empty(types) return s:LegacyCommandEdit(a:cmd,'javascript',name,'app/assets/javascripts/',types[0]) else return s:LegacyCommandEdit(a:cmd,'javascript',name,'public/javascripts/','.js') endif endif endfunction function! s:specEdit(cmd,...) abort let describe = s:sub(s:sub(rails#camelize(a:0 ? a:1 : ''), '^[^:]*::', ''), '!.*', '') return rails#buffer().open_command(a:cmd, a:0 ? a:1 : '', 'spec', [ \ {'pattern': 'spec/*_spec.rb', 'template': "require 'spec_helper'\n\ndescribe ".describe." do\nend"}, \ {'pattern': 'spec/spec_helper.rb'}]) endfunction " }}}1 " Alternate/Related {{{1 function! s:findcmdfor(cmd) let bang = '' if a:cmd =~ '\!$' let bang = '!' let cmd = s:sub(a:cmd,'\!$','') else let cmd = a:cmd endif if cmd =~ '^\d' let num = matchstr(cmd,'^\d\+') let cmd = s:sub(cmd,'^\d+','') else let num = '' endif if cmd == '' || cmd == 'E' || cmd == 'F' return num.'find'.bang elseif cmd == 'S' return num.'sfind'.bang elseif cmd == 'V' return 'vert '.num.'sfind'.bang elseif cmd == 'T' return num.'tabfind'.bang elseif cmd == 'D' return num.'read'.bang else return num.cmd.bang endif endfunction function! s:editcmdfor(cmd) let cmd = s:findcmdfor(a:cmd) let cmd = s:sub(cmd,'','split') let cmd = s:sub(cmd,'find>','edit') return cmd endfunction function! s:projection_pairs(options) let pairs = [] if has_key(a:options, 'format') for format in s:split(a:options.format) if format =~# '%s' let pairs += [s:split(format, '%s')] endif endfor else for prefix in s:split(get(a:options, 'prefix', [])) for suffix in s:split(get(a:options, 'suffix', [])) let pairs += [[prefix, suffix]] endfor endfor endif return pairs endfunction function! s:readable_open_command(cmd, argument, name, projections) dict abort let cmd = s:editcmdfor(a:cmd) let djump = '' if a:argument =~ '[#!]\|:\d*\%(:in\)\=$' let djump = matchstr(a:argument,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$') let argument = s:sub(a:argument,'[#!].*|:\d*%(:in)=$','') else let argument = a:argument endif for projection in a:projections if argument ==# '.' && projection.pattern =~# '\*' let file = split(projection.pattern, '\*')[0] elseif projection.pattern =~# '\*' if !empty(argument) let root = argument elseif get(projection, 'affinity', '') =~# '\%(model\|resource\)$' let root = self.model_name(1) elseif get(projection, 'affinity', '') =~# '^\%(controller\|collection\)$' let root = self.controller_name(1) else continue endif let file = s:sub(projection.pattern, '\*', root) elseif empty(argument) && projection.pattern !~# '\*' let file = projection.pattern else let file = '' endif if !empty(file) && self.app().has_path(file) let file = self.app().path(file) return cmd . ' ' . s:fnameescape(file) . '|exe ' . s:sid . 'djump('.string(djump) . ')' endif endfor if empty(argument) let defaults = filter(map(copy(a:projections), 'v:val.pattern'), 'v:val !~# "\\*"') if empty(defaults) return 'echoerr "E471: Argument required"' else return cmd . ' ' . s:fnameescape(defaults[0]) endif endif if djump !~# '^!' return 'echoerr '.string('No such '.tr(a:name, '_', ' ').' '.root) endif for projection in a:projections if projection.pattern !~# '\*' continue endif let [prefix, suffix; _] = split(projection.pattern, '\*', 1) if self.app().has_path(prefix) let relative = prefix . (suffix =~# '\.rb$' ? rails#underscore(root) : root) . suffix let file = self.app().path(relative) if !isdirectory(fnamemodify(file, ':h')) call mkdir(fnamemodify(file, ':h'), 'p') endif if has_key(projection, 'template') let template = s:split(projection.template) let ph = { \ 'S': rails#camelize(root), \ 'h': toupper(root[0]) . tr(rails#underscore(root), '_', ' ')[1:-1]} call map(template, 's:expand_placeholders(v:val, ph)') else let projected = self.app().file(relative).projected('template') let template = s:split(get(projected, 0, '')) endif call map(template, 's:gsub(v:val, "\t", " ")') return cmd . ' ' . s:fnameescape(simplify(file)) . '|call setline(1, '.string(template).')' . '|set nomod' endif endfor return 'echoerr '.string("Couldn't find destination directory for ".a:name.' '.a:argument) endfunction call s:add_methods('readable', ['open_command']) function! s:findedit(cmd,files,...) abort let cmd = s:findcmdfor(a:cmd) let files = type(a:files) == type([]) ? copy(a:files) : split(a:files,"\n") if len(files) == 1 let file = files[0] else let file = get(filter(copy(files),'rails#app().has_file(s:sub(v:val,"#.*|:\\d*$",""))'),0,get(files,0,'')) endif if file =~ '[#!]\|:\d*\%(:in\)\=$' let djump = matchstr(file,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$') let file = s:sub(file,'[#!].*|:\d*%(:in)=$','') else let djump = '' endif if file == '' let testcmd = "edit" elseif rails#app().has_path(file.'/') let arg = file == "." ? rails#app().path() : rails#app().path(file) let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').s:escarg(arg) exe testcmd return '' elseif rails#app().path() =~ '://' || cmd =~ 'edit' || cmd =~ 'split' if file !~ '^/' && file !~ '^\w:' && file !~ '://' let file = s:escarg(rails#app().path(file)) endif let testcmd = s:editcmdfor(cmd).' '.(a:0 ? a:1 . ' ' : '').file else let testcmd = cmd.' '.(a:0 ? a:1 . ' ' : '').file endif try exe testcmd call s:djump(djump) catch call s:error(s:sub(v:exception,'^.{-}:\zeE','')) endtry return '' endfunction function! s:edit(cmd,file,...) let cmd = s:editcmdfor(a:cmd) let cmd .= ' '.(a:0 ? a:1 . ' ' : '') let file = a:file if file !~ '^/' && file !~ '^\w:' && file !~ '://' exe cmd."`=fnamemodify(rails#app().path(file),':.')`" else exe cmd.file endif return '' endfunction function! s:Alternate(cmd,line1,line2,count,...) if a:0 if a:count && a:cmd !~# 'D' return call('s:Find',[1,a:line1.a:cmd]+a:000) elseif a:count return call('s:Edit',[1,a:line1.a:cmd]+a:000) else return call('s:Edit',[1,a:cmd]+a:000) endif else let file = get(b:, a:count ? 'rails_related' : 'rails_alternate') if empty(file) let file = rails#buffer().alternate(a:count) endif if !empty(file) call s:findedit(a:cmd,file) else call s:warn("No alternate file is defined") endif endif endfunction function! s:Related(cmd,line1,line2,count,...) if a:count == 0 && a:0 == 0 return s:Alternate(a:cmd,a:line1,a:line1,a:line1) else return call('s:Alternate',[a:cmd,a:line1,a:line2,a:count]+a:000) endif endfunction function! s:Complete_related(A,L,P) if a:L =~# '^[[:alpha:]]' return s:Complete_edit(a:A,a:L,a:P) else return s:Complete_find(a:A,a:L,a:P) endif endfunction function! s:readable_alternate_candidates(...) dict abort let f = self.name() let placeholders = {} if a:0 && a:1 let lastmethod = self.last_method(a:1) if !empty(lastmethod) let placeholders.d = lastmethod endif let projected = self.projected('related', placeholders) if !empty(projected) return projected endif if self.type_name('controller','mailer') && lastmethod != "" let view = self.resolve_view(lastmethod, line('.')) if view !=# '' return [view] else return [s:sub(s:sub(s:sub(f,'/application%(_controller)=\.rb$','/shared_controller.rb'),'/%(controllers|models|mailers)/','/views/'),'%(_controller)=\.rb$','/'.lastmethod)] endif elseif f =~# '^config/environments/' return ['config/database.yml#'. fnamemodify(f,':t:r')] elseif f ==# 'config/database.yml' if lastmethod != "" return ['config/environments/'.lastmethod.'.rb'] else return ['config/application.rb', 'config/environment.rb'] endif elseif self.type_name('view-layout') return [s:sub(s:sub(f,'/views/','/controllers/'),'/layouts/(\k+)\..*$','/\1_controller.rb')] elseif self.type_name('view') return [s:sub(s:sub(f,'/views/','/controllers/'),'/(\k+%(\.\k+)=)\..*$','_controller.rb#\1'), \ s:sub(s:sub(f,'/views/','/mailers/'),'/(\k+%(\.\k+)=)\..*$','.rb#\1'), \ s:sub(s:sub(f,'/views/','/models/'),'/(\k+)\..*$','.rb#\1')] return [controller, controller2, mailer, model] elseif self.type_name('controller') return [s:sub(s:sub(f,'/controllers/','/helpers/'),'%(_controller)=\.rb$','_helper.rb')] elseif self.type_name('model-arb') let table_name = matchstr(join(self.getline(1,50),"\n"),'\n\s*self\.table_name\s*=\s*[:"'']\zs\w\+') if table_name == '' let table_name = rails#pluralize(s:gsub(s:sub(fnamemodify(f,':r'),'.{-} me') let migration = "db/migrate/".get(candidates,0,migrations[0]).".rb" endif return [migration . (exists('lastmethod') && !empty(lastmethod) ? '#'.lastmethod : '')] 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 rails_root = rails#app().path() let ext = expand("%:e") let file = s:sub(a:1,'%(/|^)\zs_\ze[^/]*$','') let first = a:firstline let last = a:lastline let range = first.",".last if rails#buffer().type_name('view-layout') if RailsFilePath() =~ '\' let curdir = 'app/views/shared' if file !~ '/' let file = "shared/" .file endif else let curdir = s:sub(RailsFilePath(),'.*" 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" || ext == "slim" let renderstr = "= ".renderstr elseif ext == "mn" let renderstr = "_".renderstr endif let buf = @@ silent exe range."yank" let partial = @@ let @@ = buf let old_ai = &ai try let &ai = 0 silent exe "norm! :".first.",".last."change\".spaces.renderstr."\.\" finally let &ai = old_ai endtry if renderstr =~ '<%' norm ^6w else norm ^5w endif let ft = &ft let shortout = fnamemodify(out,':.') silent execute 'split '.s:fnameescape(shortout) silent %delete _ let &ft = ft let @@ = partial silent put 0delete let @@ = buf if spaces != "" silent! exe '%substitute/^'.spaces.'//' endif 1 endfunction function! s:RubyExtract(bang, root, before, name) range abort let content = getline(a:firstline, a:lastline) execute a:firstline.','.a:lastline.'delete_' let indent = get(sort(map(filter(copy(content), '!empty(v:val)'), 'len(matchstr(v:val, "^ \\+"))')), 0, 0) if indent call map(content, 's:sub(v:val, "^".repeat(" ", indent), " ")') endif call append(a:firstline-1, repeat(' ', indent).'include '.rails#camelize(a:name)) let out = rails#app().path(a:root, a:name . '.rb') if filereadable(out) && !a:bang return s:error('E13: File exists (add ! to override)') endif if !isdirectory(fnamemodify(out, ':h')) call mkdir(fnamemodify(out, ':h'), 'p') endif execute 'split '.s:fnameescape(out) silent %delete_ call setline(1, ['module '.rails#camelize(a:name)] + a:before + content + ['end']) endfunction " }}}1 " Migration Inversion {{{1 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 .= 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::IrreversibleMigration') elseif add == " " let add = "" endif let str = add."\n".str let lnum += 1 endwhile let str = s:gsub(str,'(\s*raise ActiveRecord::IrreversibleMigration\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 foldclosed(beg) > 0 exe beg."foldopen!" endif if beg + 1 < end exe (beg+1).",".(end-1)."delete _" endif if str != '' exe beg.'put =str' exe 1+beg endif endfunction " }}}1 " Cache {{{1 let s:cache_prototype = {'dict': {}} function! s:cache_clear(...) dict if a:0 == 0 let self.dict = {} elseif has_key(self,'dict') && has_key(self.dict,a:1) unlet! self.dict[a:1] endif endfunction function! rails#cache_clear(...) if exists('b:rails_root') return call(rails#app().cache.clear,a:000,rails#app().cache) endif endfunction function! s:cache_get(...) dict if a:0 == 1 return self.dict[a:1] else return self.dict endif endfunction function! s:cache_has(key) dict return has_key(self.dict,a:key) endfunction function! s:cache_needs(key) dict return !has_key(self.dict,a:key) endfunction function! s:cache_set(key,value) dict let self.dict[a:key] = a:value endfunction call s:add_methods('cache', ['clear','needs','has','get','set']) let s:app_prototype.cache = s:cache_prototype " }}}1 " Syntax {{{1 function! s:resetomnicomplete() if exists("+completefunc") && &completefunc == 'syntaxcomplete#Complete' if exists("g:loaded_syntax_completion") " Ugly but necessary, until we have our own completion unlet g:loaded_syntax_completion silent! delfunction syntaxcomplete#Complete endif endif endfunction function! s:helpermethods() return "" \."action_name asset_path asset_url atom_feed audio_path audio_tag audio_url auto_discovery_link_tag " \."button_tag button_to button_to_function " \."cache cache_fragment_name cache_if cache_unless capture cdata_section check_box check_box_tag collection_check_boxes collection_radio_buttons collection_select color_field color_field_tag compute_asset_extname compute_asset_host compute_asset_path 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 " \."date_field date_field_tag date_select datetime_field datetime_field_tag datetime_local_field datetime_local_field_tag datetime_select debug distance_of_time_in_words distance_of_time_in_words_to_now div_for dom_class dom_id " \."email_field email_field_tag escape_javascript escape_once excerpt " \."favicon_link_tag field_set_tag fields_for file_field file_field_tag flash font_path font_url form_for form_tag " \."grouped_collection_select grouped_options_for_select " \."headers hidden_field hidden_field_tag highlight " \."image_alt image_path image_submit_tag image_tag image_url " \."j javascript_cdata_section javascript_include_tag javascript_path javascript_tag javascript_url " \."l label label_tag link_to link_to_function link_to_if link_to_unless link_to_unless_current localize logger " \."mail_to month_field month_field_tag " \."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 " \."option_groups_from_collection_for_select options_for_select options_from_collection_for_select " \."params password_field password_field_tag path_to_asset path_to_audio path_to_font path_to_image path_to_javascript path_to_stylesheet path_to_video phone_field phone_field_tag pluralize provide " \."radio_button radio_button_tag range_field range_field_tag raw render request request_forgery_protection_token reset_cycle response " \."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 stylesheet_url submit_tag " \."t tag telephone_field telephone_field_tag text_area text_area_tag text_field text_field_tag time_ago_in_words time_field time_field_tag time_select time_tag time_zone_options_for_select time_zone_select translate truncate " \."url_field url_field_tag url_for url_to_asset url_to_audio url_to_font url_to_image url_to_javascript url_to_stylesheet url_to_video utf8_enforcer_tag " \."video_path video_tag video_url " \."week_field week_field_tag word_wrap" endfunction function! s:app_user_classes() dict if self.cache.needs("user_classes") let controllers = self.relglob("app/controllers/","**/*",".rb") call map(controllers,'v:val == "application" ? v:val."_controller" : v:val') let classes = \ self.relglob("app/models/","**/*",".rb") + \ controllers + \ self.relglob("app/helpers/","**/*",".rb") + \ self.relglob("lib/","**/*",".rb") call map(classes,'rails#camelize(v:val)') call self.cache.set("user_classes",classes) endif return self.cache.get('user_classes') endfunction function! s:app_user_assertions() dict if self.cache.needs("user_assertions") if self.has_file("test/test_helper.rb") let assertions = map(filter(s:readfile(self.path("test/test_helper.rb")),'v:val =~ "^ def assert_"'),'matchstr(v:val,"^ def \\zsassert_\\w\\+")') else let assertions = [] endif call self.cache.set("user_assertions",assertions) endif return self.cache.get('user_assertions') endfunction call s:add_methods('app', ['user_classes','user_assertions']) function! s:BufSyntax() if !exists("g:rails_no_syntax") let buffer = rails#buffer() let javascript_functions = "$ jQuery" let classes = s:gsub(join(rails#app().user_classes(),' '),'::',' ') if &syntax == 'ruby' let keywords = split(join(buffer.projected('keywords'), ' ')) let special = filter(copy(keywords), 'v:val =~# ''^\h\k*[?!]$''') let regular = filter(copy(keywords), 'v:val =~# ''^\h\k*$''') if !empty(special) exe 'syn match rubyRailsMethod "\<\%('.join(special, '\|').'\)"' endif if !empty(regular) exe 'syn keyword rubyRailsMethod '.join(regular, ' ') endif if classes != '' exe "syn keyword rubyRailsUserClass ".classes." containedin=rubyClassDeclaration,rubyModuleDeclaration,rubyClass,rubyModule" endif if buffer.type_name() == '' syn keyword rubyRailsMethod params request response session headers cookies flash endif if buffer.type_name() ==# 'model' || buffer.type_name('model-arb') syn keyword rubyRailsARMethod default_scope named_scope scope serialize store syn keyword rubyRailsARAssociationMethod belongs_to has_one has_many has_and_belongs_to_many composed_of accepts_nested_attributes_for 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 rubyRailsARCallbackMethod around_create around_destroy around_save around_update syn keyword rubyRailsARCallbackMethod after_commit after_find after_initialize after_rollback after_touch syn keyword rubyRailsARClassMethod attr_accessible attr_protected attr_readonly has_secure_password store_accessor 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 validates_with syn keyword rubyRailsMethod logger endif if buffer.type_name('model-aro') syn keyword rubyRailsARMethod observe endif if buffer.type_name('mailer') syn keyword rubyRailsMethod logger url_for polymorphic_path polymorphic_url syn keyword rubyRailsRenderMethod mail render syn keyword rubyRailsControllerMethod attachments default helper helper_attr helper_method endif if buffer.type_name('helper','view') syn keyword rubyRailsViewMethod polymorphic_path polymorphic_url exe "syn keyword rubyRailsHelperMethod ".s:gsub(s:helpermethods(),'<%(content_for|select)\s+','') syn match rubyRailsHelperMethod '\\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!' syn match rubyRailsHelperMethod '\<\%(content_for?\=\|current_page?\)' syn match rubyRailsViewMethod '\.\@' if buffer.type_name('view-partial') syn keyword rubyRailsMethod local_assigns endif elseif buffer.type_name('controller') syn keyword rubyRailsMethod params request response session headers cookies flash syn keyword rubyRailsRenderMethod render syn keyword rubyRailsMethod logger polymorphic_path polymorphic_url 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 syn keyword rubyRailsRenderMethod head redirect_to render_to_string respond_with 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 skip_filter syn keyword rubyRailsFilterMethod verify endif if buffer.type_name('db-migration','db-schema') syn keyword rubyRailsMigrationMethod create_table change_table drop_table rename_table create_join_table drop_join_table syn keyword rubyRailsMigrationMethod add_column rename_column change_column change_column_default remove_column remove_columns syn keyword rubyRailsMigrationMethod add_timestamps remove_timestamps syn keyword rubyRailsMigrationMethod add_reference remove_reference add_belongs_to remove_belongs_to syn keyword rubyRailsMigrationMethod add_index remove_index rename_index syn keyword rubyRailsMigrationMethod execute transaction reversible revert endif if buffer.type_name('test') if !empty(rails#app().user_assertions()) exe "syn keyword rubyRailsUserMethod ".join(rails#app().user_assertions()) endif syn keyword rubyRailsTestMethod refute refute_empty refute_equal refute_in_delta refute_in_epsilon refute_includes refute_instance_of refute_kind_of refute_match refute_nil refute_operator refute_predicate refute_respond_to refute_same 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 syn keyword rubyRailsTestMethod test setup teardown if !buffer.type_name('test-unit') syn match rubyRailsTestControllerMethod '\.\@' syn keyword rubyRailsTestControllerMethod get_via_redirect post_via_redirect put_via_redirect delete_via_redirect request_via_redirect 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 endif elseif buffer.type_name('spec') syn keyword rubyRailsTestMethod describe context it its specify shared_context shared_examples_for it_should_behave_like it_behaves_like before after around subject fixtures controller_name helper_name scenario feature background syn match rubyRailsTestMethod '\!\=' syn keyword rubyRailsTestMethod violated pending expect double mock mock_model stub_model syn match rubyRailsTestMethod '\.\@!\@!' if !buffer.type_name('spec-model') syn match rubyRailsTestControllerMethod '\.\@' syn keyword rubyRailsTestControllerMethod integrate_views render_views syn keyword rubyRailsMethod params request response session flash syn keyword rubyRailsMethod polymorphic_path polymorphic_url endif endif if buffer.type_name('task') syn match rubyRailsRakeMethod '^\s*\zs\%(task\|file\|namespace\|desc\|before\|after\|on\)\>\%(\s*=\)\@!' endif if buffer.type_name('config-routes') syn match rubyRailsMethod '\.\zs\%(connect\|named_route\)\>' syn keyword rubyRailsMethod match get put patch post delete redirect root resource resources collection member nested scope namespace controller constraints mount concern 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 superclass_delegating_accessor superclass_delegating_reader superclass_delegating_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 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 cluster htmlArgCluster add=@rubyStringSpecial syn cluster htmlPreProc add=@rubyStringSpecial elseif &syntax =~# '^eruby\>' || &syntax == 'haml' syn case match if classes != '' exe 'syn keyword '.&syntax.'RailsUserClass '.classes.' contained containedin=@'.&syntax.'RailsRegions' endif if &syntax == 'haml' exe 'syn cluster hamlRailsRegions contains=hamlRubyCodeIncluded,hamlRubyCode,hamlRubyHash,@hamlEmbeddedRuby,rubyInterpolation' else exe 'syn cluster erubyRailsRegions contains=erubyOneLiner,erubyBlock,erubyExpression,rubyInterpolation' endif exe 'syn keyword '.&syntax.'RailsHelperMethod '.s:gsub(s:helpermethods(),'<%(content_for|select)\s+','').' contained containedin=@'.&syntax.'RailsRegions' exe 'syn match '.&syntax.'RailsHelperMethod "\\%(\s*{\|\s*do\>\|\s*(\=\s*&\)\@!" contained containedin=@'.&syntax.'RailsRegions' exe 'syn match '.&syntax.'RailsHelperMethod "\<\%(content_for?\=\|current_page?\)" contained containedin=@'.&syntax.'RailsRegions' exe 'syn keyword '.&syntax.'RailsMethod debugger polymorphic_path polymorphic_url contained containedin=@'.&syntax.'RailsRegions' exe 'syn match '.&syntax.'RailsViewMethod "\.\@" contained containedin=@'.&syntax.'RailsRegions' if buffer.type_name('view-partial') exe 'syn keyword '.&syntax.'RailsMethod local_assigns contained containedin=@'.&syntax.'RailsRegions' endif exe 'syn keyword '.&syntax.'RailsRenderMethod render contained containedin=@'.&syntax.'RailsRegions' exe 'syn case match' set isk+=$ exe 'syn keyword javascriptRailsFunction contained '.javascript_functions exe 'syn cluster htmlJavaScript add=javascriptRailsFunction' elseif &syntax == "yaml" syn case match 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 javascriptRailsFunction contained ".javascript_functions syn cluster htmlJavaScript add=javascriptRailsFunction elseif &syntax == "javascript" || &syntax == "coffee" " The syntax file included with Vim incorrectly sets syn case ignore. syn case match set isk+=$ exe "syn keyword javascriptRailsFunction ".javascript_functions elseif &syntax == "scss" || &syntax == "sass" syn match sassFunction "\<\%(\%(asset\|image\|font\|video\|audio\|javascript\|stylesheet\)-\%(url\|path\)\)\>(\@=" contained syn match sassFunction "\<\asset-data-url\>(\@=" contained 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 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 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 erubyRailsUserClass railsUserClass hi def link hamlRailsHelperMethod hamlRailsMethod hi def link hamlRailsViewMethod hamlRailsMethod hi def link hamlRailsRenderMethod hamlRailsMethod hi def link hamlRailsMethod railsMethod hi def link hamlRailsUserMethod railsUserMethod hi def link hamlRailsUserClass railsUserClass hi def link railsUserMethod railsMethod 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 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! rails#log_syntax() if has('conceal') syn match railslogEscape '\e\[[0-9;]*m' conceal syn match railslogEscapeMN '\e\[[0-9;]*m' conceal nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained syn match railslogEscapeSQL '\e\[[0-9;]*m' conceal nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained else syn match railslogEscape '\e\[[0-9;]*m' syn match railslogEscapeMN '\e\[[0-9;]*m' nextgroup=railslogModelNum,railslogEscapeMN skipwhite contained syn match railslogEscapeSQL '\e\[[0-9;]*m' nextgroup=railslogSQL,railslogEscapeSQL skipwhite contained endif syn match railslogRender '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=\%(Processing\|Rendering\|Rendered\|Redirected\|Completed\)\>' syn match railslogComment '^\s*# .*' 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 syn match railslogModel '\%(^\s*\%(\e\[[0-9;]*m\)\=\)\@<=SQL\>' skipwhite nextgroup=railslogModelNum,railslogEscapeMN syn region railslogModelNum start='(' end=')' contains=railslogNumber contained skipwhite nextgroup=railslogSQL,railslogEscapeSQL syn match railslogSQL '\u[^\e]*' contained " Destroy generates multiline SQL, ugh syn match railslogSQL '\%(^ \%(\e\[[0-9;]*m\)\=\)\@<=\%(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 railslogEscapeMN railslogEscape hi def link railslogEscapeSQL railslogEscape hi def link railslogEscape Ignore 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 " Mappings {{{1 function! s:BufMappings() nnoremap RailsFind :call Find(v:count1,'E') nnoremap RailsSplitFind :call Find(v:count1,'S') nnoremap RailsVSplitFind :call Find(v:count1,'V') nnoremap RailsTabFind :call Find(v:count1,'T') if !hasmapto("RailsFind") nmap gf RailsFind endif if !hasmapto("RailsSplitFind") nmap f RailsSplitFind endif if !hasmapto("RailsTabFind") nmap gf RailsTabFind endif endfunction " }}}1 " Database {{{1 function! s:extractdbvar(str,arg) return matchstr("\n".a:str."\n",'\n'.a:arg.'=\zs.\{-\}\ze\n') endfunction function! s:app_dbext_settings(environment) dict if self.cache.needs('dbext_settings') call self.cache.set('dbext_settings',{}) endif let cache = self.cache.get('dbext_settings') if !has_key(cache,a:environment) let dict = {} if self.has_path("config/database.yml") let cmdb = 'require %{yaml}; File.open(%q{'.self.path().'/config/database.yml}) {|f| y = YAML::load(f); e = y[%{' 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}}' let out = self.lightweight_ruby_eval(cmdb.a:environment.cmde) let adapter = s:extractdbvar(out,'adapter') let adapter = get({'mysql2': 'mysql', 'postgresql': 'pgsql', 'sqlite3': 'sqlite', 'sqlserver': 'sqlsrv', 'sybase': 'asa', 'oracle': 'ora', 'oracle_enhanced': 'ora'},adapter,adapter) let dict['type'] = toupper(adapter) let dict['user'] = s:extractdbvar(out,'username') let dict['passwd'] = s:extractdbvar(out,'password') if dict['passwd'] == '' && adapter == 'mysql' " Hack to override password from .my.cnf let dict['extra'] = ' --password=' else let dict['extra'] = '' endif let dict['dbname'] = s:extractdbvar(out,'database') if dict['dbname'] == '' let dict['dbname'] = s:extractdbvar(out,'dbfile') endif if dict['dbname'] != '' && dict['dbname'] !~ '^:' && adapter =~? '^sqlite' let dict['dbname'] = self.path(dict['dbname']) endif let dict['profile'] = '' if adapter == 'ora' let dict['srvname'] = s:extractdbvar(out,'database') else let dict['srvname'] = s:extractdbvar(out,'host') endif let dict['host'] = s:extractdbvar(out,'host') let dict['port'] = s:extractdbvar(out,'port') let dict['dsnname'] = s:extractdbvar(out,'dsn') if dict['host'] =~? '^\cDBI:' if dict['host'] =~? '\c\' let dict['integratedlogin'] = 1 endif let dict['host'] = matchstr(dict['host'],'\c\<\%(Server\|Data Source\)\s*=\s*\zs[^;]*') endif call filter(dict,'v:val != ""') endif let cache[a:environment] = dict endif return cache[a:environment] endfunction function! s:BufDatabase(...) if exists("s:lock_database") || !exists('g:loaded_dbext') || !exists('b:rails_root') return endif let self = rails#app() if (a:0 && a:1 > 1) call self.cache.clear('dbext_settings') endif if (a:0 > 1 && a:2 != '') let env = a:2 else let env = s:environment() endif if (!self.cache.has('dbext_settings') || !has_key(self.cache.get('dbext_settings'),env)) && (a:0 ? a:1 : 0) <= 0 return endif let dict = self.dbext_settings(env) for key in ['type', 'profile', 'bin', 'user', 'passwd', 'dbname', 'srvname', 'host', 'port', 'dsnname', 'extra', 'integratedlogin'] let b:dbext_{key} = get(dict,key,'') endfor if b:dbext_type == 'SQLITE' " dbext seems to have overlooked the release of sqlite3 a decade ago let g:dbext_default_SQLITE_bin = "sqlite3" endif if b:dbext_type == 'PGSQL' let $PGPASSWORD = b:dbext_passwd elseif exists('$PGPASSWORD') let $PGPASSWORD = '' endif endfunction call s:add_methods('app', ['dbext_settings']) " }}}1 " Abbreviations {{{1 function! s:selectiveexpand(pat,good,default,...) if a:0 > 0 let nd = a:1 else let nd = "" endif let c = nr2char(getchar(0)) let good = a:good if c == "" " ^] return s:sub(good.(a:0 ? " ".a:1 : ''),'\s+$','') elseif c == "\t" return good.(a:0 ? " ".a:1 : '') elseif c =~ a:pat return good.c.(a:0 ? a:1 : '') else return a:default.c endif endfunction function! s:AddSelectiveExpand(abbr,pat,expn,...) let expn = s:gsub(s:gsub(a:expn ,'[\"|]','\\&'),'\<','\\') let expn2 = s:gsub(s:gsub(a:0 ? a:1 : '','[\"|]','\\&'),'\<','\\') if a:0 exe "inoreabbrev ".a:abbr." =selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).",\"".expn2."\")" else exe "inoreabbrev ".a:abbr." =selectiveexpand(".string(a:pat).",\"".expn."\",".string(a:abbr).")" endif endfunction function! s:AddTabExpand(abbr,expn) call s:AddSelectiveExpand(a:abbr,'..',a:expn) endfunction function! s:AddBracketExpand(abbr,expn) call s:AddSelectiveExpand(a:abbr,'[[.]',a:expn) endfunction function! s:AddColonExpand(abbr,expn) call s:AddSelectiveExpand(a:abbr,'[:.]',a:expn) endfunction function! s:AddParenExpand(abbr,expn,...) if a:0 call s:AddSelectiveExpand(a:abbr,'(',a:expn,a:1) else call s:AddSelectiveExpand(a:abbr,'(',a:expn,'') endif endfunction if !exists('g:rails_no_abbreviations') && type(get(g:, 'rails_abbreviations', {})) == type(0) call s:error('Use rails_no_abbreviations not rails_abbreviations to disable abbreviations') let g:rails_no_abbreviations = 1 endif function! s:BufAbbreviations() " Some of these were cherry picked from the TextMate snippets if !exists('g:rails_no_abbreviations') let buffer = rails#buffer() " Limit to the right filetypes. But error on the liberal side if buffer.type_name('controller','view','helper','test-functional','test-integration') Rabbrev pa[ params Rabbrev rq[ request Rabbrev rs[ response Rabbrev se[ session Rabbrev hd[ headers Rabbrev coo[ cookies Rabbrev fl[ flash Rabbrev rr( render Rabbrev rf( render :file\ =>\ Rabbrev rj( render :json\ =>\ Rabbrev rp( render :partial\ =>\ Rabbrev rt( render :text\ =>\ Rabbrev rx( render :xml\ =>\ endif if buffer.type_name('view','helper') Rabbrev dotiw distance_of_time_in_words Rabbrev taiw time_ago_in_words endif if buffer.type_name('controller') Rabbrev re( redirect_to Rabbrev rst( respond_to endif if buffer.type_name() ==# 'model' || buffer.type_name('model-arb') Rabbrev bt( belongs_to Rabbrev ho( has_one Rabbrev hm( has_many Rabbrev habtm( has_and_belongs_to_many Rabbrev co( composed_of Rabbrev va( validates_associated Rabbrev vb( validates_acceptance_of Rabbrev vc( validates_confirmation_of Rabbrev ve( validates_exclusion_of Rabbrev vf( validates_format_of Rabbrev vi( validates_inclusion_of Rabbrev vl( validates_length_of Rabbrev vn( validates_numericality_of Rabbrev vp( validates_presence_of Rabbrev vu( validates_uniqueness_of endif if buffer.type_name('db-migration','db-schema') Rabbrev mac( add_column Rabbrev mrnc( rename_column Rabbrev mrc( remove_column Rabbrev mct( create_table Rabbrev mcht( change_table Rabbrev mrnt( rename_table Rabbrev mdt( drop_table endif if buffer.type_name('test') Rabbrev ase( assert_equal Rabbrev asko( assert_kind_of Rabbrev asnn( assert_not_nil Rabbrev asr( assert_raise Rabbrev asre( assert_response Rabbrev art( assert_redirected_to endif Rabbrev logd( logger.debug Rabbrev logi( logger.info Rabbrev logw( logger.warn Rabbrev loge( logger.error Rabbrev logf( logger.fatal Rabbrev AR:: ActiveRecord Rabbrev AV:: ActionView Rabbrev AC:: ActionController Rabbrev AD:: ActionDispatch Rabbrev AS:: ActiveSupport Rabbrev AM:: ActionMailer Rabbrev AO:: ActiveModel for pairs in \ items(type(get(g:, 'rails_abbreviations', 0)) == type({}) ? g:rails_abbreviations : {}) call call(function(s:sid.'Abbrev'), [0, pairs[0]] + s:split(pairs[1])) endfor for hash in reverse(rails#buffer().projected('abbreviations')) for pairs in items(hash) call call(function(s:sid.'Abbrev'), [0, pairs[0]] + s:split(pairs[1])) endfor endfor endif endfunction function! s:Abbrev(bang,...) abort if !exists("b:rails_abbreviations") let b:rails_abbreviations = {} endif if a:0 > 3 || (a:bang && (a:0 != 1)) return s:error("Rabbrev: invalid arguments") endif if a:0 == 0 for key in sort(keys(b:rails_abbreviations)) echo key . join(b:rails_abbreviations[key],"\t") endfor return endif let lhs = a:1 let root = s:sub(lhs,'%(::|\(|\[)$','') if a:bang if has_key(b:rails_abbreviations,root) call remove(b:rails_abbreviations,root) endif exe "iunabbrev ".root return endif if a:0 > 3 || a:0 < 2 return s:error("Rabbrev: invalid arguments") endif let rhs = a:2 if has_key(b:rails_abbreviations,root) call remove(b:rails_abbreviations,root) endif if lhs =~ '($' let b:rails_abbreviations[root] = ["(", rhs . (a:0 > 2 ? "\t".a:3 : "")] if a:0 > 2 call s:AddParenExpand(root,rhs,a:3) else call s:AddParenExpand(root,rhs) endif return endif if a:0 > 2 return s:error("Rabbrev: invalid arguments") endif if lhs =~ ':$' call s:AddColonExpand(root,rhs) elseif lhs =~ '\[$' call s:AddBracketExpand(root,rhs) elseif lhs =~ '\w$' call s:AddTabExpand(lhs,rhs) else return s:error("Rabbrev: unimplemented") endif let b:rails_abbreviations[root] = [matchstr(lhs,'\W*$'),rhs] endfunction " }}}1 " Projections {{{1 function! rails#json_parse(string) abort let [null, false, true] = ['', 0, 1] let string = type(a:string) == type([]) ? join(a:string, ' ') : a:string let stripped = substitute(string,'\C"\(\\.\|[^"\\]\)*"','','g') if stripped !~# "[^,:{}\\[\\]0-9.\\-+Eaeflnr-u \n\r\t]" try return eval(substitute(string,"[\r\n]"," ",'g')) catch endtry endif throw "invalid JSON: ".string endfunction function! s:app_gems() dict abort if self.has('bundler') && exists('*bundler#project') return bundler#project(self.path()).gems() else return {} endif endfunction function! s:app_has_gem(gem) dict abort if self.has('bundler') && exists('*bundler#project') let project = bundler#project(self.path()) if has_key(project, 'has') return project.has(a:gem) elseif has_key(project, 'gems') return has_key(bundler#project(self.path()).gems(), a:gem) endif else return 0 endif endfunction function! s:app_engines() dict abort let gems = escape(join(values(self.gems()),','), ' ') if empty(gems) return [] else return sort(map(finddir('app', gems, -1), 'fnamemodify(v:val, ":h")')) endif endfunction function! s:extend_projection(dest, src) let dest = copy(a:dest) for key in keys(a:src) if !has_key(dest, key) || key ==# 'affinity' let dest[key] = a:src[key] elseif type(a:src[key]) == type({}) && type(dest[key]) == type({}) let dest[key] = extend(copy(dest[key]), a:src[key]) else let dest[key] = s:uniq(s:getlist(a:src, key) + s:getlist(dest, key)) endif endfor return dest endfunction function! s:combine_projections(dest, src, ...) abort let extra = a:0 ? a:1 : {} if type(a:src) == type({}) for [pattern, original] in items(a:src) let projection = extend(copy(original), extra) if !has_key(projection, 'prefix') && !has_key(projection, 'format') let a:dest[pattern] = s:extend_projection(get(a:dest, pattern, {}), projection) endif endfor endif return a:dest endfunction let s:projections_for_gems = {} function! s:app_projections() dict abort let dict = {} call s:combine_projections(dict, get(g:, 'rails_projections', ''), {'check': 1}) for gem in keys(get(g:, 'rails_gem_projections', {})) if self.has_gem(gem) call s:combine_projections(dict, g:rails_gem_projections[gem]) endif endfor let gem_path = escape(join(values(self.gems()),','), ' ') if !empty(gem_path) if !has_key(s:projections_for_gems, gem_path) let gem_projections = {} for path in ['lib/', 'lib/rails/'] for file in findfile(path.'projections.json', gem_path, -1) try call s:combine_projections(gem_projections, rails#json_parse(readfile(self.path(file)))) catch endtry endfor endfor let s:projections_for_gems[gem_path] = gem_projections endif call s:combine_projections(dict, s:projections_for_gems[gem_path]) endif if self.cache.needs('projections') call self.cache.set('projections', {}) let projections = {} if self.has_path('config/projections.json') try let projections = rails#json_parse(readfile(self.path('config/projections.json'))) if type(projections) == type({}) call self.cache.set('projections', projections) endif catch /^invalid JSON:/ endtry endif endif call s:combine_projections(dict, self.cache.get('projections')) return dict endfunction call s:add_methods('app', ['gems', 'has_gem', 'engines', 'projections']) function! s:expand_placeholders(string, placeholders) if type(a:string) !=# type('') return a:string endif let ph = extend({'%': '%'}, a:placeholders) let value = substitute(a:string, '%\([^: ]\)', '\=get(ph, submatch(1), "\001")', 'g') return value =~# "\001" ? '' : value endfunction function! s:readable_projected(key, ...) dict abort let f = self.name() let all = self.app().projections() let mine = [] if has_key(all, f) let mine += map(s:getlist(all[f], a:key), 's:expand_placeholders(v:val, a:0 ? a:1 : 0)') endif for pattern in reverse(sort(filter(keys(all), 'v:val =~# "^[^*]*\\*[^*]*$"'), s:function('rails#lencmp'))) let [prefix, suffix; _] = split(pattern, '\*', 1) if s:startswith(f, prefix) && s:endswith(f, suffix) let root = f[strlen(prefix) : -strlen(suffix)-1] let ph = extend({ \ 's': root, \ 'S': rails#camelize(root), \ 'h': toupper(root[0]) . tr(rails#underscore(root), '_', ' ')[1:-1], \ 'p': rails#pluralize(root), \ 'i': rails#singularize(root), \ '%': '%'}, a:0 ? a:1 : {}) if suffix =~# '\.js\>' let ph.S = s:gsub(ph.S, '::', '.') endif let mine += map(s:getlist(all[pattern], a:key), 's:expand_placeholders(v:val, ph)') endif endfor return filter(mine, '!empty(v:val)') endfunction call s:add_methods('readable', ['projected']) function! s:Set(bang,...) call s:warn('Rset is obsolete and has no effect') endfunction " }}}1 " Detection {{{1 function! RailsBufInit(path) let firsttime = !(exists("b:rails_root") && b:rails_root == a:path) let b:rails_root = a:path if !has_key(s:apps,a:path) let s:apps[a:path] = deepcopy(s:app_prototype) let s:apps[a:path].root = a:path let s:apps[a:path]._root = a:path endif let app = s:apps[a:path] let buffer = rails#buffer() " Apparently rails#buffer().calculate_file_type() can be slow if the " underlying file system is slow (even though it doesn't really do anything " IO related). This caching is a temporary hack; if it doesn't cause " problems it should probably be refactored. let b:rails_cached_file_type = buffer.calculate_file_type() if expand('%:t') =~ '\.yml\.example$' setlocal filetype=yaml elseif expand('%:e') =~ '^\%(rjs\|rxml\|builder\|jbuilder\)$' setlocal filetype=ruby elseif firsttime " Activate custom syntax let &syntax = &syntax endif if expand('%:e') == 'log' nnoremap R :checktime nnoremap G :checktime$ nnoremap q :bwipe setlocal modifiable filetype=railslog noswapfile autoread foldmethod=syntax if exists('+concealcursor') setlocal concealcursor=nc conceallevel=2 else silent %s/\%(\e\[[0-9;]*m\|\r$\)//ge endif setlocal readonly nomodifiable $ endif call s:BufSettings() call s:BufMappings() call s:BufCommands() if !empty(findfile('macros/rails.vim', escape(&runtimepath, ' '))) runtime! macros/rails.vim endif silent doautocmd User Rails call s:BufProjectionCommands() call s:BufAbbreviations() return b:rails_root endfunction function! s:SetBasePath() abort let self = rails#buffer() if self.app().path() =~ '://' return endif let transformed_path = s:pathsplit(s:pathjoin([self.app().path()]))[0] let add_dot = self.getvar('&path') =~# '^\.\%(,\|$\)' let old_path = s:pathsplit(s:sub(self.getvar('&path'),'^\.%(,|$)','')) let path = ['lib', 'vendor'] let path += get(g:, 'rails_path_additions', []) let path += get(g:, 'rails_path', []) let path += ['app/models/concerns', 'app/controllers/concerns', 'app/controllers', 'app/helpers', 'app/mailers', 'app/models'] for [key, projection] in items(self.app().projections()) if get(projection, 'path', 0) is 1 || get(projection, 'autoload', 0) is 1 let path += split(key, '*')[0] endif endfor let path += filter(self.projected('path'), 'type(v:val) == type("")') let path += ['app/*', 'app/views'] if self.controller_name() != '' let path += ['app/views/'.self.controller_name(), 'public'] endif if self.app().has('test') let path += ['test', 'test/unit', 'test/functional', 'test/integration', 'test/controllers', 'test/helpers', 'test/mailers', 'test/models'] endif if self.app().has('spec') let path += ['spec', 'spec/controllers', 'spec/helpers', 'spec/mailers', 'spec/models', 'spec/views', 'spec/lib', 'spec/features', 'spec/requests', 'spec/integration'] endif if self.app().has('cucumber') let path += ['features'] endif let path += ['vendor/plugins/*/lib', 'vendor/plugins/*/test', 'vendor/rails/*/lib', 'vendor/rails/*/test'] let engine_paths = map(copy(self.app().engines()), 'v:val . "/app/*"') call self.setvar('&path',(add_dot ? '.,' : '').s:pathjoin(s:uniq(path + [self.app().path()] + old_path + engine_paths))) endfunction function! s:BufSettings() if !exists('b:rails_root') return '' endif let self = rails#buffer() call s:SetBasePath() let rp = s:gsub(self.app().path(),'[ ,]','\\&') if stridx(&tags,rp.'/tags') == -1 let &l:tags = rp . '/tags,' . rp . '/tmp/tags,' . &tags endif call self.setvar('&includeexpr','RailsIncludeexpr()') call self.setvar('&suffixesadd', s:sub(self.getvar('&suffixesadd'),'^$','.rb')) let ft = self.getvar('&filetype') if ft =~# '^\%(e\=ruby\|haml\)\>' && exists('+completefunc') && self.getvar('&completefunc') ==# '' && &g:completefunc ==# '' call self.setvar('&completefunc','syntaxcomplete#Complete') endif if ft =~# '^ruby\>' call self.setvar('&define',self.define_pattern()) " This really belongs in after/ftplugin/ruby.vim but we'll be nice if exists('g:loaded_surround') && self.getvar('surround_101') == '' call self.setvar('surround_5', "\r\nend") call self.setvar('surround_69', "\1expr: \1\rend") call self.setvar('surround_101', "\r\nend") endif if exists(':UltiSnipsAddFiletypes') UltiSnipsAddFiletypes rails endif elseif ft =~# 'yaml\>' || fnamemodify(self.name(),':e') ==# 'yml' call self.setvar('&define',self.define_pattern()) elseif ft =~# '^eruby\>' if exists("g:loaded_ragtag") call self.setvar('ragtag_stylesheet_link_tag', "<%= stylesheet_link_tag '\r' %>") call self.setvar('ragtag_javascript_include_tag', "<%= javascript_include_tag '\r' %>") call self.setvar('ragtag_doctype_index', 10) endif elseif ft =~# '^haml\>' if exists("g:loaded_ragtag") call self.setvar('ragtag_stylesheet_link_tag', "= stylesheet_link_tag '\r'") call self.setvar('ragtag_javascript_include_tag', "= javascript_include_tag '\r'") call self.setvar('ragtag_doctype_index', 10) endif endif if ft =~# '^eruby\>' || ft =~# '^yaml\>' if exists("g:loaded_surround") if self.getvar('surround_45') == '' || self.getvar('surround_45') == "<% \r %>" " - call self.setvar('surround_45', "<% \r %>") endif if self.getvar('surround_61') == '' " = call self.setvar('surround_61', "<%= \r %>") endif if self.getvar("surround_35") == '' " # call self.setvar('surround_35', "<%# \r %>") endif if self.getvar('surround_101') == '' || self.getvar('surround_101')== "<% \r %>\n<% end %>" "e call self.setvar('surround_5', "<% \r %>\n<% end %>") call self.setvar('surround_69', "<% \1expr: \1 %>\r<% end %>") call self.setvar('surround_101', "<% \r %>\n<% end %>") endif endif endif endfunction " }}}1 " Autocommands {{{1 augroup railsPluginAuto autocmd! autocmd User BufEnterRails call s:RefreshBuffer() autocmd User BufEnterRails call s:resetomnicomplete() autocmd User BufEnterRails call s:BufDatabase(-1) autocmd User dbextPreConnection call s:BufDatabase(1) autocmd BufWritePost */config/database.yml call rails#cache_clear("dbext_settings") autocmd BufWritePost */config/editor.json call rails#cache_clear("config") autocmd BufWritePost */test/test_helper.rb call rails#cache_clear("user_assertions") autocmd BufWritePost */config/routes.rb call rails#cache_clear("named_routes") autocmd BufWritePost */config/application.rb call rails#cache_clear("default_locale") autocmd BufWritePost */config/environments/*.rb call rails#cache_clear("environments") autocmd BufWritePost */tasks/**.rake call rails#cache_clear("rake_tasks") autocmd BufWritePost */generators/** call rails#cache_clear("generators") autocmd FileType * if exists("b:rails_root") | call s:BufSettings() | endif autocmd Syntax ruby,eruby,yaml,haml,javascript,coffee,railslog,sass,scss if exists("b:rails_root") | call s:BufSyntax() | endif augroup END " }}}1 " Initialization {{{1 map xx xx let s:sid = s:sub(maparg("xx"),'xx$','') unmap xx let s:file = expand(':p') if !exists('s:apps') let s:apps = {} endif " }}}1 " vim:set sw=2 sts=2: