Permalink
Fetching contributors…
Cannot retrieve contributors at this time
5056 lines (4644 sloc) 168 KB
" autoload/rails.vim
" Author: Tim Pope <http://tpo.pe/>
" Install this file as autoload/rails.vim.
if exists('g:autoloaded_rails') || &cp
finish
endif
let g:autoloaded_rails = '5.4'
" 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) abort
return function(substitute(a:name, '^s:', matchstr(expand('<sfile>'), '.*\zs<SNR>\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)
let key = string(a:list[i])
if has_key(seen, key)
call remove(a:list, i)
else
let seen[key] = 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)
endfunction
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:dot_relative(path) abort
let slash = matchstr(a:path, '^\%(\w\:\)\=\zs[\/]')
if !empty(slash)
let path = fnamemodify(a:path, ':.')
if path !=# a:path
return '.' . slash . path
endif
endif
return a:path
endfunction
function! s:mods(mods) abort
return s:gsub(a:mods, '[<]mods[>]\s*|^\s', '')
endfunction
function! s:webcat() abort
if !exists('s:webcat')
if executable('curl')
let s:webcat = 'curl'
elseif executable('wget')
let s:webcat = 'wget -qO-'
else
let s:webcat = ''
endif
endif
return s:webcat
endfunction
function! s:active() abort
return !empty(get(b:, 'rails_root'))
endfunction
function! s:fcall(fn, path, ...) abort
let ns = matchstr(a:path, '^\a\a\+\ze:')
if len(ns) && exists('*' . ns . '#' . a:fn)
return call(ns . '#' . a:fn, [a:path] + a:000)
else
return call(a:fn, [a:path] + a:000)
endif
endfunction
function! s:filereadable(path) abort
return s:fcall('filereadable', a:path)
endfunction
function! s:isdirectory(path) abort
return s:fcall('isdirectory', a:path)
endfunction
function! s:getftime(path) abort
return s:fcall('getftime', a:path)
endfunction
function! s:simplify(path) abort
return s:fcall('simplify', a:path)
endfunction
function! s:glob(path) abort
if v:version >= 704
return s:fcall('glob', a:path, 0, 1)
else
return split(s:fcall('glob', a:path), "\n")
endif
endfunction
function! s:mkdir_p(path) abort
if a:path !~# '^\a\a\+:' && !isdirectory(a:path)
call mkdir(a:path, 'p')
endif
endfunction
function! s:readfile(path, ...) abort
if !s:filereadable(a:path)
return []
elseif a:0
return s:fcall('readfile', a:path, '', a:1)
else
return s:fcall('readfile', a:path)
endif
endfunction
function! s:readbuf(path,...) abort
let nr = bufnr('^'.a:path.'$')
if nr < 0 && exists('+shellslash') && ! &shellslash
let nr = bufnr('^'.s:gsub(a:path,'/','\\').'$')
endif
if bufloaded(nr)
return getbufline(nr,1,a:0 ? a:1 : '$')
elseif a:0
return s:readfile(a:path, a:1)
else
return s:readfile(a:path)
endif
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 s:active() && (a:0 ? getcwd() !=# rails#app().path() : !s:startswith(getcwd(), rails#app().real()))
let chdir = exists("*haslocaldir") && haslocaldir() ? "lchdir " : "chdir "
call add(s:command_stack,chdir.s:escarg(getcwd()))
exe chdir.s:escarg(rails#app().real())
else
call add(s:command_stack,"")
endif
endfunction
function! s:app_real(...) dict abort
let pre = substitute(matchstr(self._root, '^\a\a\+\ze:'), '^.', '\u&', '')
if empty(pre)
let real = self._root
elseif exists('*' . pre . 'Real')
let real = {pre}Real(self._root)
else
return ''
endif
return join([real]+a:000,'/')
endfunction
function! s:app_path(...) dict dict
if a:0 && a:1 =~# '\%(^\|^\w*:\)[\/]'
return a:1
else
return join([self._root]+a:000,'/')
endif
endfunction
function! s:app_spec(...) dict abort
if a:0 && a:1 =~# '\%(^\|^\w*:\)[\/]'
return a:1
else
return join([self._root]+a:000,'/')
endif
endfunction
function! s:app_root(...) dict abort
if a:0 && a:1 =~# '\%(^\|^\w*:\)[\/]'
return a:1
else
return join([self._root]+a:000,'/')
endif
endfunction
function! s:app_has_path(path) dict abort
return s:getftime(self.path(a:path)) != -1
endfunction
function! s:app_has_file(file) dict abort
let file = self.path(a:file)
return a:file =~# '/$' ? s:isdirectory(file) : s:filereadable(file)
endfunction
function! s:find_file(name, ...) abort
let path = s:pathsplit(a:0 ? a:1 : &path)
let index = 1
let default = ''
if a:0 > 1 && type(a:2) == type(0)
let index = a:2
elseif a:0 > 1 && type(a:2) == type('')
let default = a:2
endif
let results = []
for glob in path
for dir in s:glob(glob)
let dir = substitute(substitute(dir, '[\/]\=$', '/', ''), '^+\ze\a\a\+:', '', '')
for suf in [''] + (a:name =~# '/$' ? [] : s:pathsplit(get(a:000, 1, [])))
if s:fcall(a:name =~# '/$' ? 'isdirectory' : 'filereadable', dir . a:name . suf)
call add(results, dir . a:name . suf)
endif
if len(results) == index
return results[-1]
endif
endfor
endfor
endfor
return index == -1 ? results : default
endfunction
function! s:app_find_file(name, ...) dict abort
if a:0
let path = map(s:pathsplit(a:1),'self.path(v:val)')
else
let path = [self.path()]
endif
return s:find_file(a:name, path, a:0 > 1 ? a:2 : '')
endfunction
call s:add_methods('app',['real','path','spec','root','has_path','has_file','find_file'])
" Split a path into a list.
function! s:pathsplit(path) abort
if type(a:path) == type([]) | return copy(a:path) | endif
return split(s:gsub(a:path, '\\ ', ' '), ',')
endfunction
" Convert a list to a path.
function! s:pathjoin(...) abort
let i = 0
let path = ""
while i < a:0
if type(a:000[i]) == type([])
let path .= "," . escape(join(a:000[i], ','), ' ')
else
let path .= "," . a:000[i]
endif
let i += 1
endwhile
return substitute(path,'^,','','')
endfunction
function! s:readable_end_of(lnum) dict abort
if a:lnum == 0
return 0
endif
let cline = self.getline(a:lnum)
let spc = matchstr(cline,'^\s*')
let endpat = '\<end\>'
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
if self.name() =~# '\.\%(rb\|rake\)$'
let lend = self.end_of(line)
else
let lend = -1
endif
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
let s:sql_define = substitute(
\ '\v\c^\s*create %(or replace )=%(table|%(materialized |recursive )=view|%(unique |fulltext )=index|trigger|function|procedure|sequence|extension) %(if not exists )=%(\i+\.)=[`"]=',
\ ' ', '\\s+', 'g')
function! s:readable_define_pattern() dict abort
if self.name() =~# '\.yml\%(\.example\|sample\)\=$'
return '^\%(\h\k*:\)\@='
elseif self.name() =~# '\.sql$'
return s:sql_define
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.name() =~# '\.erb$'
let define .= '\|\<id=["'']\='
endif
if self.name() =~# '\.haml$'
let define .= '\|^\s*\%(%\w*\)\=\%(\.[[:alnum:]_-]\+\)*#'
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
if a:start
let format = matchstr(self.getline(a:start), '\%(:formats *=>\|\<formats:\) *\[\= *[:''"]\zs\w\+')
if format !=# ''
return format
endif
endif
if self.type_name('view')
let format = fnamemodify(self.path(),':r:e')
if empty(format)
return get({'rhtml': 'html', 'rxml': 'xml', 'rjs': 'js', 'haml': 'html'},
\ matchstr(self.path(),'\.\zs\w\+$'), '')
else
return format
endif
endif
if !a:start
return ''
endif
let rline = self.last_opening_line(a:start,'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|',self.last_method_line(a:start))
if rline
let variable = matchstr(self.getline(rline),'\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|')
let line = a:start
while line > 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') && !empty(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 =~# '^app/views/layouts/'
return s:sub(f,'^app/views/layouts/(.{-})\..*','\1')
elseif f =~# '^app/views/'
return s:sub(f,'^app/views/(.{-})/\w+%(\.[[:alnum:]_+]+)=\.\w+$','\1')
elseif f =~# '^app/helpers/.*_helper\.rb$'
return s:sub(f,'^app/helpers/(.{-})_helper\.rb$','\1')
elseif f =~# '^app/controllers/.*\.rb$'
return s:sub(f,'^app/controllers/(.{-})%(_controller)=\.rb$','\1')
elseif f =~# '^app/mailers/.*\.rb$'
return s:sub(f,'^app/mailers/(.{-})\.rb$','\1')
elseif f =~# '^\%(test\|spec\)/mailers/previews/.*_preview\.rb$'
return s:sub(f,'^%(test|spec)/mailers/previews/(.{-})_preview\.rb$','\1')
elseif f =~# '^app/jobs/.*\.rb$'
return s:sub(f,'^app/jobs/(.{-})%(_job)=\.rb$','\1')
elseif f =~# '^test/\%(functional\|controllers\)/.*_test\.rb$'
return s:sub(f,'^test/%(functional|controllers)/(.{-})%(_controller)=_test\.rb$','\1')
elseif f =~# '^test/\%(unit/\)\?helpers/.*_helper_test\.rb$'
return s:sub(f,'^test/%(unit/)?helpers/(.{-})_helper_test\.rb$','\1')
elseif f =~# '^spec/controllers/.*_spec\.rb$'
return s:sub(f,'^spec/controllers/(.{-})%(_controller)=_spec\.rb$','\1')
elseif f =~# '^spec/jobs/.*_spec\.rb$'
return s:sub(f,'^spec/jobs/(.{-})%(_job)=_spec\.rb$','\1')
elseif f =~# '^spec/helpers/.*_helper_spec\.rb$'
return s:sub(f,'^spec/helpers/(.{-})_helper_spec\.rb$','\1')
elseif f =~# '^spec/views/.*/\w\+_view_spec\.rb$'
return s:sub(f,'^spec/views/(.{-})/\w+_view_spec\.rb$','\1')
elseif f =~# '^app/models/.*\.rb$' && self.type_name('mailer')
return s:sub(f,'^app/models/(.{-})\.rb$','\1')
elseif f =~# '^\%(public\|app/assets\)/stylesheets/[^.]\+\.'
return s:sub(f,'^%(public|app/assets)/stylesheets/(.{-})\..*$','\1')
elseif f =~# '^\%(public\|app/assets\)/javascripts/.[^.]\+\.'
return s:sub(f,'^%(public|app/assets)/javascripts/(.{-})\..*$','\1')
elseif a:0 && a:1
return rails#pluralize(self.model_name())
endif
return ""
endfunction
function! s:model(...)
return rails#buffer().model_name(a:0 ? a:1 : 0)
endfunction
function! s:readable_model_name(...) dict abort
let f = self.name()
if has_key(self,'getvar') && !empty(self.getvar('rails_model'))
return self.getvar('rails_model')
endif
let [affinity, root] = self.find_affinity()
if affinity ==# 'model'
return root
elseif affinity ==# 'collection'
return rails#singularize(root)
endif
if f =~# '^app/models/.*_observer.rb$'
return s:sub(f,'^app/models/(.*)_observer\.rb$','\1')
elseif f =~# '^app/models/.*\.rb$'
return s:sub(f,'^app/models/(.*)\.rb$','\1')
elseif f =~# '^test/\%(unit\|models\)/.*_observer_test\.rb$'
return s:sub(f,'^test/unit/(.*)_observer_test\.rb$','\1')
elseif f =~# '^test/\%(unit\|models\)/.*_test\.rb$'
return s:sub(f,'^test/%(unit|models)/(.*)_test\.rb$','\1')
elseif f =~# '^spec/models/.*_spec\.rb$'
return s:sub(f,'^spec/models/(.*)_spec\.rb$','\1')
elseif f =~# '^\%(test\|spec\)/blueprints/.*\.rb$'
return s:sub(f,'^%(test|spec)/blueprints/(.{-})%(_blueprint)=\.rb$','\1')
elseif f =~# '^\%(test\|spec\)/exemplars/.*_exemplar\.rb$'
return s:sub(f,'^%(test|spec)/exemplars/(.*)_exemplar\.rb$','\1')
elseif f =~# '^\%(test/\|spec/\)\=factories/.*_factory\.rb$'
return s:sub(f,'^%(test/|spec/)=factories/(.{-})_factory.rb$','\1')
elseif f =~# '^\%(test/\|spec/\)\=fabricators/.*\.rb$'
return s:sub(f,'^%(test/|spec/)=fabricators/(.{-})_fabricator.rb$','\1')
elseif f =~# '^\%(test\|spec\)/\%(fixtures\|factories\|fabricators\)/.*\.\w\+$'
return rails#singularize(s:sub(f,'^%(test|spec)/\w+/(.*)\.\w+$','\1'))
elseif a:0 && a:1
return rails#singularize(s:sub(self.controller_name(), '_mailer$', ''))
endif
return ""
endfunction
call s:add_methods('readable', ['find_affinity', 'controller_name', 'model_name'])
function! s:file_lines() dict abort
let ftime = s:getftime(self.path())
if ftime > 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(...) abort
return s:completion_filter(rails#app().environments(),a:0 ? a:1 : "")
endfunction
function! s:warn(str) abort
echohl WarningMsg
echomsg a:str
echohl None
" Sometimes required to flush output
echo ""
let v:warningmsg = a:str
return ''
endfunction
function! s:error(str) abort
echohl ErrorMsg
echomsg a:str
echohl None
let v:errmsg = a:str
return ''
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, ...) abort
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 a:0 && a:1 ? s:sub(str, '^/', '') : str
endfunction
function! rails#camelize(str) abort
let str = s:gsub(a:str,'/(.=)','::\u\1')
let str = s:gsub(str,'%([_-]|<)(.)','\u\1')
return str
endfunction
function! rails#singularize(word) abort
" 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$\|redis$' || empty(word)
return word
endif
let word = s:sub(word,'eople$','ersons')
let word = s:sub(word,'%([Mm]ov|[aeio])@<!ies$','ys')
let word = s:sub(word,'xe[ns]$','xs')
let word = s:sub(word,'ves$','fs')
let word = s:sub(word,'ss%(es)=$','sss')
let word = s:sub(word,'s$','')
let word = s:sub(word,'%([nrt]ch|tatus|lias)\zse$','')
let word = s:sub(word,'%(nd|rt)\zsice$','ex')
return word
endfunction
function! rails#pluralize(word, ...) abort
let word = a:word
if empty(word)
return word
endif
if a:0 && a:1 && word !=# rails#singularize(word)
return word
endif
let word = s:sub(word,'[aeio]@<!y$','ie')
let word = s:sub(word,'%(nd|rt)@<=ex$','ice')
let word = s:sub(word,'%([sxz]|[cs]h)$','&e')
let word = s:sub(word,'f@<!f$','ve')
let word .= 's'
let word = s:sub(word,'ersons$','eople')
return word
endfunction
function! rails#app(...) abort
let root = s:sub(a:0 && len(a:1) ? a:1 : get(b:, 'rails_root', ''), '[\/]$', '')
if !empty(root)
if !has_key(s:apps, root)
let s:apps[root] = deepcopy(s:app_prototype)
let s:apps[root]._root = root
endif
return get(s:apps, root, {})
endif
return {}
endfunction
function! rails#buffer(...)
return extend(extend({'#': bufnr(a:0 ? a:1 : '%')},s:buffer_prototype,'keep'),s:readable_prototype,'keep')
endfunction
function! s:buffer_app() dict abort
if len(self.getvar('rails_root'))
return rails#app(self.getvar('rails_root'))
else
throw 'Not in a Rails app'
endif
endfunction
function! s:readable_app() dict abort
return self._app
endfunction
function! rails#revision() abort
return 1000*matchstr(g:autoloaded_rails,'^\d\+')+matchstr(g:autoloaded_rails,'[1-9]\d*$')
endfunction
function! s:app_file(name) dict abort
return extend(extend({'_app': self, '_name': a:name}, s:file_prototype,'keep'),s:readable_prototype,'keep')
endfunction
function! s:readable_relative() dict abort
return self.name()
endfunction
function! s:readable_absolute() dict abort
return self.path()
endfunction
function! s:readable_spec() dict abort
return self.path()
endfunction
function! s:file_path() dict abort
return self.app().path(self._name)
endfunction
function! s:file_name() dict abort
return self._name
endfunction
function! s:buffer_number() dict abort
return self['#']
endfunction
function! s:buffer_path() dict abort
let bufname = bufname(self.number())
return empty(bufname) ? '' : s:gsub(fnamemodify(bufname,':p'),'\\ @!','/')
endfunction
function! s:buffer_name() dict abort
let app = self.app()
let bufname = bufname(self.number())
let f = len(bufname) ? fnamemodify(bufname, ':p') : ''
if f !~# ':[\/][\/]'
let f = resolve(f)
endif
let f = s:gsub(f, '\\ @!', '/')
let f = s:sub(f,'/$','')
let sep = matchstr(f,'^[^\\/:]\+\zs[\\/]')
if len(sep)
let f = getcwd().sep.f
endif
if s:startswith(tolower(f),s:gsub(tolower(app.path()),'\\ @!','/')) || f == ""
return strpart(f,strlen(app.path())+1)
else
if !exists("s:path_warn") && &verbose
let s:path_warn = 1
call s:warn("File ".f." does not appear to be under the Rails root ".self.app().path().". Please report to the rails.vim author!")
endif
return f
endif
endfunction
function! s:readable_calculate_file_type() dict abort
let f = self.name()
let e = matchstr(f, '\.\zs[^.\/]\+$')
let ae = e
if ae ==# 'erb'
let ae = matchstr(f, '\.\zs[^.\/]\+\ze\.erb$')
endif
let r = "-"
let full_path = self.path()
if empty(f)
let r = ""
elseif f =~# '^app/controllers/concerns/.*\.rb$'
let r = "controller-concern"
elseif f =~# '_controller\.rb$' || f =~# '^app/controllers/.*\.rb$'
let r = "controller"
elseif f =~# '^test/test_helper\.rb$'
let r = "test"
elseif f =~# '^spec/\%(spec\|rails\)_helper\.rb$'
let r = "spec"
elseif f =~# '_helper\.rb$'
let r = "helper"
elseif f =~# '^app/mailers/.*\.rb'
let r = "mailer"
elseif f =~# '^\%(test\|spec\)/mailers/previews/.*_preview\.rb'
let r = "mailerpreview"
elseif f =~# '^app/jobs/.*\.rb'
let r = "job"
elseif f =~# '^app/models/concerns/.*\.rb$'
let r = "model-concern"
elseif f =~# '^app/models/'
let top = "\n".join(s:readbuf(full_path,50),"\n")
let class = matchstr(top,"\n".'class\s\+\S\+\s*<\s*\<\zs\S\+\>')
let type = tolower(matchstr(class, '^Application\zs[A-Z]\w*$\|^Acti\w\w\zs[A-Z]\w*\ze::Base'))
if type ==# 'mailer' || f =~# '_mailer\.rb$'
let r = 'mailer'
elseif class ==# 'ActiveRecord::Observer'
let r = 'model-observer'
elseif !empty(type)
let r = 'model-'.type
elseif top =~# '^\%(self\.\%(table_name\|primary_key\)\|has_one\|has_many\|belongs_to\)\>'
let r = 'model-record'
else
let r = 'model'
endif
elseif f =~# '^app/views/.*/_\w\+\%(\.[[:alnum:]_+]\+\)\=\.\w\+$'
let r = "view-partial-" . e
elseif f =~# '^app/views/layouts\>.*\.'
let r = "view-layout-" . e
elseif f =~# '^app/views\>.*\.'
let r = "view-" . e
elseif f =~# '^test/unit/.*_helper\.rb$'
let r = "test-helper"
elseif f =~# '^test/unit/.*\.rb$'
let r = "test-model"
elseif f =~# '^test/functional/.*_controller_test\.rb$'
let r = "test-controller"
elseif f =~# '^test/integration/.*_test\.rb$'
let r = "test-integration"
elseif f =~# '^test/lib/.*_test\.rb$'
let r = "test-lib"
elseif f =~# '^test/\w*s/.*_test\.rb$'
let r = s:sub(f,'.*<test/(\w*)s/.*','test-\1')
elseif f =~# '^test/.*_test\.rb'
let r = "test"
elseif f =~# '^spec/lib/.*_spec\.rb$'
let r = 'spec-lib'
elseif f =~# '^lib/.*\.rb$'
let r = 'lib'
elseif f =~# '^spec/\w*s/.*_spec\.rb$'
let r = s:sub(f,'.*<spec/(\w*)s/.*','spec-\1')
elseif f =~# '^features/.*\.feature$'
let r = 'cucumber-feature'
elseif f =~# '^features/step_definitions/.*_steps\.rb$'
let r = 'cucumber-steps'
elseif f =~# '^features/.*\.rb$'
let r = 'cucumber'
elseif f =~# '^spec/.*\.feature$'
let r = 'spec-feature'
elseif f =~# '^\%(test\|spec\)/fixtures\>'
if e ==# "yml"
let r = "fixtures-yaml"
else
let r = "fixtures" . (empty(e) ? "" : "-" . e)
endif
elseif f =~# '^\%(test\|spec\)/\%(factories\|fabricators\)\>'
let r = "fixtures-replacement"
elseif f =~# '^spec/.*_spec\.rb'
let r = "spec"
elseif f =~# '^spec/support/.*\.rb'
let r = "spec"
elseif f =~# '^db/migrate\>'
let r = "db-migration"
elseif f=~# '^db/schema\.rb$'
let r = "db-schema"
elseif f =~# '\.rake$' || f =~# '^\%(Rake\|Cap\)file$' || f =~# '^config/deploy\.rb$' || f =~# '^config/deploy/.*\.rb$'
let r = "task"
elseif f =~# '^log/.*\.log$'
let r = "log"
elseif ae ==# "css" || ae =~# "^s[ac]ss$" || ae ==# "^less$"
let r = "stylesheet-".ae
elseif ae ==# "js" || ae ==# "es6"
let r = "javascript"
elseif ae ==# "coffee"
let r = "javascript-coffee"
elseif e ==# "html"
let r = e
elseif f =~# '^config/routes\>.*\.rb$'
let r = "config-routes"
elseif f =~# '^config/'
let r = "config"
endif
return r
endfunction
function! s:buffer_type_name(...) dict abort
let type = getbufvar(self.number(),'rails_cached_file_type')
if empty(type)
let type = self.calculate_file_type()
endif
return call('s:match_type',[type ==# '-' ? '' : type] + a:000)
endfunction
function! s:readable_type_name(...) dict abort
let type = self.calculate_file_type()
return call('s:match_type',[type ==# '-' ? '' : type] + a:000)
endfunction
function! s:match_type(type,...)
if a:0
return !empty(filter(copy(a:000),'a:type =~# "^".v:val."\\%(-\\|$\\)"'))
else
return a:type
endif
endfunction
function! s:app_environments() dict
if self.cache.needs('environments')
call self.cache.set('environments',self.relglob('config/environments/','**/*','.rb'))
endif
return copy(self.cache.get('environments'))
endfunction
function! s:app_default_locale() dict abort
if self.cache.needs('default_locale')
let candidates = map(filter(
\ s:readfile(self.path('config/application.rb')) + s:readfile(self.path('config/environment.rb')),
\ 'v:val =~# "^ *config.i18n.default_locale = :[\"'']\\=[A-Za-z-]\\+[\"'']\\= *$"'
\ ), 'matchstr(v:val,"[A-Za-z-]\\+\\ze[\"'']\\= *$")')
call self.cache.set('default_locale', get(candidates, 0, 'en'))
endif
return self.cache.get('default_locale')
endfunction
function! s:app_stylesheet_suffix() dict abort
if self.cache.needs('stylesheet_suffix')
let default = self.has_gem('sass-rails') ? '.scss' : '.css'
let candidates = map(filter(
\ s:readfile(self.path('config/application.rb')),
\ 'v:val =~# "^ *config.sass.preferred_syntax *= *:[A-Za-z-]\\+ *$"'
\ ), '".".matchstr(v:val,"[A-Za-z-]\\+\\ze *$")')
call self.cache.set('stylesheet_suffix', get(candidates, 0, default))
endif
return self.cache.get('stylesheet_suffix')
endfunction
function! s:app_has(feature) dict
let map = {
\'test': 'test/',
\'spec': 'spec/',
\'bundler': 'Gemfile|gems.locked',
\'rails2': 'script/about',
\'rails3': 'config/application.rb',
\'rails5': 'app/assets/config/manifest.js|config/initializers/application_controller_renderer.rb',
\'cucumber': 'features/',
\'webpack': 'app/javascript/packs',
\'turnip': 'spec/acceptance/',
\'sass': 'public/stylesheets/sass/'}
if self.cache.needs('features')
call self.cache.set('features',{})
endif
let features = self.cache.get('features')
if !has_key(features,a:feature)
let path = get(map,a:feature,a:feature.'/')
let features[a:feature] =
\ !empty(filter(split(path, '|'), 'self.has_file(v:val)'))
endif
return features[a:feature]
endfunction
function! s:app_has_rails5() abort dict
let gemdir = get(self.gems(), 'railties')
return self.has('rails5') || gemdir =~# '-\%([5-9]\|\d\d\+\)\.[^\/]*$'
endfunction
call s:add_methods('app',['default_locale','environments','file','has','has_rails5','stylesheet_suffix'])
call s:add_methods('file',['path','name','lines','getline'])
call s:add_methods('buffer',['app','number','path','name','lines','getline','type_name'])
call s:add_methods('readable',['app','relative','absolute','spec','calculate_file_type','type_name','line_count'])
" }}}1
" Ruby Execution {{{1
function! s:app_has_zeus() dict abort
return getftype(self.real('zeus.sock')) ==# 'socket' && executable('zeus')
endfunction
function! s:app_ruby_script_command(cmd) dict abort
if has('win32')
return 'ruby ' . a:cmd
else
return a:cmd
endif
endfunction
function! s:app_static_rails_command(cmd) dict abort
if filereadable(self.real('bin/rails'))
let cmd = 'bin/rails '.a:cmd
elseif filereadable(self.real('script/rails'))
let cmd = 'script/rails '.a:cmd
elseif !self.has('rails3')
let cmd = 'script/'.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_prepare_rails_command(cmd) dict abort
if self.has_zeus() && a:cmd =~# '^\%(console\|dbconsole\|destroy\|generate\|server\|runner\)\>'
return 'zeus '.a:cmd
endif
return self.static_rails_command(a: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') == 2
let title = escape(fnamemodify(self.path(), ':t').' '.title, ' ')
exe 'Start'.(a:0 && a:1 ? '!' : '').' -title='.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
call s:add_methods('app', ['has_zeus', 'ruby_script_command','static_rails_command','prepare_rails_command','execute_rails_command','start_rails_command'])
" }}}1
" Commands {{{1
function! s:BufCommands()
call s:BufNavCommands()
call s:BufScriptWrappers()
command! -buffer -bar -nargs=* -bang Rabbrev :echoerr "Rabbrev has been removed."
command! -buffer -bar -nargs=? -bang -count -complete=customlist,rails#complete_rake Rake :call s:Rake(<bang>0,!<count> && <line1> ? -1 : <count>,<q-args>)
command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rbrowse :call s:Preview(<bang>0,<line1>,<q-args>)
command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Preview :call s:Preview(<bang>0,<line1>,<q-args>)
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_log Clog exe s:Clog(1<bang>, '<mods>', <q-args>)
command! -buffer -bar -nargs=0 Rtags :echoerr "Use :Ctags"
command! -buffer -bar -nargs=0 Ctags :execute rails#app().tags_command()
command! -buffer -bar -nargs=0 -bang Rrefresh :if <bang>0|unlet! g:autoloaded_rails|source `=s:file`|endif|call s:Refresh(<bang>0)
if exists("g:loaded_dbext")
command! -buffer -bar -nargs=? -complete=customlist,s:Complete_environments Rdbext :echoerr 'Install dadbod.vim and let g:dadbod_manage_dbext = 1'
endif
let ext = expand("%:e")
if rails#buffer().name() =~# '^app/views/'
" TODO: complete controller names with trailing slashes here
command! -buffer -bar -bang -nargs=1 -range -complete=customlist,s:controllerList Extract :exe s:ViewExtract(<bang>0,'<mods>',<line1>,<line2>,<f-args>)
elseif rails#buffer().name() =~# '^app/helpers/.*\.rb$'
command! -buffer -bar -bang -nargs=1 -range Extract :<line1>,<line2>call s:RubyExtract(<bang>0, '<mods>', 'app/helpers', [], s:sub(<f-args>, '_helper$|Helper$|$', '_helper'))
elseif rails#buffer().name() =~# '^app/\w\+/.*\.rb$'
command! -buffer -bar -bang -nargs=1 -range Extract :<line1>,<line2>call s:RubyExtract(<bang>0, '<mods>', matchstr(rails#buffer().name(), '^app/\w\+/').'concerns', [' extend ActiveSupport::Concern', ''], <f-args>)
endif
if rails#buffer().name() =~# '^db/migrate/.*\.rb$'
command! -buffer -bar Rinvert :call s:Invert(<bang>0)
endif
endfunction
function! s:Complete_log(A, L, P) abort
return s:completion_filter(rails#app().relglob('log/','**/*', '.log'), a:A)
endfunction
function! s:Clog(bang, mods, arg) abort
let lf = rails#app().real('log/' . (empty(a:arg) ? s:environment() : a:arg) . '.log')
if !filereadable(lf)
return 'cgetfile ' . s:fnameescape(lf)
endif
let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
let chdir = exists("*haslocaldir") && haslocaldir() ? 'lchdir' : 'chdir'
let cwd = getcwd()
try
compiler rails
exe chdir s:fnameescape(rails#app().real())
exe 'cgetfile' s:fnameescape(lf)
finally
let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
if empty(cc) | unlet! b:current_compiler | endif
exe chdir s:fnameescape(cwd)
endtry
return s:mods(a:mods) . ' copen|$'
endfunction
function! s:Plog(bang, arg) abort
let lf = rails#app().path('log/' . (empty(a:arg) ? s:environment() : a:arg) . '.log')
return 'pedit' . (a:bang ? '!' : '') . ' +$ ' . s:fnameescape(lf)
endfunction
function! rails#command(bang, mods, count, arg) abort
if s:active()
return s:Rails(a:bang, a:count, a:arg)
elseif a:arg !~# '^new\>'
return 'echoerr '.string('Usage: rails new <path>')
endif
let arg = a:arg
if &shellpipe !~# 'tee' && arg !~# ' --\%(skip\|force\)\>'
let arg .= ' --skip'
endif
let temp = tempname()
try
if &shellpipe =~# '%s'
let pipe = s:sub(&shellpipe, '\%s', temp)
else
let pipe = &shellpipe . ' ' . temp
endif
exe '!rails' arg pipe
let error = v:shell_error
catch /^Vim:Interrupt/
endtry
let dir = matchstr(arg, ' ["'']\=\zs[^- "''][^ "'']\+')
if isdirectory(dir)
let old_errorformat = &l:errorformat
let chdir = exists("*haslocaldir") && haslocaldir() ? 'lchdir' : 'chdir'
let cwd = getcwd()
try
exe chdir s:fnameescape(dir)
let &l:errorformat = s:efm_generate
exe 'cgetfile' temp
return 'copen|cfirst'
finally
let &l:errorformat = old_errorformat
exe chdir s:fnameescape(cwd)
endtry
elseif exists('error') && !error && !empty(dir)
call s:warn("Couldn't find app directory")
endif
return ''
endfunction
function! s:app_tags_command() dict abort
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 cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
try
execute cd fnameescape(self.real())
if filereadable('.ctags')
let args = []
else
let args = s:split(get(g:, 'rails_ctags_arguments', '--languages=Ruby'))
endif
exe '!'.cmd.' -R '.join(args,' ')
finally
execute cd fnameescape(cwd)
endtry
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()
if exists('#User#BufLeaveRails')
try
let [modelines, &modelines] = [&modelines, 0]
doautocmd User BufLeaveRails
finally
let &modelines = modelines
endtry
endif
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 !empty(rr)
call setbufvar(i,"rails_refresh",1)
endif
let i += 1
endwhile
if exists('#User#BufEnterRails')
try
let [modelines, &modelines] = [&modelines, 0]
doautocmd User BufEnterRails
finally
let &modelines = modelines
endtry
endif
endfunction
" }}}1
" Rake {{{1
function! s:efm_dir() abort
return substitute(matchstr(','.&l:errorformat, ',%\\&chdir \zs\%(\\.\|[^,]\)*'), '\\,' ,',', 'g')
endfunction
function! s:qf_pre() abort
let dir = s:efm_dir()
let cwd = getcwd()
if !empty(dir) && dir !=# cwd
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
execute 'lcd' fnameescape(dir)
let s:qf_post = cd . ' ' . fnameescape(cwd)
endif
endfunction
augroup railsPluginMake
autocmd!
autocmd QuickFixCmdPre *make* call s:qf_pre()
autocmd QuickFixCmdPost *make*
\ if exists('s:qf_post') | execute remove(s:, 'qf_post') | endif
augroup END
function! s:app_rake_tasks() dict abort
if self.cache.needs('rake_tasks')
call s:push_chdir()
try
let output = system(self.rake_command('norails').' -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,"^\\S\\+\\s\\+\\zs[^][ ]\\+")')
call filter(lines,'v:val != ""')
call self.cache.set('rake_tasks',s:uniq(['default'] + lines))
endif
return self.cache.get('rake_tasks')
endfunction
call s:add_methods('app', ['rake_tasks'])
function! s:make(bang, args, ...)
if exists(':Make') == 2
exe 'Make'.(a:bang ? '! ' : ' ').a:args
else
exe 'make! '.a:args
let qf = &l:buftype ==# 'quickfix'
if !a:bang
exe (a:0 ? a:1 : 'cwindow')
if !qf && &l:buftype ==# 'quickfix'
wincmd p
endif
endif
endif
endfunction
function! s:Rake(bang, lnum, arg) abort
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
compiler rails
let b:current_compiler = 'rake'
let &l:makeprg = rails#app().rake_command('norails')
let &l:errorformat .= ',chdir '.escape(self.path(), ',')
let arg = a:arg
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
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\|middleware\|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
endtry
endfunction
function! s:readable_test_file_candidates() dict abort
let f = self.name()
let projected = self.projected('railsTest') + self.projected('test')
if self.type_name('view')
let tests = [
\ fnamemodify(f,':s?\<app/?spec/?')."_spec.rb",
\ fnamemodify(f,':r:s?\<app/?spec/?')."_spec.rb",
\ fnamemodify(f,':r:r:s?\<app/?spec/?')."_spec.rb",
\ s:sub(s:sub(f,'<app/views/','test/controllers/'),'/[^/]*$','_controller_test.rb'),
\ s:sub(s:sub(f,'<app/views/','test/functional/'),'/[^/]*$','_controller_test.rb')]
elseif self.type_name('lib')
let tests = [
\ s:sub(f,'<lib/(.*)\.rb$','test/lib/\1_test.rb'),
\ s:sub(f,'<lib/(.*)\.rb$','test/unit/\1_test.rb'),
\ s:sub(f,'<lib/(.*)\.rb$','spec/lib/\1_spec.rb')]
elseif self.type_name('fixtures') && f =~# '\<spec/'
let tests = [
\ 'spec/models/' . self.model_name() . '_spec.rb']
elseif self.type_name('fixtures')
let tests = [
\ 'test/models/' . self.model_name() . '_test.rb',
\ 'test/unit/' . self.model_name() . '_test.rb']
elseif f =~# '\<app/.*/.*\.rb'
let file = fnamemodify(f,":r")
let test_file = s:sub(file,'<app/','test/') . '_test.rb'
let spec_file = s:sub(file,'<app/','spec/') . '_spec.rb'
let old_test_file = s:sub(s:sub(s:sub(s:sub(test_file,
\ '<test/helpers/', 'test/unit/helpers/'),
\ '<test/models/', 'test/unit/'),
\ '<test/mailers/', 'test/functional/'),
\ '<test/controllers/', 'test/functional/')
let tests = s:uniq([test_file, old_test_file, spec_file])
elseif f =~# '\<\(test\|spec\)/\%(\1_helper\.rb$\|support\>\)' || f =~# '\%(\<spec/\|\<test/\)\@<!\<features/.*\.rb$'
let tests = [matchstr(f, '.*\<\%(test\|spec\|features\)\>')]
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_placeholders(lnum) dict abort
let placeholders = {}
if a:lnum
let placeholders.lnum = a:lnum
let placeholders.line = a:lnum
let last = self.last_method(a:lnum)
if !empty(last)
let placeholders.define = last
endif
endif
return placeholders
endfunction
function! s:readable_default_rake_task(...) dict abort
let app = self.app()
let lnum = a:0 ? (a:1 < 0 ? 0 : a:1) : 0
let taskpat = '\C# ra\%(ils\|ke\)\s\+\zs.\{-\}\ze\%(\s\s\|#\|$\)'
if self.getvar('&buftype') == 'quickfix'
return '-'
elseif self.getline(lnum) =~# '# ra\%(ils\|ke\) \S'
return matchstr(self.getline(lnum),'\C# ra\%(ils\|ke\) \zs.*')
elseif self.getline(self.last_method_line(lnum)-1) =~# taskpat
return matchstr(self.getline(self.last_method_line(lnum)-1), taskpat)
elseif self.getline(self.last_method_line(lnum)) =~# taskpat
return matchstr(self.getline(self.last_method_line(lnum)), taskpat)
elseif self.getline(1) =~# taskpat && !lnum
return matchstr(self.getline(1), taskpat)
endif
let placeholders = self.placeholders(lnum)
let tasks = self.projected('rakeTask', placeholders) + self.projected('task', placeholders)
if len(tasks)
return tasks[0]
endif
let tasks = self.projected('railsTask', placeholders)
if len(tasks)
let task = substitute(tasks[0], '^$', '--tasks', '')
if task =~# '^test\>'
let task = substitute(substitute(task, ' \zs[^-[:upper:][:space:]]', 'TEST=', ''), ' -n', ' TESTOPTS=-n', '')
endif
return task
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\)\>'
let task = self.last_method(lnum)
else
let task = matchstr(self.getline(1),'\C# rake \zs.*')
endif
return s:sub(task, '^$', '--tasks')
elseif self.type_name('db-migration')
let ver = matchstr(self.name(),'\<db/migrate/0*\zs\d*\ze_')
if !empty(ver)
if lnum
return "db:migrate:down VERSION=".ver
else
return "db:migrate:redo VERSION=".ver
endif
else
return 'db:migrate'
endif
elseif self.name() =~# '\<db/seeds\.rb$'
return 'db:seed'
elseif self.name() =~# '\<db/\|\<config/database\.'
return 'db:migrate:status'
elseif self.name() =~# '\<config\.ru$'
return 'middleware'
elseif self.name() =~# '\<README'
return 'about'
else
let test = self.test_file()
let with_line = test
if test ==# self.name()
let with_line .= (lnum > 0 ? ':'.lnum : '')
endif
if empty(test)
return '--tasks'
elseif test =~# '^test\>'
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 self.app().has_rails5()
return 'test TEST='.s:rquote(test).opts
elseif test =~# '^test/\%(unit\|models\|jobs\)\>'
return 'test:units TEST='.s:rquote(test).opts
elseif test =~# '^test/\%(functional\|controllers\)\>'
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:units 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! s:rake2rails(task) abort
let task = s:gsub(a:task, '^--tasks$', '')
let task = s:gsub(task, '<TEST\w*\=', '')
return task
endfunction
function! s:readable_default_task(...) dict abort
let tasks = self.projected('railsTask', self.placeholders(a:0 ? a:1 : 0))
if len(tasks)
return tasks[0]
endif
return s:rake2rails(call(self.default_rake_task, a:000, self))
endfunction
function! s:app_rake_command(...) dict abort
let cmd = 'rake'
if self.has_rails5() && get(a:, 1, '') !=# 'norails' && get(g:, 'rails_make', '') !=# 'rake'
let cmd = 'rails'
endif
if get(a:, 1, '') !=# 'static' && self.has_zeus()
return 'zeus ' . cmd
elseif filereadable(self.real('bin/' . cmd))
return self.ruby_script_command('bin/' . cmd)
elseif self.has('bundler')
return 'bundle exec ' . cmd
else
return cmd
endif
endfunction
function! rails#complete_rake(A,L,P) abort
return s:completion_filter(rails#app().rake_tasks(), a:A, ':')
endfunction
call s:add_methods('readable', ['test_file_candidates', 'test_file', 'placeholders', 'default_rake_task', 'default_task'])
call s:add_methods('app', ['rake_command'])
" }}}1
" Preview {{{1
function! s:initOpenURL() abort
if exists(":OpenURL") != 2
if exists(":Browse") == 2
command -bar -nargs=1 OpenURL Browse <args>
elseif has("gui_mac") || has("gui_macvim") || exists("$SECURITYSESSIONID")
command -bar -nargs=1 OpenURL exe '!open' shellescape(<q-args>, 1)
elseif has("gui_win32")
command -bar -nargs=1 OpenURL exe '!start cmd /cstart /b' shellescape(<q-args>, 1)
elseif executable("xdg-open")
command -bar -nargs=1 OpenURL exe '!xdg-open' shellescape(<q-args>, 1) '&'
elseif executable("sensible-browser")
command -bar -nargs=1 OpenURL exe '!sensible-browser' shellescape(<q-args>, 1)
elseif executable('launchy')
command -bar -nargs=1 OpenURL exe '!launchy' shellescape(<q-args>, 1)
elseif executable('git')
command -bar -nargs=1 OpenURL exe '!git web--browse' shellescape(<q-args>, 1)
endif
endif
endfunction
function! s:scanlineforuris(line) abort
let url = matchstr(a:line,"\\v\\C%(%(GET|PUT|POST|DELETE)\\s+|\\w+://[^/]*)/[^ \n\r\t<>\"]*[^] .,;\n\r\t<>\":]")
if url =~# '^\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 len(url)
return [url]
else
return []
endif
endfunction
function! s:readable_params(...) dict abort
let lnum = a:0 ? a:1 : 0
let params = {}
let controller = self.controller_name(1)
if len(controller)
let params.controller = controller
endif
if self.type_name('controller') && len(self.last_method(lnum))
let params.action = self.last_method(lnum)
elseif self.type_name('controller','view-layout','view-partial')
let params.action = 'index'
elseif self.type_name('view')
let params.action = fnamemodify(self.name(),':t:r:r:r')
let format = fnamemodify(self.name(), ':r:e')
if len(format) && format !=# 'html'
let params.format = format
endif
endif
for item in reverse(self.projected('railsParams') + self.projected('params'))
if type(item) == type({})
call extend(params, item)
endif
endfor
return params
endfunction
function! s:expand_url(url, params) abort
let params = extend({'controller': "\030", 'action': "\030", 'format': "\030"}, a:params, 'keep')
let url = substitute(a:url, '\%(/\(\w\+\)/\)\=\zs[:*]\(\h\w*\)',
\ '\=strftime(get(s:split(get(params,rails#singularize(submatch(1))."_".submatch(2),get(params,submatch(2),1))), 0, "\030"))', 'g')
let url = s:gsub(url, '\([^()]*'."\030".'[^()]*\)', '')
let url = s:gsub(url, '[()]', '')
if url !~# "\030"
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') && len(self.getvar('rails_preview'))
let urls += [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\|lib\|vendor\)/assets/stylesheets/'
call add(urls, '/assets/' . matchstr(self.name(), 'stylesheets/\zs[^.]*') . '.css')
elseif self.name() =~# '^\%(app\|lib\|vendor\)/assets/javascripts/'
call add(urls, '/assets/' . matchstr(self.name(), 'javascripts/\zs[^.]*') . '.js')
elseif self.name() =~# '^app/javascript/packs/'
let file = matchstr(self.name(), 'packs/\zs.\{-\}\%(\.erb\)\=$')
if file =~# escape(join(rails#pack_suffixes('css'), '\|'), '.') . '$'
let file = fnamemodify(file, ':r') . '.css'
elseif file =~# escape(join(rails#pack_suffixes('js'), '\|'), '.') . '$'
let file = fnamemodify(file, ':r') . '.js'
endif
if filereadable(self.app().real('public/packs/manifest.json'))
let manifest = rails#json_parse(readfile(self.app().real('public/packs/manifest.json')))
else
let manifest = {}
endif
if has_key(manifest, file)
call add(urls, manifest[file])
else
call add(urls, '/packs/' . file)
endif
elseif self.app().has_file('app/mailers/' . self.controller_name() . '.rb')
if self.type_name('mailer', 'mailerpreview') && len(self.last_method(a:lnum))
call add(urls, '/rails/mailers/' . self.controller_name() . '/' . self.last_method(a:lnum))
elseif self.type_name('view')
call add(urls, '/rails/mailers/' . self.controller_name() . '/' . fnamemodify(self.name(),':t:r:r:r'))
endif
else
let params = self.params()
let handler = get(params, 'controller', '') . '#' . get(params, 'action', '')
for route in self.app().routes()
if get(route, 'method') =~# 'GET' && get(route, 'handler') =~# '^:\=[[:alnum:]_/]*#:\=\w*$' && handler =~# '^'.s:gsub(route.handler, ':\w+', '\\w\\+').'$'
call add(urls, s:expand_url(route.path, params))
endif
endfor
endif
return urls
endfunction
call s:add_methods('readable', ['params', 'preview_urls'])
function! s:app_server_pid() dict abort
for type in ['server', 'unicorn']
let pidfile = self.real('tmp/pids/'.type.'.pid')
if filereadable(pidfile)
let pid = get(readfile(pidfile, 'b', 1), 0, 0)
if pid
return pid
endif
endif
endfor
endfunction
function! s:app_server_binding() dict abort
let pid = self.server_pid()
if pid
if self.cache.needs('server')
let old = {'pid': 0, 'binding': ''}
else
let old = self.cache.get('server')
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_pid', '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 binding = s:sub(binding, '^\[::\]', '[::1]')
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') == 2) && !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
let root = rails#app().path()
wincmd w
let b:rails_root = root
if &filetype ==# ''
if uri =~ '\.css$'
setlocal filetype=css
elseif uri =~ '\.js$'
setlocal filetype=javascript
elseif getline(1) =~ '^\s*<'
setlocal filetype=xhtml
endif
endif
call rails#buffer_setup()
map <buffer> <silent> q :bwipe<CR>
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) abort
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_environments Console :Rails<bang> console <args>
command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_generate Generate :execute rails#app().generator_command(<bang>0,'<mods>','generate',<f-args>)
command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Destroy :execute rails#app().generator_command(1,'<mods>','destroy',<f-args>)
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Server :execute rails#app().server_command(0, <bang>0, <q-args>)
command! -buffer -bang -nargs=? -range=0 -complete=customlist,s:Complete_edit Runner :execute rails#buffer().runner_command(<bang>0, <count>?<line1>:0, <q-args>)
command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rp :execute rails#app().output_command(<count>==<line2>?<count>:-1, 'p begin '.<q-args>.' end')
command! -buffer -nargs=1 -range=0 -complete=customlist,s:Complete_ruby Rpp :execute rails#app().output_command(<count>==<line2>?<count>:-1, 'require %{pp}; pp begin '.<q-args>.' 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(copy(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', 'job']
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:Rails(bang, count, arg) abort
let use_rake = 0
if !empty(a:arg)
let str = a:arg
let native = '\v^%(application|benchmarker|console|dbconsole|destroy|generate|new|plugin|profiler|runner|server|version|[cgst]|db)>'
if !rails#app().has('rails3')
let use_rake = !rails#app().has_file('script/' . matchstr(str, '\S\+'))
elseif str !~# '^-' && str !~# native
let use_rake = 1
endif
else
let str = rails#buffer().default_rake_task(a:count)
if str ==# '--tasks'
let str = ''
else
let use_rake = 1
endif
endif
if str =~# '^\%(c\|console\|db\|dbconsole\|s\|server\)\S\@!' && str !~# ' -d\| --daemon\| --help'
return rails#app().start_rails_command(str, a:bang)
else
let [mp, efm, cc] = [&l:mp, &l:efm, get(b:, 'current_compiler', '')]
try
compiler rails
if use_rake && !rails#app().has_rails5()
let &l:makeprg = rails#app().rake_command()
else
let str = s:rake2rails(str)
let &l:makeprg = rails#app().prepare_rails_command('$*')
endif
let &l:errorformat .= ',chdir '.escape(rails#app().path(), ',')
call s:make(a:bang, str)
finally
let [&l:mp, &l:efm, b:current_compiler] = [mp, efm, cc]
if empty(cc) | unlet! b:current_compiler | endif
endtry
return ''
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', '')
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 = self.name()
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(a:count)
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('railsRunner') + 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_zeus()
let &l:makeprg = 'zeus ' . &l:makeprg
elseif compiler ==# 'rubyunit'
let &l:makeprg = 'ruby -Itest'
elseif filereadable(self.app().real('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
let &l:errorformat .= ',chdir '.escape(self.app().path(), ',')
call s:make(a:bang, arg . extra)
return ''
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
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) abort
if empty(a:pid)
return ''
endif
if has('win32')
let output = system('netstat -anop tcp')
let binding = matchstr(output, '\n\s*TCP\s\+\zs\S\+\ze\s\+\S\+\s\+LISTENING\s\+'.a:pid.'\>')
return s:sub(binding, '^([^[]*:.*):', '[\1]:')
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.' -anP -i4tcp -sTCP:LISTEN -p'.a:pid)
let binding = matchstr(output, '\S\+:\d\+\ze\s\+(LISTEN)\n')
let binding = s:sub(binding, '^\*', '0.0.0.0')
if empty(binding)
let output = system(lsof.' -anP -i6tcp -sTCP:LISTEN -p'.a:pid)
let binding = matchstr(output, '\S\+:\d\+\ze\s\+(LISTEN)\n')
let binding = s:sub(binding, '^\*', '[::]')
endif
return binding
endif
if executable('netstat')
let output = system('netstat -antp')
let binding = matchstr(output, '\S\+:\d\+\ze\s\+\S\+\s\+LISTEN\s\+'.a:pid.'/')
return s:sub(binding, '^([^[]*:.*):', '[\1]:')
endif
return ''
endfunction
function! s:app_server_command(kill, bg, arg) dict abort
let arg = empty(a:arg) ? '' : ' '.a:arg
let flags = ' -d\| --daemon\| --help'
if a:kill || a:arg =~# '^ *[!-]$' || (a:bg && arg =~# flags)
let pid = self.server_pid()
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
else
echo "No server running"
endif
if a:arg =~# '^ *[-!]$'
return
endif
endif
if exists(':Start') == 0 && !has('win32') && arg !~# flags
let arg .= ' -d'
endif
if a:arg =~# flags
call self.execute_rails_command('server '.a:arg)
else
call self.start_rails_command('server '.a:arg, a:bg)
endif
return ''
endfunction
function! s:color_efm(pre, before, after)
return a:pre . '%\e%\S%\+ %#' . a:before . '%\e[0m %#' . a:after . ',' .
\ a:pre . '%\s %#'.a:before.' %#'.a:after . ','
endfunction
let s:efm_generate =
\ s:color_efm('%-G', 'invoke', '%.%#') .
\ s:color_efm('%-G', 'conflict', '%.%#') .
\ s:color_efm('%-G', 'run', '%.%#') .
\ s:color_efm('%-G', 'route', '%.%#') .
\ s:color_efm('%-G', '%\w%\+', ' ') .
\ '%-G %#Overwrite%.%#"h"%.%#,' .
\ ' %#Overwrite%.%#%\S%\+ %#%m%\e[0m %#%f,' .
\ s:color_efm('', '%m%\>', '%f') .
\ '%-G%.%#'
function! s:app_generator_command(bang, mods, ...) dict abort
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 . ',chdir '.escape(self.path(), ',')
call s:push_chdir(1)
noautocmd make!
finally
call s:pop_command()
let &l:errorformat = old_errorformat
let &l:makeprg = old_makeprg
endtry
if a:bang || empty(getqflist())
return ''
else
return s:mods(a:mods) . ' cfirst'
endif
endfunction
call s:add_methods('app', ['generators','output_command','server_command','generator_command'])
function! rails#complete_rails(ArgLead, CmdLine, P, ...) abort
if a:0
let app = a:1
else
let root = s:efm_dir()
if empty(root)
let manifest = findfile('config/environment.rb', escape(getcwd(), ' ,;').';')
let root = empty(manifest) ? '' : fnamemodify(manifest, ':p:h:h')
endif
let app = empty(root) ? {} : rails#app(root)
endif
let cmd = s:sub(a:CmdLine,'^\u\w*\s+','')
if cmd =~# '^new\s\+'
return split(glob(a:ArgLead.'*/'), "\n")
elseif empty(app)
return s:completion_filter(['new'], a:ArgLead)
elseif cmd =~# '^$\|^\w\S*$'
let cmds = ['generate', 'console', 'server', 'dbconsole', 'destroy', 'plugin', 'runner']
call extend(cmds, app.rake_tasks())
call sort(cmds)
return s:completion_filter(cmds, a:ArgLead)
elseif cmd =~# '^\%([rt]\|runner\|test\|test:db\)\s\+'
return s:completion_filter(app.relglob('', s:fuzzyglob(a:ArgLead)), a:ArgLead)
elseif cmd =~# '^\%([gd]\|generate\|destroy\)\s\+'.a:ArgLead.'$'
return s:completion_filter(app.generators(),a:ArgLead)
elseif cmd =~# '^\%([gd]\|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(app.relglob('lib/generators/','*'),'s:sub(v:val,"/$","")'), a:ArgLead)
elseif target ==# 'helper'
return s:autocamelize(app.relglob('app/helpers/','**/*','_helper.rb'),a:ArgLead)
elseif target ==# 'integration_test' || target ==# 'integration_spec' || target ==# 'feature'
return s:autocamelize(
\ app.relglob('test/integration/','**/*','_test.rb') +
\ app.relglob('spec/features/', '**/*', '_spec.rb') +
\ app.relglob('spec/requests/', '**/*', '_spec.rb') +
\ app.relglob('features/', '**/*', '.feature'), a:ArgLead)
elseif target ==# 'migration' || target ==# 'session_migration'
return s:migrationList(a:ArgLead,"","")
elseif target ==# 'mailer'
return s:completion_filter(app.relglob("app/mailers/","**/*",".rb"),a:ArgLead)
elseif target =~# '^\w*\%(model\|resource\)$' || target =~# '\w*scaffold\%(_controller\)\=$'
return s:completion_filter(app.relglob('app/models/','**/*','.rb'), a:ArgLead)
else
return []
endif
elseif cmd =~# '^\%([gd]\|generate\|destroy\)\s\+scaffold\s\+\w\+\s\+'.a:ArgLead.'$'
return filter(s:controllerList(a:ArgLead,"",""),'v:val !=# "application"')
return s:completion_filter(app.environments())
elseif cmd =~# '^\%(c\|console\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$"
return s:completion_filter(app.environments()+["-s","--sandbox"],a:ArgLead)
elseif cmd =~# '^\%(db\|dbconsole\)\s\+\(--\=\w\+\s\+\)\='.a:ArgLead."$"
return s:completion_filter(app.environments()+["-p","--include-password"],a:ArgLead)
elseif cmd =~# '^\%(s\|server\)\s\+.*-e\s\+'.a:ArgLead."$"
return s:completion_filter(app.environments(),a:ArgLead)
elseif cmd =~# '^\%(s\|server\)\s\+'
if a:ArgLead =~# '^--environment='
return s:completion_filter(map(copy(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) abort
let L = "Rscript ".a:cmd." ".s:sub(a:L,'^\h\w*\s+','')
let P = a:P - strlen(a:L) + strlen(L)
return rails#complete_rails(a:A, L, P, rails#app())
endfunction
function! s:Complete_server(A,L,P) abort
return s:CustomComplete(a:A,a:L,a:P,"server")
endfunction
function! s:Complete_console(A,L,P) abort
return s:CustomComplete(a:A,a:L,a:P,"console")
endfunction
function! s:Complete_generate(A,L,P) abort
return s:CustomComplete(a:A,a:L,a:P,"generate")
endfunction
function! s:Complete_destroy(A,L,P) abort
return s:CustomComplete(a:A,a:L,a:P,"destroy")
endfunction
function! s:Complete_ruby(A,L,P) abort
return s:completion_filter(rails#app().user_classes()+["ActiveRecord::Base"],a:A)
endfunction
" }}}1
" Navigation {{{1
function! s:BufNavCommands()
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate A exe s:Alternate('<mods> E<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AE exe s:Alternate('<mods> E<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AS exe s:Alternate('<mods> S<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AV exe s:Alternate('<mods> V<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AT exe s:Alternate('<mods> T<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit AD exe s:Alternate('<mods> D<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit AR exe s:Alternate('<mods> D<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related R exe s:Related('<mods> E<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RE exe s:Related('<mods> E<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RS exe s:Related('<mods> S<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RV exe s:Related('<mods> V<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RT exe s:Related('<mods> T<bang>',<line1>,<line2>,<count>,<f-args>)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit RD exe s:Related('<mods> D<bang>',<line1>,<line2>,<count>,<f-args>)
endfunction
function! s:jumpargs(file, jump) abort
let file = fnameescape(a:file)
if empty(a:jump)
return file
elseif a:jump ==# '!'
return '+AD ' . file
elseif a:jump =~# '^\d\+$'
return '+' . a:jump . ' ' . file
else
return '+A#' . a:jump . ' ' . file
endif
endfunction
function! s:jump(def, ...) abort
let def = s:sub(a:def,'^[#:]','')
let edit = s:editcmdfor(a:0 ? a:1 : '')
if edit !~# 'edit'
exe edit
endif
if def =~ '^\d\+$'
exe def
elseif def !~# '^$\|^!'
let ext = matchstr(def,'\.\zs.*')
let def = matchstr(def,'[^.]*')
let include = &l:include
try
setlocal include=
exe 'djump' def
catch /^Vim(djump):E387/
catch
let error = 1
finally
let &l:include = include
endtry
if !empty(ext) && expand('%:e') ==# 'rb' && !exists('error')
let rpat = '\C^\s*\%(mail\>.*\|respond_to\)\s*\%(\<do\|{\)\s*|\zs\h\k*\ze|'
let end = s:endof(line('.'))
let rline = search(rpat,'',end)
if rline > 0
let variable = matchstr(getline(rline),rpat)
let success = search('\C^\s*'.variable.'\s*\.\s*\zs'.ext.'\>','',end)
if !success
try
setlocal include=
exe 'djump' def
catch
finally
let &l:include = include
endtry
endif
endif
endif
endif
return ''
endfunction
function! s:fuzzyglob(arg) abort
return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*')
endfunction
function! s:Complete_edit(ArgLead, CmdLine, CursorPos) abort
return s:completion_filter(rails#app().relglob("",s:fuzzyglob(a:ArgLead)),a:ArgLead)
endfunction
function! s:Complete_cd(ArgLead, CmdLine, CursorPos) abort
let all = rails#app().relglob("",a:ArgLead."*")
call filter(all,'v:val =~ "/$"')
return filter(all,'s:startswith(v:val,a:ArgLead)')
endfunction
function! s:match_cursor(pat) abort
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:match_it(pat, repl) abort
let res = s:match_cursor(a:pat)
if res != ""
return substitute(res,'\C'.a:pat,a:repl,'')
else
return ""
endif
endfunction
function! s:match_method(func, ...) abort
let l = ''
let r = ''
if &filetype =~# '\<eruby\>'
let l = '\s*\%(<%\)\=[=-]\='
let r = '\s*\%(-\=%>\s*\)\='
elseif &filetype =~# '\<haml\>'
let l = '\s*[=-]'
endif
let result = s:match_it(l.'\s*\<\%('.a:func.'\)\s*(\=\s*\(:\=[''"@]\=\f\+\)\>[''"]\='.r, '\1')
return a:0 ? result : substitute(result, '^:\=[''"@]\=', '', '')
endfunction
function! s:match_symbol(sym) abort
return s:match_it('\s*\%(:\%('.a:sym.'\)\s*=>\|\<'.a:sym.':\)\s*(\=\s*[@:'."'".'"]\(\f\+\)\>.\=', '\1')
endfunction
function! s:match_partial(func) abort
let res = s:match_method(a:func, '\1', 1)
if empty(res)
return ''
elseif res =~# '^\w\+\%(\.\w\+\)\=$'
let res = rails#singularize(s:sub(res, '^\w*\.', ''))
return s:findview(rails#pluralize(res).'/_'.res)
else
return s:findview(s:sub(s:sub(res, '^:=[''"@]=', ''), '[^/]*$', '_&'))
endif
endfunction
function! s:suffixes(type) abort
if a:type =~# '^stylesheets\=$\|^css$'
let exts = ['css', 'scss', 'css.scss', 'sass', 'css.sass']
call extend(exts, map(copy(exts), 'v:val.".erb"'))
elseif a:type =~# '^javascripts\=$\|^js$'
let exts = ['js', 'coffee', 'js.coffee', 'es6']
call extend(exts, map(copy(exts), 'v:val.".erb"'))
call extend(exts, ['ejs', 'eco', 'jst', 'jst.ejs', 'jst.eco'])
else
return []
endif
let suffixes = map(copy(exts), '".".v:val')
call extend(suffixes, map(copy(suffixes), '"/index".v:val'))
return s:uniq(suffixes)
endfunction
function! s:findasset(path, dir) abort
let path = a:path
if path =~# '^\.\.\=/'
let path = expand('%:p:h:h') . '/' . path[3:-1]
endif
let suffixes = s:suffixes(a:dir)
let asset = s:resolve_asset(path, suffixes)
if len(asset)
return asset
endif
if path ==# a:path
if empty(a:dir)
return ''
endif
if a:dir ==# 'stylesheets' && rails#app().has('sass')
let sass = rails#app().path('public/stylesheets/sass/' . path)
if s:filereadable(sass)
return sass
elseif s:filereadable(sass.'.sass')
return sass.'.sass'
elseif s:filereadable(sass.'.scss')
return sass.'.scss'
endif
endif
let public = rails#app().path('public/' . a:dir . '/' . path)
let post = get(suffixes, 0, '')
if s:filereadable(public)
return public
elseif s:filereadable(public . post)
return public . post
elseif rails#app().has_path('app/assets/' . a:dir) || !rails#app().has_path('public/' . a:dir)
let path = rails#app().path('app/assets/' . a:dir . '/' . path)
else
let path = public
endif
endif
if !empty(getftype(path)) || path =~# '\.\w\+$'
return path
endif
return path . post
endfunction
function! s:cfile_delegate(expr) abort
let expr = empty(a:expr) ? matchstr(&includeexpr, '.*\<v:fname\>.*') : a:expr
if empty(expr)
let expr = 'v:fname'
endif
let expr = substitute(expr, '\<v:fname\>', 'expand("<cfile>")', 'g')
return expr
endfunction
function! s:sprockets_cfile() abort
let dir = ''
if &sua =~# '\.js\>'
let dir = 'javascripts'
elseif &sua =~# '\.css\>'
let dir = 'stylesheets'
let asset = ''
let sssuf = s:suffixes('stylesheets')
let res = s:match_it('\%(^\s*[[:alnum:]-]\+:\s\+\)\=\<[[:alnum:]-]\+-\%(path\|url\)(["'']\=\([^"''() ]*\)', '\1')
if !empty(res)
let asset = s:resolve_asset(res)
endif
let res = s:match_it('\%(^\s*[[:alnum:]-]\+:\s\+\)\=\<stylesheet-\%(path\|url\)(["'']\=\([^"''() ]*\)', '\1')
if !empty(res)
let asset = s:resolve_asset(res, sssuf)
endif
let res = s:match_it('\%(^\s*[[:alnum:]-]\+:\s\+\)\=\<javascript-\%(path\|url\)(["'']\=\([^"''() ]*\)', '\1')
if !empty(res)
let asset = s:resolve_asset(res, s:suffixes('javascripts'))
endif
if !empty(asset)
return asset
endif
let res = s:match_it('^\s*@import\s*\%(url(\)\=["'']\=\([^"''() ]*\)', '\1')
if !empty(res)
let base = expand('%:p:h')
let rel = s:sub(res, '\ze[^/]*$', '_')
let sssuf = s:suffixes('stylesheets')
for ext in [''] + sssuf
for name in [res.ext, rel.ext]
if s:filereadable(base.'/'.name)
return base.'/'.name
endif
endfor
endfor
let asset = s:resolve_asset(res, sssuf)
if empty(asset) && expand('%:e') =~# '^s[ac]ss$'
let asset = s:resolve_asset(rel, sssuf)
endif
return empty(asset) ? 'app/assets/stylesheets/'.res : asset
endif
endif
let res = s:match_it('^\s*\%(//\|[*#]\)=\s*\%(link\|require\|depend_on\|stub\)\w*\s*["'']\=\([^"'' ]*\)', '\1')
if !empty(res) && exists('l:dir')
let asset = s:resolve_asset(res, dir)
return empty(asset) ? res : asset
endif
return ''
endfunction
function! rails#sprockets_cfile(...) abort
let file = s:dot_relative(s:sprockets_cfile())
if empty(file)
return eval(s:cfile_delegate(a:0 ? a:1 : ''))
endif
let escaped = s:fnameescape(file)
if file ==# escaped
return file
else
return '+ '.escaped
endif
endfunction
function! s:ruby_cfile() abort
let buffer = rails#buffer()
let res = s:match_it('\v\s*<require\s*\(=\s*File.expand_path\([''"]../(\f+)[''"],\s*__FILE__\s*\)',expand('%:p:h').'/\1')
if len(res)|return s:simplify(res.(res !~ '\.[^\/.]\+$' ? '.rb' : ''))|endif
let res = s:match_it('\v<File.expand_path\([''"]../(\f+)[''"],\s*__FILE__\s*\)',expand('%:p:h').'/\1')
if len(res)|return s:simplify(res)|endif
let res = s:match_it('\v\s*<require\s*\(=\s*File.dirname\(__FILE__\)\s*\+\s*[:''"](\f+)>.=',expand('%:p:h').'/\1')
if len(res)|return s:simplify(res.(res !~ '\.[^\/.]\+$' ? '.rb' : ''))|endif
let res = s:match_it('\v<File.dirname\(__FILE__\)\s*\+\s*[:''"](\f+)>[''"]=',expand('%:p:h').'\1')
if len(res)|return s:simplify(res)|endif
let res = s:match_it('\v\s*<%(include|extend)\(=\s*<([[:alnum:]_:]+)>','\1')
if len(res)|return rails#underscore(res, 1).".rb"|endif
let res = s:match_method('require')
if len(res)|return res.(res !~ '\.[^\/.]\+$' ? '.rb' : '')|endif
if !empty(s:match_method('\w\+'))
let class = s:match_it('^[^;#]*,\s*\%(:class_name\s*=>\|class_name:\)\s*["'':]\=\([[:alnum:]_:]\+\)','\1')
if len(class)|return rails#underscore(class, 1).".rb"|endif
endif
let res = s:match_method('belongs_to\|has_one\|embedded_in\|embeds_one\|composed_of\|validates_associated\|scaffold')
if len(res)|return res.'.rb'|endif
let res = s:match_method('has_many\|has_and_belongs_to_many\|embeds_many\|accepts_nested_attributes_for\|expose')
if len(res)|return rails#singularize(res).'.rb'|endif
let res = s:match_method('create_table\|change_table\|drop_table\|rename_table\|\%(add\|remove\)_\%(column\|index\|timestamps\|reference\|belongs_to\)\|rename_column\|remove_columns\|rename_index')
if len(res)|return rails#singularize(res).'.rb'|endif
let res = s:match_symbol('through')
if len(res)|return rails#singularize(res).".rb"|endif
let res = s:match_method('fixtures')
if len(res)|return 'fixtures/'.res.'.yml'|endif
let res = s:match_method('fixture_file_upload')
if len(res)|return 'fixtures/'.res|endif
let res = s:match_method('file_fixture')
if len(res)|return 'fixtures/files/'.res|endif
let res = s:match_method('\%(\w\+\.\)\=resources')
if len(res)|return res.'_controller.rb'|endif
let res = s:match_method('\%(\w\+\.\)\=resource')
if len(res)|return rails#pluralize(res)."_controller.rb"|endif
let res = s:match_symbol('to')
if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
let res = s:match_method('root\s*\%(:to\s*=>\|\<to:\)\s*')
if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
let res = s:match_method('\%(match\|get\|put\|patch\|post\|delete\|redirect\)\s*(\=\s*[:''"][^''"]*[''"]\=\s*\%(\%(,\s*:to\s*\)\==>\|,\s*to:\)\s*')
if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
let res = s:match_method('layout')
if len(res)|return s:findlayout(res)|endif
let res = s:match_method('helper')
if len(res)|return res.'_helper.rb'|endif
let res = s:match_symbol('controller')
if len(res)|return s:sub(res, '^/', '').'_controller.rb'|endif
let res = s:match_symbol('action')
if len(res)|return s:findview(res)|endif
let res = s:match_symbol('template')
if len(res)|return s:findview(res)|endif
let res = s:sub(s:sub(s:match_symbol('partial'),'^/',''),'[^/]+$','_&')
if len(res)|return s:findview(res)|endif
let res = s:sub(s:sub(s:match_method('(\=\s*\%(:partial\s\+=>\|partial:\s*\|json.partial!\)\s*'),'^/',''),'[^/]+$','_&')
if len(res)|return s:findview(res)|endif
let res = s:match_partial('render\%(_to_string\)\=\s*(\=\s*\%(:partial\s\+=>\|partial:\)\s*')
if len(res)|return res|endif
let res = s:match_method('render\>\s*\%(:\%(template\|action\)\s\+=>\|template:\|action:\)\s*')
if len(res)|return s:findview(res)|endif
let contr = matchstr(expand('%:p'), '.*[\/]app[\/]\%(controllers[\/].*\ze_controller\|mailers[\/].*\ze\|models[\/].*_mailer\ze\)\.rb$')
if len(contr)
let res = s:sub(s:match_symbol('layout'),'^/','')
if len(res)|return s:findlayout(res)|endif
let raw = s:sub(s:match_method('render\s*(\=\s*\%(:layout\s\+=>\|layout:\)\s*',1),'^/','')
if len(res)|return s:findview(res)|endif
let res = s:sub(s:match_method('render'),'^/','')
if len(res)|return s:findview(res)|endif
let viewpath = substitute(contr, '\([\/]\)app\zs[\/]\%(controllers\|mailers\|models\)\([\/].*\)', '\1views\2\1', '')
let view = s:match_it('\s*\<def\s\+\(\k\+\)\>(\=','\1')
if len(viewpath) && len(view)
let res = s:glob(viewpath . view . '.html.*')
if len(res)|return res[0]|endif
let res = s:glob(viewpath . view . '.*')
if len(res)|return res[0]|endif
return substitute(viewpath, '.*[\/]app[\/]views[\/]', '', '') . view . '.html'
endif
else
let res = s:sub(s:match_symbol('layout'),'^/','')
if len(res)|return s:findview(s:sub(res, '[^/]+$', '_&'))|endif
let raw = s:sub(s:match_method('render\s*(\=\s*\%(:layout\s\+=>\|layout:\)\s*',1),'^/','')
if len(res)|return s:findview(s:sub(res, '[^/]+$', '_&'))|endif
let res = s:match_partial('render')
if len(res)|return res|endif
endif
let res = s:match_method('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\<action:\)\s*')
if len(res)|return res|endif
let res = s:match_method('image[_-]\%(\|path\|url\)\|\%(path\|url\)_to_image')
if len(res)
return s:findasset(res, 'images')
endif
let res = s:match_method('stylesheet[_-]\%(link_tag\|path\|url\)\|\%(path\|url\)_to_stylesheet')
if len(res)
return s:findasset(res, 'stylesheets')
endif
let res = s:sub(s:match_method('javascript_\%(include_tag\|path\|url\)\|\%(path\|url\)_to_javascript'),'/defaults>','/application')
if len(res)
return s:findasset(res, 'javascripts')
endif
for [type, suf] in [['javascript', '.js'], ['stylesheet', '.css'], ['asset', '']]
let res = s:match_method(type.'_pack_\%(path\|tag\)')
let appdir = matchstr(expand('%:p'), '.*[\/]app[\/]\ze\%(views\|helpers\)[\/]')
if empty(appdir) && s:active()
let appdir = rails#app().path('app/')
endif
if len(res) && len(appdir)
let name = res . suf
let suffixes = rails#pack_suffixes(matchstr(name, '\.\zs\w\+$'))
call extend(suffixes, map(copy(suffixes), '"/index".v:val'))
let dir = appdir . 'javascript' . appdir[-1:-1] . 'packs' . appdir[-1:-1]
if len(suffixes)
let base = dir . substitute(name, '\.\w\+$', '', '')
for suffix in [''] + suffixes
if s:filereadable(base . suffix)
return base . suffix
endif
endfor
return dir . name
endif
endif
endfor
let decl = matchlist(getline('.'),
\ '^\(\s*\)\(\w\+\)\>\%\(\s\+\|\s*(\s*\):\=\([''"]\=\)\(\%(\w\|::\)\+\)\3')
if len(decl) && len(decl[0]) >= col('.')
let declid = synID(line('.'), 1+len(decl[1]), 1)
let declbase = rails#underscore(decl[4], 1)
if declid ==# hlID('rubyEntities')
return rails#singularize(declbase) . '.rb'
elseif declid ==# hlID('rubyEntity') || decl[4] =~# '\u'
return declbase . '.rb'
elseif index([hlID('rubyMacro'), hlID('rubyAttribute')], declid) >= 0
return rails#singularize(declbase) . '.rb'
endif
endif
let synid = synID(line('.'), col('.'), 1)
let old_isfname = &isfname
try
if synid == hlID('rubyString')
set isfname+=:
let cfile = expand("<cfile>")
else
set isfname=@,48-57,/,-,_,:,#
let cfile = expand("<cfile>")
if cfile !~# '\u\|/'
let cfile = s:sub(cfile, '_attributes$', '')
let cfile = rails#singularize(cfile)
let cfile = s:sub(cfile, '_ids=$', '')
endif
endif
finally
let &isfname = old_isfname
endtry
let cfile = s:sub(cfile, '^:=[:@]', '')
let cfile = s:sub(cfile, ':0x\x+$', '') " For #<Object:0x...> style output
if cfile =~# '^\l\w*#\w\+$'
let cfile = s:sub(cfile, '#', '_controller.rb#')
elseif cfile =~# '\u'
let cfile = rails#underscore(cfile, 1) . '.rb'
elseif cfile =~# '^\w*_\%(path\|url\)$' && synid != hlID('rubyString')
let route = s:gsub(cfile, '^hash_for_|_%(path|url)$', '')
let cfile = s:active() ? rails#app().named_route_file(route) : ''
if empty(cfile)
let cfile = s:sub(route, '^formatted_', '')
if cfile =~# '^\%(new\|edit\)_'
let cfile = s:sub(rails#pluralize(cfile), '^(new|edit)_(.*)', '\2_controller.rb#\1')
elseif cfile ==# rails#singularize(cfile)
let cfile = rails#pluralize(cfile).'_controller.rb#show'
else
let cfile = cfile.'_controller.rb#index'
endif
endif
elseif cfile !~# '\.'
let cfile .= '.rb'
endif
return cfile
endfunction
function! rails#ruby_cfile(...) abort
let cfile = s:find('find', s:ruby_cfile())[5:-1]
return empty(cfile) ? (a:0 ? eval(a:1) : expand('<cfile>')) : cfile
endfunction
function! s:app_named_route_file(route_name) dict abort
for route in self.routes()
if get(route, 'name', '') ==# a:route_name && route.handler =~# '#'
return s:sub(route.handler, '#', '_controller.rb#')
endif
endfor
return ""
endfunction
function! s:app_routes() dict abort
if self.cache.needs('routes')
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
let routes = []
let paths = {}
let binding = self.server_binding()
if len(binding) && len(s:webcat())
let html = system(s:webcat() . ' ' . shellescape('http://' . binding . '/rails/info/routes'))
for line in split(matchstr(html, '.*<tbody>\zs.*\ze</tbody>'), "\n")
let val = matchstr(line, '\C<td data-route-name=''\zs[^'']*''\ze>')
if len(val)
if len(routes) && len(routes[-1]) < 4
call remove(routes, -1)
endif
call add(routes, {'name': val[0:-2]})
endif
if empty(routes)
continue
endif
let val = matchstr(line, '\C<td data-route-path=''\zs[^'']*\ze''>')
if len(val)
let routes[-1].path = val
if empty(routes[-1].name)
l