pax_global_header 0000666 0000000 0000000 00000000064 13246657203 0014522 g ustar 00root root 0000000 0000000 52 comment=30f449fa4d8d85b9376e77e8f0cbee57af4bab84
vim-rails-5.4/ 0000775 0000000 0000000 00000000000 13246657203 0013275 5 ustar 00root root 0000000 0000000 vim-rails-5.4/.gitignore 0000664 0000000 0000000 00000000040 13246657203 0015257 0 ustar 00root root 0000000 0000000 /rails.zip
/rails.vba
/doc/tags
vim-rails-5.4/CONTRIBUTING.markdown 0000664 0000000 0000000 00000001744 13246657203 0016756 0 ustar 00root root 0000000 0000000 If your [commit message sucks][suck], I'm not going to accept your pull
request. I've explained very politely dozens of times that [my general
guidelines][guidelines] are absolute rules on my own repositories, so I may
lack the energy to explain it to you yet another time. And please, if I ask
you to change something, `git commit --amend` and `git push -f`.
If a feature idea is nontrivial, you should probably open an issue to [discuss
it][] before attempting a pull request. One of the biggest challenges in
maintaining rails.vim has been beating back the bloat, so do not assume that
your idea will make the cut. And if I like your idea, I'm generally amenable
to just knocking it out myself, rather than making you familiarize yourself
with a 4 thousand line code base.
[suck]: http://stopwritingramblingcommitmessages.com/
[guidelines]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
[discuss it]: http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/
vim-rails-5.4/README.markdown 0000664 0000000 0000000 00000011073 13246657203 0016000 0 ustar 00root root 0000000 0000000 # rails.vim
This is a massive (in a good way) Vim plugin for editing Ruby on Rails
applications.
* Easy navigation of the Rails directory structure. `gf` considers
context and knows about partials, fixtures, and much more. There are
two commands, `:A` (alternate) and `:R` (related) for easy jumping
between files, including favorites like model to schema, template to
helper, and controller to functional test. Commands like `:Emodel`,
`:Eview`, `:Econtroller`, are provided to `:edit` files by type, along
with `S`, `V`, and `T` variants for `:split`, `:vsplit`, and
`:tabedit`. Throw a bang on the end (`:Emodel foo!`) to automatically
create the file with the standard boilerplate if it doesn't exist.
`:help rails-navigation`
* Enhanced syntax highlighting. From `has_and_belongs_to_many` to
`distance_of_time_in_words`, it's here.
* Interface to the `rails` command. Generally, use `:Rails console` to
call `rails console`. Many commands have wrappers with additional features:
`:Generate controller Blog` generates a blog controller and loads the
generated files into the quickfix list, and `:Runner` wraps `rails runner`
and doubles as a direct test runner. `:help rails-exec`
* Default task runner. Use `:Rails` (with no arguments) to run the current
test, spec, or feature. Use `:.Rails` to do a focused run of just the
method, example, or scenario on the current line. `:Rails` can also run
arbitrary migrations, load individual fixtures, and more.
`:help rails-default-task`
* Partial and concern extraction. In a view, `:Extract {file}`
replaces the desired range (typically selected in visual line mode)
with `render '{file}'`, which is automatically created with your
content. In a model or controller, a concern is created, with the
appropriate `include` declaration left behind.
`:help rails-:Extract`
* Fully customizable. Define "projections" at the global, app, or gem
level to define navigation commands and override the alternate file,
default rake task, syntax highlighting, abbreviations, and more.
`:help rails-projections`.
* Integration with other plugins. If [dispatch.vim][] is installed, `:Rails`
and other command wrappers will use it for asynchronous execution. Users of
[abolish.vim](https://github.com/tpope/vim-abolish) get pluralize and
tableize coercions, and users of [bundler.vim][] get a smattering of
features. `:help rails-integration`
## Installation
If you don't have a preferred installation method, I recommend
installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and
then simply copy and paste:
cd ~/.vim/bundle
git clone https://github.com/tpope/vim-rails.git
vim -u NONE -c "helptags vim-rails/doc" -c q
While not strictly necessary, [bundler.vim][] and [dispatch.vim][] are highly
recommended.
[bundler.vim]: https://github.com/tpope/vim-bundler
[dispatch.vim]: https://github.com/tpope/vim-dispatch
## FAQ
> I installed the plugin and started Vim. Why does only the `:Rails`
> command exist?
This plugin cares about the current file, not the current working
directory. Edit a file from a Rails application.
> I opened a new tab. Why does only the `:Rails` command exist?
This plugin cares about the current file, not the current working directory.
Edit a file from a Rails application. You can use `:AT` and the `:T` family
of commands to open a new tab and edit a file at the same time.
> Can I use rails.vim to edit Rails engines?
It's not supported, but if you `touch config/environment.rb` in the root
of the engine, things should mostly work.
> Can I use rails.vim to edit other Ruby projects?
I wrote [rake.vim](https://github.com/tpope/vim-rake) for exactly that
purpose. It activates for any project with a `Rakefile` that's not a
Rails application.
> What Rails versions are supported?
All of them, although you may notice a few minor breakages if you dip below
3.0. A few features like syntax highlighting tend to reflect the latest
version only.
> Didn't rails.vim used to handle indent settings?
It got yanked after increasing contention over JavaScript. Check out
[sleuth.vim](https://github.com/tpope/vim-sleuth).
## Self-Promotion
Like rails.vim? Follow the repository on
[GitHub](https://github.com/tpope/vim-rails) and vote for it on
[vim.org](http://www.vim.org/scripts/script.php?script_id=1567). And if
you're feeling especially charitable, follow [tpope](http://tpo.pe/) on
[Twitter](http://twitter.com/tpope) and
[GitHub](https://github.com/tpope).
## License
Copyright (c) Tim Pope. Distributed under the same terms as Vim itself.
See `:help license`.
vim-rails-5.4/autoload/ 0000775 0000000 0000000 00000000000 13246657203 0015105 5 ustar 00root root 0000000 0000000 vim-rails-5.4/autoload/rails.vim 0000664 0000000 0000000 00000576544 13246657203 0016761 0 ustar 00root root 0000000 0000000 " 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.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(''), '.*\zs\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)
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: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: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
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
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 &l: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 == "" && type(default) == type('') ? 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.
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 = '\'
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
function! s:readable_define_pattern() dict abort
if self.name() =~ '\.yml\%(\.example\)\=$'
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.name() =~# '\.erb$'
let define .= '\|\\|\.*\|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) abort
echohl WarningMsg
echomsg a:str
echohl None
" Sometimes required to flush output
echo ""
let v:warningmsg = a:str
return ''
endfunction
function! s:deprecate(old, new, ...) abort
call s:warn(a:old . ' is deprecated in favor of ' . a:new)
return a:0 ? a:1 : ''
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)
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 =~ '\')
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 =~ '\.*\.'
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
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', ['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=+ 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 :exe s:deprecate(':Rpreview', ':Preview')|call s:Preview(0,,)
command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Rbrowse :call s:Preview(0,,)
command! -buffer -bar -nargs=? -bang -range -complete=customlist,s:Complete_preview Preview :call s:Preview(0,,)
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_log Rlog exe s:deprecate(':Rlog', ':Clog', 0 ? 'Clog '. : s:Plog(0, ))
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_log Clog exe s:Clog(1, '', )
command! -buffer -bar -nargs=* -bang Rset :exe 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() =~ '\,Extract '.)
command! -buffer -bar -bang -nargs=? -range -complete=customlist,s:controllerList Extract :,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'))
command! -buffer -bar -bang -nargs=1 -range Extract :,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', ''], )
command! -buffer -bar -bang -nargs=1 -range Extract :,call s:RubyExtract(0, '', matchstr(rails#buffer().name(), '^app/\w\+/').'concerns', [' extend ActiveSupport::Concern', ''], )
endif
if RailsFilePath() =~ '\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().path('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().path())
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 exists('b:rails_root')
return s:Rails(a:bang, a:count, a:arg)
elseif a:arg !~# '^new\>'
return 'echoerr '.string('Usage: rails new ')
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.path())
if self.has_path('.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 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
function! s:RefreshBuffer()
if exists("b:rails_refresh") && b:rails_refresh
let b:rails_refresh = 0
let &filetype = &filetype
unlet! b:rails_refresh
endif
endfunction
" }}}1
" Rake {{{1
function! s:qf_pre() abort
let dir = substitute(matchstr(','.&l:errorformat, ',chdir \zs\%(\\.\|[^,]\)*'), '\\,' ,',', 'g')
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('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
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 = {}
if lnum
let placeholders.l = lnum
let placeholders.lnum = lnum
let placeholders.line = lnum
let last = self.last_method(lnum)
if !empty(last)
let placeholders.d = last
let placeholders.define = 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\)\>'
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(),'\
elseif has("gui_mac") || has("gui_macvim") || exists("$SECURITYSESSIONID")
command -bar -nargs=1 OpenURL exe '!open' shellescape(, 1)
elseif has("gui_win32")
command -bar -nargs=1 OpenURL exe '!start cmd /cstart /b' shellescape(, 1)
elseif executable("xdg-open")
command -bar -nargs=1 OpenURL exe '!xdg-open' shellescape(, 1) '&'
elseif executable("sensible-browser")
command -bar -nargs=1 OpenURL exe '!sensible-browser' shellescape(, 1)
elseif executable('launchy')
command -bar -nargs=1 OpenURL exe '!launchy' shellescape(, 1)
elseif executable('git')
command -bar -nargs=1 OpenURL exe '!git web--browse' shellescape(, 1)
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 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/assets/stylesheets/'
let urls = urls + ['/assets/application.css']
elseif self.name() =~ '^app/assets/javascripts/'
let urls = urls + ['/assets/application.js']
elseif self.controller_name() != '' && self.controller_name() != 'application'
if self.type_name('controller') && self.last_method(a:lnum) != ''
let handler = self.controller_name().'#'.self.last_method(a:lnum)
elseif self.type_name('controller','view-layout','view-partial')
let handler = self.controller_name().'#index'
elseif self.type_name('view')
let handler = self.controller_name().'#'.fnamemodify(self.name(),':t:r:r')
endif
if exists('handler')
for route in self.app().routes()
if route.method =~# 'GET' && route.handler ==# handler
let urls += [s:gsub(s:gsub(route.path, '\([^()]*\)', ''), ':\w+', '1')]
endif
endfor
endif
endif
return urls
endfunction
call s:add_methods('readable', ['preview_urls'])
function! s:app_server_pid() dict abort
for type in ['server', 'unicorn']
let pidfile = self.path('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 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 s:deprecate(':Rscript', ':Rails', 'Rails' . empty() ? 'console' : )
command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_environments Console :Rails console
command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_generate Rgenerate :execute s:deprecate(':Rgenerate', ':Generate', rails#app().generator_command(0,'','generate',))
command! -buffer -bang -bar -nargs=* -complete=customlist,s:Complete_generate Generate :execute rails#app().generator_command(0,'','generate',)
command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Rdestroy :execute s:deprecate(':Rdestroy', ':Destroy', rails#app().generator_command(1,'','destroy',))
command! -buffer -bar -nargs=* -complete=customlist,s:Complete_destroy Destroy :execute rails#app().generator_command(1,'','destroy',)
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Rserver :execute s:deprecate(':Rserver', ':Server', rails#app().server_command(0, 1, ))
command! -buffer -bar -nargs=? -bang -complete=customlist,s:Complete_server Server :execute rails#app().server_command(0, 0, )
command! -buffer -bang -nargs=? -range=0 -complete=customlist,s:Complete_edit Rrunner :execute s:deprecate(':Rrunner', ':Runner', rails#buffer().runner_command(0, ?:0, ))
command! -buffer -bang -nargs=? -range=0 -complete=customlist,s:Complete_edit Runner :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(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('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
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.' -an -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.' -an -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! s:Complete_script(ArgLead, CmdLine, P) abort
return rails#complete_rails(a:ArgLead, a:CmdLine, a:P, rails#app())
endfunction
function! rails#complete_rails(ArgLead, CmdLine, P, ...) abort
if a:0
let app = a:1
else
let manifest = findfile('config/environment.rb', escape(getcwd(), ' ,;').';')
let app = empty(manifest) ? {} : rails#app(fnamemodify(manifest, ':p:h:h'))
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*$'
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:mailerList(a:ArgLead,"","")
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)
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_alternate A exe s:Alternate(' E',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AE exe s:Alternate(' E',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AS exe s:Alternate(' S',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AV exe s:Alternate(' V',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_alternate AT exe s:Alternate(' T',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit AD exe s:Alternate(' D',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit AR exe s:Alternate(' D',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related R exe s:Related(' E',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RE exe s:Related(' E',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RS exe s:Related(' S',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RV exe s:Related(' V',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_related RT exe s:Related(' T',,,,)
command! -buffer -bar -nargs=* -range=0 -complete=customlist,s:Complete_edit RD exe s:Related(' D',,,,)
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*\%(\ 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)
return s:gsub(s:gsub(a:arg,'[^/.]','[&]*'),'%(/|^)\.@!|\.','&*')
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! rails#includeexpr(fname) abort
if a:fname =~# '\u' && a:fname !~# '[./]'
return rails#underscore(a:fname) . '.rb'
else
return a:fname
endif
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: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 = simplify(expand('%:p:h') . '/' . path)
endif
let suffixes = s:suffixes(a:dir)
let asset = rails#app().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 filereadable(sass)
return sass
elseif filereadable(sass.'.sass')
return sass.'.sass'
elseif filereadable(sass.'.scss')
return sass.'.scss'
endif
endif
let public = rails#app().path('public/' . a:dir . '/' . path)
let post = get(suffixes, 0, '')
if filereadable(public)
return public
elseif 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:is_embedded_ruby() abort
let groups = [
\ 'erubyBlock', 'erubyExpression', 'erubyComment', 'erubyOneLiner',
\ 'hamlRuby']
call map(groups, 'hlID(v:val)')
for id in synstack(line('.'), col('.'))
if index(groups, id) >= 0 || synIDattr(id, 'name') =~# '^ruby'
return 1
endif
endfor
endfunction
function! s:cfile_delegate(expr) abort
let expr = empty(a:expr) ? matchstr(&includeexpr, '.*\.*') : a:expr
if empty(expr)
let expr = 'v:fname'
endif
let expr = substitute(expr, '\', 'expand("")', 'g')
return expr
endfunction
function! rails#embedded_cfile(...) abort
if s:is_embedded_ruby()
let expr = 'rails#cfile('.(a:0 > 1 ? string(a:2) : '').')'
else
let expr = s:cfile_delegate(a:0 ? a:1 : '')
endif
return eval(expr)
endfunction
function! s:asset_cfile() abort
let buffer = rails#buffer()
let dir = ''
if buffer.type_name('javascript')
let dir = 'javascripts'
elseif buffer.type_name('stylesheet')
let dir = 'stylesheets'
let asset = ''
let sssuf = s:suffixes('stylesheets')
let res = s:findit('\%(^\s*[[:alnum:]-]\+:\s\+\)\=\<[[:alnum:]-]\+-\%(path\|url\)(["'']\=\([^"''() ]*\)', '\1')
if !empty(res)
let asset = rails#app().resolve_asset(res)
endif
let res = s:findit('\%(^\s*[[:alnum:]-]\+:\s\+\)\=\.=',expand('%:p:h').'/\1')
if res != ""|return simplify(res.(res !~ '\.[^\/.]\+$' ? '.rb' : ''))|endif
let res = s:findit('\v[''"]=',expand('%:p:h').'\1')
if res != ""|return simplify(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.(res !~ '\.[^\/.]\+$' ? '.rb' : '')|endif
if !empty(s:findamethod('\w\+', '\1'))
let class = s:findit('^[^;#]*,\s*\%(:class_name\s*=>\|class_name:\)\s*["'':]\=\([[:alnum:]_:]\+\)','\1')
if class != ""|return rails#underscore(class).".rb"|endif
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\|accepts_nested_attributes_for\|expose','\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.yml')
if res != ""|return res|endif
let res = s:findamethod('\%(\w\+\.\)\=resources','\1_controller.rb')
if res != ""|return res|endif
let res = s:findamethod('\%(\w\+\.\)\=resource','\1')
if res != ""|return rails#pluralize(res)."_controller.rb"|endif
let res = s:findasymbol('to','\1')
if res =~ '#'|return s:sub(res,'#','_controller.rb#')|endif
let res = s:findamethod('root\s*\%(:to\s*=>\|\\|,\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 s:findview(res)|endif
let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:layout\s\+=>\|layout:\)\s*','\1'),'^/',''),'[^/]+$','_&')
if res != ""|return 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 s:findview(res)|endif
let res = s:sub(s:sub(s:findfromview('json\.(\=\s*\%(:partial\s\+=>\|partial!\)\s*','\1'),'^/',''),'[^/]+$','_&')
if res != ""|return s:findview(res)|endif
let res = s:sub(s:sub(s:findfromview('render\s*(\=\s*\%(:partial\s\+=>\|partial:\)\s*','\1'),'^/',''),'[^/]+$','_&')
if res != ""|return 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 s:findview(res)|endif
let res = s:findamethod('redirect_to\s*(\=\s*\%\(:action\s\+=>\|\','/application')
if res != ""
return s:findasset(res, 'javascripts')
endif
if buffer.type_name('controller', 'mailer')
let contr = s:controller()
let view = s:findit('\s*\(\=','/\1')
if view !=# ''
let res = rails#buffer().resolve_view(contr.view)
if res != ""|return res|endif
endif
endif
let synid = synID(line('.'), col('.'), 1)
let old_isfname = &isfname
try
if synid == hlID('rubyString')
set isfname+=:
let cfile = expand("")
else
set isfname=@,48-57,/,-,_,:,#
let cfile = expand("")
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 # style output
if cfile =~# '^\l\w*#\w\+$'
let cfile = s:sub(cfile, '#', '_controller.rb#')
elseif cfile =~# '\u'
let cfile = rails#underscore(cfile) . '.rb'
elseif cfile =~# '^\w*_\%(path\|url\)$' && synid != hlID('rubyString')
let route = s:gsub(cfile, '^hash_for_|_%(path|url)$', '')
let cfile = 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#cfile(...) abort
let cfile = s:find('find', s:ruby_cfile())[5:-1]
return empty(cfile) ? (a:0 ? eval(a:1) : expand('')) : cfile
endfunction
function! s:app_named_route_file(route_name) dict abort
for route in self.routes()
if get(route, 'name', '') ==# a:route_name
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 = {}
try
execute cd fnameescape(rails#app().path())
let output = system(self.rake_command().' routes')
finally
execute cd fnameescape(cwd)
endtry
for line in split(output, "\n")
let matches = matchlist(line, '^ *\(\l\w*\|\) \{-\}\([A-Z|]*\) \+\(\S\+\) \+\([[:alnum:]_/]\+#\w\+\)\%( {.*\)\=$')
if !empty(matches)
let [_, name, method, path, handler; __] = matches
if !empty(name)
let paths[path] = name
else
let name = get(paths, path, '')
endif
call insert(routes, {'method': method, 'path': path, 'handler': handler, 'name': name})
endif
endfor
call self.cache.set('routes', routes)
endif
return self.cache.get('routes')
endfunction
call s:add_methods('app', ['routes', 'named_route_file'])
" }}}1
" Projection Commands {{{1
function! s:app_commands() dict abort
let commands = {}
let all = self.projections()
for pattern in sort(keys(all), function('rails#lencmp'))
let projection = all[pattern]
for name in s:split(get(projection, 'command', get(projection, 'type', get(projection, 'name', ''))))
let command = {
\ 'pattern': pattern,
\ 'affinity': get(projection, 'affinity', '')}
if !has_key(commands, name)
let commands[name] = []
endif
call insert(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("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, ...) abort
if exists('*projectionist#completion_filter')
return projectionist#completion_filter(a:results, a:A, a:0 ? a:1 : '/')
endif
let results = s:uniq(sort(type(a:results) == type("") ? split(a:results,"\n") : copy(a:results)))
call filter(results,'v:val !~# "\\~$"')
if a:A =~# '\*'
let regex = s:gsub(a:A,'\*','.*')
return filter(copy(results),'v:val =~# "^".regex')
endif
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: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' ? '' : '') . prefix . "") . ',' .
\ 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: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 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
elseif 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:open(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/schema.rb'
if !rails#app().has_file('db/schema.rb')
if rails#app().has_file('db/structure.sql')
let schema = 'db/structure.sql'
elseif rails#app().has_file('db/'.s:environment().'_structure.sql')
let schema = 'db/'.s:environment().'_structure.sql'
endif
endif
return s:open(cmd,schema.(a:0 && a:1 !=# '.' ? '#'.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:open(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
if name !~# '/'
let controller = self.controller_name(1)
let found = ''
if controller != ''
let found = call(self.resolve_view, [controller.'/'.name] + a:000, self)
endif
if empty(found)
let found = call(self.resolve_view, ['application/'.name] + a:000, self)
endif
return found
endif
if name =~# '/' && !self.app().has_path(fnamemodify('app/views/'.name, ':h'))
return ''
elseif name =~# '\.[[:alnum:]_+]\+\.\w\+$' || name =~# '\.\%('.join(s:view_types,'\|').'\)$'
return self.app().path('app/views/'.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 self.app().path(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
function! s:app_asset_path() dict abort
let gems = self.gems()
if self.cache.needs('gem_assets', gems)
let path = []
let gempath = escape(join(values(gems),','), ' ')
if !empty(gempath)
call extend(path, finddir('app/assets/', gempath, -1))
call extend(path, finddir('lib/assets/', gempath, -1))
call extend(path, finddir('vendor/assets/', gempath, -1))
call extend(path, finddir('assets/', gempath, -1))
call map(path, 'v:val . "*"')
call sort(path)
endif
call self.cache.set('gem_assets', path, gems)
endif
return extend([self.path('app/assets/*'), self.path('lib/assets/*'), self.path('vendor/assets/*'), self.path('node_modules')],
\ self.cache.get('gem_assets'))
endfunction
function! s:app_resolve_asset(name, ...) dict abort
let path = join(map(copy(self.asset_path()), 'escape(v:val, " ,")'), ',')
let suffixesadd = &l:suffixesadd
try
let &l:suffixesadd = join(a:0 ? (type(a:1) ==# type([]) ? a:1 : s:suffixes(a:1)) : [], ',')
let exact = findfile(a:name, path)
finally
let &l:suffixesadd = suffixesadd
endtry
if !empty(exact)
return fnamemodify(exact, ':p')
endif
return ''
endfunction
call s:add_methods('readable', ['resolve_view', 'resolve_layout'])
call s:add_methods('app', ['asset_path', 'resolve_asset'])
function! s:findview(name) abort
let view = rails#buffer().resolve_view(a:name, line('.'))
return empty(view) ? a:name : view
endfunction
function! s:findlayout(name)
return rails#buffer().resolve_layout(a:name, line('.'))
endfunction
function! s:viewEdit(cmd, ...) abort
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,'#.*\|:\d*\ze\%(:in\)\=$') : ''
if !empty(found)
return s:edit(a:cmd,found.djump)
elseif a:0 && a:1 =~# '!'
let file = 'app/views/'.view
if !rails#app().has_path(fnamemodify(file, ':h'))
call mkdir(rails#app().path(fnamemodify(file, ':h')), 'p')
endif
return s:edit(a:cmd, file)
else
return s:open(a:cmd, 'app/views/'.view)
endif
endfunction
function! s:layoutEdit(cmd,...) abort
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, file)
endfunction
function! s:AssetEdit(cmd, name, dir, suffix, fallbacks) abort
let name = matchstr(a:name, '^[^!#:]*')
if empty(name)
let name = s:controller(1)
endif
if empty(name)
return s:error("E471: Argument required")
endif
let suffixes = s:suffixes(a:dir)
for file in map([''] + suffixes, '"app/assets/".a:dir."/".name.v:val') +
\ map(copy(a:fallbacks), 'printf(v:val, name)') +
\ [ 'public/'.a:dir.'/'.name.suffixes[0],
\ 'app/assets/'.a:dir.'/'.name.(name =~# '\.' ? '' : a:suffix)]
if rails#app().has_file(file)
break
endif
endfor
if name =~# '\.' || a:name =~# '!'
return s:edit(a:cmd, file . matchstr(a:name, '[!#:]*'))
else
return s:open(a:cmd, file . matchstr(a:name, '[!#:]*'))
endif
endfunction
function! s:javascriptEdit(cmd,...) abort
return s:AssetEdit(a:cmd, a:0 ? a:1 : '', 'javascripts',
\ rails#app().has_gem('coffee-rails') ? '.coffee' : '.js', [])
endfunction
function! s:stylesheetEdit(cmd,...) abort
let fallbacks = []
if rails#app().has('sass')
let fallbacks = ['public/stylesheets/sass/%s.sass', 'public/stylesheets/sass/%s.scss']
endif
return s:AssetEdit(a:cmd, a:0 ? a:1 : '', 'stylesheets',
\ rails#app().stylesheet_suffix(), fallbacks)
endfunction
function! s:javascriptList(A, L, P, ...) abort
let dir = a:0 ? a:1 : 'javascripts'
let list = rails#app().relglob('app/assets/'.dir.'/','**/*.*','')
let suffixes = s:suffixes(dir)
let strip = '\%('.escape(join(suffixes, '\|'), '.*[]~').'\)$'
let g:strip = strip
call map(list,'substitute(v:val,strip,"","")')
call extend(list, rails#app().relglob("public/".dir."/","**/*",suffixes[0]))
if !empty(a:0 ? a:2 : [])
call extend(list, a:2)
call s:uniq(list)
endif
return s:completion_filter(list,a:A)
endfunction
function! s:stylesheetList(A, L, P) abort
let extra = []
if rails#app().has('sass')
let extra = rails#app().relglob('public/stylesheets/sass/','**/*','.s?ss')
endif
return s:javascriptList(a:A, a:L, a:P, 'stylesheets', extra)
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 'rails_helper'\n\ndescribe ".describe." do\nend"},
\ {'pattern': 'spec/spec_helper.rb'},
\ {'pattern': 'spec/rails_helper.rb'}])
endfunction
" }}}1
" Alternate/Related {{{1
function! s:findcmdfor(cmd) abort
let bang = ''
if a:cmd =~ '\!$'
let bang = '!'
let cmd = s:sub(a:cmd,'\!$','')
else
let cmd = a:cmd
endif
let cmd = s:mods(cmd)
let num = matchstr(cmd, '.\{-\}\ze\a*$')
let cmd = matchstr(cmd, '\a*$')
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.'tab sfind'.bang
elseif cmd == 'D'
return num.'read'.bang
else
return num.cmd.bang
endif
endfunction
function! s:editcmdfor(cmd) abort
let cmd = s:findcmdfor(a:cmd)
let cmd = s:sub(cmd,'','split')
let cmd = s:sub(cmd,'','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:r_warning(cmd) abort
let cmd = matchstr(a:cmd, '\w\+$')
if cmd =~# 'R\|^$'
let old = s:sub(cmd, '^$', 'R')
let instead = s:sub(s:sub(cmd, '^R', ''), '^$', 'E')
return '|echohl WarningMsg|echomsg ":'.old.' navigation commands are deprecated. Use :'.instead.' commands instead."|echohl None'
endif
return ''
endfunction
function! s:readable_open_command(cmd, argument, name, projections) dict abort
let cmd = s:editcmdfor(s:sub(a:cmd, '^R', ''))
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 = fnamemodify(self.app().path(file), ':.')
return cmd . ' ' . s:jumpargs(file, djump) . s:r_warning(a:cmd)
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 = {
\ 'match': root,
\ 'file': file,
\ 'project': self.app().path(),
\ 'S': rails#camelize(root),
\ 'h': toupper(root[0]) . tr(rails#underscore(root), '_', ' ')[1:-1]}
call map(template, 's:expand_placeholders(v:val, ph)')
call map(template, 's:gsub(v:val, "\t", " ")')
let file = fnamemodify(simplify(file), ':.')
return cmd . ' ' . s:fnameescape(file) . '|call setline(1, '.string(template).')' . '|set nomod'.s:r_warning(a:cmd)
else
return cmd . ' +AD ' . s:fnameescape(file) . s:r_warning(a:cmd)
endif
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:find(cmd, file) abort
let djump = matchstr(a:file,'!.*\|#\zs.*\|:\zs\d*\ze\%(:in\)\=$')
let file = s:sub(a:file,'[#!].*|:\d*%(:in)=$','')
if file =~# '^\.\.\=\%([\/]\|$\)'
let file = simplify(rails#app().path() . s:sub(file[1:-1], '^\.', '/..'))
endif
let cmd = (empty(a:cmd) ? '' : s:findcmdfor(a:cmd))
if djump =~# '!'
if !isdirectory(fnamemodify(file, ':h'))
call mkdir(fnamemodify(file, ':h'), 'p')
endif
return s:editcmdfor(cmd) . ' ' . s:jumpargs(fnamemodify(file, ':~:.'), djump)
else
return cmd . ' ' . s:jumpargs(file, djump)
endif
endfunction
function! s:open(cmd, file) abort
return s:find(a:cmd, rails#app().path(a:file))
endfunction
function! s:edit(cmd, file) abort
return s:open(s:editcmdfor(a:cmd), a:file)
endfunction
function! s:AR(cmd,related,line1,line2,count,...) abort
if a:0
let cmd = ''
let i = 1
while i < a:0
let cmd .= ' ' . s:escarg(a:{i})
let i += 1
endwhile
let file = a:{i}
if file =~# '^#\h'
return s:jump(file[1:-1], s:sub(a:cmd, 'D', 'E'))
elseif a:count && a:cmd !~# 'D'
let c = a:count
let tail = matchstr(file,'[#!].*$\|:\d*\%(:in\>.*\)\=$')
if tail != ""
let file = s:sub(file,'[#!].*$|:\d*%(:in>.*)=$','')
endif
if file != ""
if a:related
if file =~# '\u'
let file = rails#underscore(file)
endif
let found = rails#app().find_file(file, rails#app().internal_load_path(), '.rb', a:count)
if !empty(found)
let file = fnamemodify(found, ':p')
let c = ''
else
let c = 99999999
endif
endif
endif
return c.s:find(a:cmd . cmd, file . tail)
else
let cmd = s:editcmdfor((a:count ? a:count : '').a:cmd) . cmd
return s:edit(cmd, file)
endif
elseif a:cmd =~# 'D'
let modified = &l:modified
let template = s:split(get(rails#buffer().projected('template'), 0, []))
call map(template, 's:gsub(v:val, "\t", " ")')
if a:line2 == a:count
call append(a:line2, template)
else
silent %delete_
call setline(1, template)
if !modified && !filereadable(expand('%'))
setlocal nomodified
endif
endif
return ''
else
let line = a:related ? a:line1 : a:count
let file = get(b:, line ? 'rails_related' : 'rails_alternate')
if empty(file)
let file = rails#buffer().alternate(line)
endif
let has_path = !empty(file) && rails#app().has_path(file)
let confirm = &confirm || (histget(':', -1) =~# '\%(^\||\)\s*conf\%[irm]\>')
if confirm && !line && !has_path
let projected = rails#buffer().projected_with_raw('alternate')
call filter(projected, 'rails#app().has_path(matchstr(v:val[1], "^[^{}]*/"))')
if len(projected)
let choices = ['Create alternate file?']
let i = 0
for [alt, _] in projected
let i += 1
call add(choices, i.' '.alt)
endfor
let i = inputlist(choices)
if i > 0 && i <= len(projected)
let file = projected[i-1][0] . '!'
else
return ''
endif
endif
endif
if empty(file)
call s:error("No alternate file defined")
return ''
else
return s:find(a:cmd, rails#app().path(file))
endif
endif
endfunction
function! s:Alternate(cmd,line1,line2,count,...) abort
return call('s:AR',[a:cmd,0,a:line1,a:line2,a:count]+a:000)
endfunction
function! s:Related(cmd,line1,line2,count,...)
return call('s:AR',[a:cmd,1,a:line1,a:line2,a:count]+a:000)
endfunction
function! s:Complete_alternate(A,L,P)
if a:L =~# '^[[:alpha:]]' || a:A =~# '^\w*:\|^\.\=[\/]'
return s:Complete_edit(a:A,a:L,a:P)
else
let seen = {}
for glob in filter(s:pathsplit(&l:path), 's:startswith(v:val,rails#app().path())')
for path in split(glob(glob), "\n")
for file in split(glob(path.'/'.s:fuzzyglob(a:A)), "\n")
let file = file[strlen(path) + 1 : ]
let file = substitute(file, '\%('.escape(tr(&l:suffixesadd, ',', '|'), '.|').'\)$', '', '')
let seen[file] = 1
endfor
endfor
endfor
return s:completion_filter(sort(keys(seen)), a:A)
endif
endfunction
function! s:Complete_related(A,L,P)
if a:L =~# '^[[:alpha:]]' || a:A =~# '^\w*:\|^\.\=[\/]'
return s:Complete_edit(a:A,a:L,a:P)
else
let seen = {}
for path in rails#app().internal_load_path()
let path = path[strlen(rails#app().path()) + 1 : ]
if path !~# '[][*]\|^\.\=$\|^vendor\>'
for file in rails#app().relglob(path == '' ? '' : path.'/',s:fuzzyglob(rails#underscore(a:A)), a:A =~# '\u' ? '.rb' : '')
let file = substitute(file, '\.rb$', '', '')
let seen[file] = 1
endfor
endif
endfor
return s:autocamelize(sort(keys(seen)), a:A)
endif
endfunction
function! s:readable_alternate_candidates(...) dict abort
let f = self.name()
let placeholders = {}
if a:0 && a:1
let placeholders.lnum = a:1
let placeholders.line = a:1
let lastmethod = self.last_method(a:1)
if !empty(lastmethod)
let placeholders.d = lastmethod
let placeholders.define = 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-record')
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 s:mods(a:mods) '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, mods, 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 s:mods(a:mods) '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 =~ '\'
let add = s:migspc(line).'change_column_null'.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 abort
if a:0 == 1
return self.dict[a:1][0]
else
return self.dict
endif
endfunction
function! s:cache_needs(key, ...) dict abort
return !has_key(self.dict, a:key) || (a:0 && a:1 isnot# get(self.dict[a:key], 1, {}))
endfunction
function! s:cache_set(key, value, ...) dict abort
let self.dict[a:key] = [a:value] + a:000
endfunction
call s:add_methods('cache', ['clear','needs','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 "
\."cache cache_fragment_name cache_hit 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_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 fields_for file_field file_field_tag flash font_path font_url form_for form_tag form_with "
\."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_if link_to_unless link_to_unless_current localize "
\."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 public_compute_asset_path "
\."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_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 to_sentence 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") +
\ self.relglob("app/jobs/","**/*",".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! rails#buffer_syntax()
if !exists("g:rails_no_syntax")
let buffer = rails#buffer()
let keywords = split(join(filter(buffer.projected('keywords'), 'type(v:val) == type("")'), ' '))
let special = filter(copy(keywords), 'v:val =~# ''^\h\k*[?!]$''')
let regular = filter(copy(keywords), 'v:val =~# ''^\h\k*$''')
if &syntax == 'ruby'
if !empty(special)
exe 'syn match rubyRailsMethod "\<\%('.join(special, '\|').'\)"'
endif
if !empty(regular)
exe 'syn keyword rubyRailsMethod '.join(regular, ' ')
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-record')
syn keyword rubyRailsARMethod attribute default_scope enum 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 before_commit
syn keyword rubyRailsARCallbackMethod after_create after_destroy after_save after_update after_validation after_validation_on_create after_validation_on_update after_create_commit after_update_commit after_destroy_commit
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 has_secure_token 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_absence_of validates_size_of validates_uniqueness_of validates_with
syn keyword rubyRailsMethod logger
endif
if buffer.type_name('model-observer')
syn keyword rubyRailsARMethod observe
endif
if buffer.type_name('mailer')
syn keyword rubyRailsRenderMethod mail render
syn keyword rubyRailsControllerMethod attachments default helper helper_attr helper_method layout
endif
if buffer.type_name('job')
syn keyword rubyRailsAPIMethod queue_as rescue_from
syn keyword rubyRailsARCallbackMethod before_enqueue around_enqueue after_enqueue before_perform around_perform after_perform
endif
if buffer.type_name('controller','helper','mailer','view')
syn keyword rubyRailsMethod logger url_for polymorphic_path polymorphic_url edit_polymorphic_path edit_polymorphic_url new_polymorphic_path new_polymorphic_url
endif
if buffer.type_name('helper','view')
exe 'syn keyword rubyRailsHelperMethod' s:helpermethods()
syn match rubyRailsHelperMethod '\