diff options
-rw-r--r-- | runtime/autoload/rubycomplete.vim | 57 | ||||
-rw-r--r-- | runtime/compiler/eruby.vim | 5 | ||||
-rw-r--r-- | runtime/compiler/rake.vim | 13 | ||||
-rw-r--r-- | runtime/compiler/rspec.vim | 4 | ||||
-rw-r--r-- | runtime/compiler/ruby.vim | 10 | ||||
-rw-r--r-- | runtime/compiler/rubyunit.vim | 1 | ||||
-rw-r--r-- | runtime/doc/autocmd.txt | 6 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 6 | ||||
-rw-r--r-- | runtime/doc/index.txt | 5 | ||||
-rw-r--r-- | runtime/doc/options.txt | 5 | ||||
-rw-r--r-- | runtime/doc/quickfix.txt | 21 | ||||
-rw-r--r-- | runtime/doc/sign.txt | 7 | ||||
-rw-r--r-- | runtime/doc/spell.txt | 2 | ||||
-rw-r--r-- | runtime/ftplugin/bash.vim | 31 | ||||
-rw-r--r-- | runtime/ftplugin/eruby.vim | 34 | ||||
-rw-r--r-- | runtime/ftplugin/ruby.vim | 100 | ||||
-rw-r--r-- | runtime/ftplugin/text.vim | 7 | ||||
-rw-r--r-- | runtime/indent/eruby.vim | 10 | ||||
-rw-r--r-- | runtime/indent/ruby.vim | 886 | ||||
-rw-r--r-- | runtime/syntax/eruby.vim | 19 | ||||
-rw-r--r-- | runtime/syntax/ruby.vim | 71 |
21 files changed, 842 insertions, 458 deletions
diff --git a/runtime/autoload/rubycomplete.vim b/runtime/autoload/rubycomplete.vim index 40b87f4cbe..ea18470232 100644 --- a/runtime/autoload/rubycomplete.vim +++ b/runtime/autoload/rubycomplete.vim @@ -1,9 +1,9 @@ " Vim completion script -" Language: Ruby -" Maintainer: Mark Guzman <segfault@hasno.info> -" URL: https://github.com/vim-ruby/vim-ruby -" Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" Maintainer Version: 0.8.1 +" Language: Ruby +" Maintainer: Mark Guzman <segfault@hasno.info> +" URL: https://github.com/vim-ruby/vim-ruby +" Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 " ---------------------------------------------------------------------------- " " Ruby IRB/Complete author: Keiju ISHITSUKA(keiju@ishitsuka.com) @@ -103,7 +103,7 @@ function! s:GetBufferRubyEntity( name, type, ... ) endif let curpos = getpos(".") - let [enum,ecol] = searchpairpos( crex, '', '\(end\|}\)', 'wr' ) + let [enum,ecol] = searchpairpos( crex, '', '\(end\|}\)', 'W' ) call cursor(lastpos[1], lastpos[2]) if lnum > enum @@ -253,15 +253,27 @@ class VimRubyCompletion # {{{ buffer analysis magic def load_requires + + custom_paths = VIM::evaluate("get(g:, 'rubycomplete_load_paths', [])") + + if !custom_paths.empty? + $LOAD_PATH.concat(custom_paths).uniq! + end + buf = VIM::Buffer.current enum = buf.line_number nums = Range.new( 1, enum ) nums.each do |x| + ln = buf[x] begin - eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln ) - rescue Exception - #ignore? + if /.*require_relative\s*(.*)$/.match( ln ) + eval( "require %s" % File.expand_path($1) ) + elsif /.*require\s*(["'].*?["'])/.match( ln ) + eval( "require %s" % $1 ) + end + rescue Exception => e + dprint e.inspect end end end @@ -344,8 +356,13 @@ class VimRubyCompletion if x != cur_line next if x == 0 ln = buf[x] - if /^\s*(module|class|def|include)\s+/.match(ln) - clscnt += 1 if $1 == "class" + is_const = false + if /^\s*(module|class|def|include)\s+/.match(ln) || is_const = /^\s*?[A-Z]([A-z]|[1-9])*\s*?[|]{0,2}=\s*?.+\s*?/.match(ln) + clscnt += 1 if /class|module/.match($1) + # We must make sure to load each constant only once to avoid errors + if is_const + ln.gsub!(/\s*?[|]{0,2}=\s*?/, '||=') + end #dprint "\$1$1 classdef += "%s\n" % ln classdef += "end\n" if /def\s+/.match(ln) @@ -423,7 +440,6 @@ class VimRubyCompletion return get_buffer_entity_list( "class" ) end - def load_rails allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails") return if allow_rails.to_i.zero? @@ -529,7 +545,6 @@ class VimRubyCompletion ret += ActiveRecord::ConnectionAdapters::SchemaStatements.methods end - return ret end @@ -587,11 +602,13 @@ class VimRubyCompletion # {{{ main completion code def self.preload_rails a = VimRubyCompletion.new - require 'Thread' - Thread.new(a) do |b| - begin - b.load_rails - rescue + if VIM::evaluate("has('nvim')") == 0 + require 'thread' + Thread.new(a) do |b| + begin + b.load_rails + rescue + end end end a.load_rails @@ -612,7 +629,6 @@ class VimRubyCompletion want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')") load_gems unless want_gems.to_i.zero? - input = VIM::Buffer.current.line cpos = VIM::Window.current.cursor[1] - 1 @@ -666,6 +682,7 @@ class VimRubyCompletion message = Regexp.quote($4) dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ] load_buffer_class( receiver ) + load_buffer_module( receiver ) begin classes = eval("#{receiver}.constants") #methods = eval("#{receiver}.methods") @@ -786,7 +803,6 @@ class VimRubyCompletion methods += Kernel.public_methods end - include_object = VIM::evaluate("exists('g:rubycomplete_include_object') && g:rubycomplete_include_object") methods = clean_sel( methods, message ) methods = (methods-Object.instance_methods) if include_object == "0" @@ -829,5 +845,4 @@ let s:rubycomplete_rails_loaded = 0 call s:DefRuby() "}}} ruby-side code - " vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl: diff --git a/runtime/compiler/eruby.vim b/runtime/compiler/eruby.vim index 45ad5eeadf..a81a3f3b77 100644 --- a/runtime/compiler/eruby.vim +++ b/runtime/compiler/eruby.vim @@ -3,6 +3,7 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2018 Jan 25 if exists("current_compiler") finish @@ -28,8 +29,8 @@ CompilerSet errorformat= \%W%f:%l:\ warning:\ %m, \%E%f:%l:in\ %*[^:]:\ %m, \%E%f:%l:\ %m, - \%-C%\tfrom\ %f:%l:in\ %.%#, - \%-Z%\tfrom\ %f:%l, + \%-C%\t%\\d%#:%#\ %#from\ %f:%l:in\ %.%#, + \%-Z%\t%\\d%#:%#\ %#from\ %f:%l, \%-Z%p^, \%-G%.%# diff --git a/runtime/compiler/rake.vim b/runtime/compiler/rake.vim index 8490f2a9e9..3d11a31f89 100644 --- a/runtime/compiler/rake.vim +++ b/runtime/compiler/rake.vim @@ -3,6 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2018 Mar 02 if exists("current_compiler") finish @@ -20,12 +21,12 @@ CompilerSet makeprg=rake CompilerSet errorformat= \%D(in\ %f), - \%\\s%#from\ %f:%l:%m, - \%\\s%#from\ %f:%l:, - \%\\s%##\ %f:%l:%m, - \%\\s%##\ %f:%l, - \%\\s%#[%f:%l:\ %#%m, - \%\\s%#%f:%l:\ %#%m, + \%\\s%#%\\d%#:%#\ %#from\ %f:%l:%m, + \%\\s%#%\\d%#:%#\ %#from\ %f:%l:, + \%\\s%##\ %f:%l:%m%\\&%.%#%\\D:%\\d%\\+:%.%#, + \%\\s%##\ %f:%l%\\&%.%#%\\D:%\\d%\\+, + \%\\s%#[%f:%l:\ %#%m%\\&%.%#%\\D:%\\d%\\+:%.%#, + \%\\s%#%f:%l:\ %#%m%\\&%.%#%\\D:%\\d%\\+:%.%#, \%\\s%#%f:%l:, \%m\ [%f:%l]:, \%+Erake\ aborted!, diff --git a/runtime/compiler/rspec.vim b/runtime/compiler/rspec.vim index c77bd70da7..0cfce04572 100644 --- a/runtime/compiler/rspec.vim +++ b/runtime/compiler/rspec.vim @@ -3,6 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2018 Aug 07 if exists("current_compiler") finish @@ -23,7 +24,8 @@ CompilerSet errorformat= \%E%.%#:in\ `load':\ %f:%l:%m, \%E%f:%l:in\ `%*[^']':\ %m, \%-Z\ \ \ \ \ %\\+\#\ %f:%l:%.%#, - \%E\ \ %\\d%\\+)%.%#, + \%E\ \ \ \ \ Failure/Error:\ %m, + \%E\ \ \ \ \ Failure/Error:, \%C\ \ \ \ \ %m, \%C%\\s%#, \%-G%.%# diff --git a/runtime/compiler/ruby.vim b/runtime/compiler/ruby.vim index dcf7a40129..82d4d1c876 100644 --- a/runtime/compiler/ruby.vim +++ b/runtime/compiler/ruby.vim @@ -4,7 +4,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" ---------------------------------------------------------------------------- +" Last Change: 2019 Jan 06 if exists("current_compiler") finish @@ -21,21 +21,21 @@ set cpo-=C " default settings runs script normally " add '-c' switch to run syntax check only: " -" CompilerSet makeprg=ruby\ -wc\ $* +" CompilerSet makeprg=ruby\ -c " " or add '-c' at :make command line: " " :make -c %<CR> " -CompilerSet makeprg=ruby\ -w\ $* +CompilerSet makeprg=ruby CompilerSet errorformat= \%+E%f:%l:\ parse\ error, \%W%f:%l:\ warning:\ %m, \%E%f:%l:in\ %*[^:]:\ %m, \%E%f:%l:\ %m, - \%-C%\tfrom\ %f:%l:in\ %.%#, - \%-Z%\tfrom\ %f:%l, + \%-C%\t%\\d%#:%#\ %#from\ %f:%l:in\ %.%#, + \%-Z%\t%\\d%#:%#\ %#from\ %f:%l, \%-Z%p^, \%-G%.%# diff --git a/runtime/compiler/rubyunit.vim b/runtime/compiler/rubyunit.vim index ed0639b581..48e8fa41ab 100644 --- a/runtime/compiler/rubyunit.vim +++ b/runtime/compiler/rubyunit.vim @@ -3,6 +3,7 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2014 Mar 23 if exists("current_compiler") finish diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index a4e42d61be..859b31a170 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1043,8 +1043,10 @@ TermResponse After the response to t_RV is received from anything else that takes time is involved). *TextChanged* TextChanged After a change was made to the text in the - current buffer in Normal mode. That is when - |b:changedtick| has changed. + current buffer in Normal mode. That is after + |b:changedtick| has changed (also when that + happened before the TextChanged autocommand + was defined). Not triggered when there is typeahead or when an operator is pending. Careful: This is triggered very often, don't diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 5ec649cf40..c73bd40cb6 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -69,7 +69,7 @@ To force conversion from String to Number, add zero to it: > To avoid a leading zero to cause octal conversion, or for using a different base, use |str2nr()|. - *TRUE* *FALSE* + *TRUE* *FALSE* *Boolean* For boolean operators Numbers are used. Zero is FALSE, non-zero is TRUE. You can also use |v:false| and |v:true|. When TRUE is returned from a function it is the Number one, FALSE is the number zero. @@ -92,7 +92,8 @@ Note that " " and "0" are also non-empty strings, thus considered to be TRUE. A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE. *E745* *E728* *E703* *E729* *E730* *E731* -List, Dictionary and Funcref types are not automatically converted. +|List|, |Dictionary|, |Funcref|, and |Blob| types are not automatically +converted. *E805* *E806* *E808* When mixing Number and Float the Number is converted to Float. Otherwise @@ -5419,6 +5420,7 @@ len({expr}) The result is a Number, which is the length of the argument. used, as with |strlen()|. When {expr} is a |List| the number of items in the |List| is returned. + When {expr} is a |Blob| the number of bytes is returned. When {expr} is a |Dictionary| the number of entries in the |Dictionary| is returned. Otherwise an error is given. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 765f8ce3a1..03523042c6 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1429,8 +1429,9 @@ tag command action ~ |:recover| :rec[over] recover a file from a swap file |:redo| :red[o] redo one undone change |:redir| :redi[r] redirect messages to a file or register -|:redraw| :redr[aw] force a redraw of the display -|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) +|:redraw| :redr[aw] force a redraw of the display +|:redrawstatus| :redraws[tatus] force a redraw of the status line(s) +|:redrawtabline| :redrawt[abline] force a redraw of the tabline |:registers| :reg[isters] display the contents of registers |:resize| :res[ize] change current window height |:retab| :ret[ab] change tab size diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index cb396eea38..f27edef412 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1661,7 +1661,10 @@ A jump table for the options with a short description can be found at |Q_op|. deleted only once. Also when repeating "R" with "." and a count. *cpo-y* - y A yank command can be redone with ".". + y A yank command can be redone with ".". Think twice if + you really want to use this, it may break some + plugins, since most people expect "." to only repeat a + change. *cpo-Z* Z When using "w!" while the 'readonly' option is set, don't reset 'readonly'. diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 3e90a5cfd0..ca836fea67 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -97,14 +97,14 @@ processing a quickfix or location list command, it will be aborted. :[count]lne[xt][!] Same as ":cnext", except the location list for the current window is used instead of the quickfix list. -:[count]cN[ext][!] *:cp* *:cprevious* *:cN* *:cNext* +:[count]cN[ext][!] *:cp* *:cprevious* *:cprev* *:cN* *:cNext* :[count]cp[revious][!] Display the [count] previous error in the list that includes a file name. If there are no file names at all, go to the [count] previous error. See |:cc| for [!] and 'switchbuf'. -:[count]lN[ext][!] *:lp* *:lprevious* *:lN* *:lNext* +:[count]lN[ext][!] *:lp* *:lprevious* *:lprev* *:lN* *:lNext* :[count]lp[revious][!] Same as ":cNext" and ":cprevious", except the location list for the current window is used instead of the quickfix list. @@ -355,6 +355,23 @@ modify the title of a quickfix and location list respectively. Examples: > call setloclist(3, [], 'a', {'title' : 'Cmd output'}) echo getloclist(3, {'title' : 1}) < + *quickfix-index* +When you jump to a quickfix/location list entry using any of the quickfix +commands (e.g. |:cc|, |:cnext|, |:cprev|, etc.), that entry becomes the +currently selected entry. The index of the currently selected entry in a +quickfix/location list can be obtained using the getqflist()/getloclist() +functions. Examples: > + echo getqflist({'idx' : 0}).idx + echo getqflist({'id' : qfid, 'idx' : 0}).idx + echo getloclist(2, {'idx' : 0}).idx +< +For a new quickfix list, the first entry is selected and the index is 1. Any +entry in any quickfix/location list can be set as the currently selected entry +using the setqflist() function. Examples: > + call setqflist([], 'a', {'idx' : 12}) + call setqflist([], 'a', {'id' : qfid, 'idx' : 7}) + call setloclist(1, [], 'a', {'idx' : 7}) +< *quickfix-size* You can get the number of entries (size) in a quickfix and a location list using the |getqflist()| and |getloclist()| functions respectively. Examples: > diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index bbf877bf04..4e0d91dae0 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -37,6 +37,7 @@ There are two steps in using signs: displayed. A defined sign can be placed several times in different lines and files. + *sign-column* When signs are defined for a file, Vim will automatically add a column of two characters to display them in. When the last sign is unplaced the column disappears again. This behavior can be changed with the 'signcolumn' option. @@ -49,7 +50,7 @@ Example to set the color: > *sign-identifier* Each placed sign is identified by a number called the sign identifier. This identifier is used to jump to the sign or to remove the sign. The identifier -is assigned when placing the sign using the |sign-place| command or the +is assigned when placing the sign using the |:sign-place| command or the |sign_place()| function. Each sign identifier should be a unique number. If multiple placed signs use the same identifier, then jumping to or removing a sign becomes unpredictable. To avoid overlapping identifiers, sign groups can @@ -70,6 +71,10 @@ on the same line, the attributes of the sign with the highest priority is used independent of the sign group. The default priority for a sign is 10. The priority is assigned at the time of placing a sign. +When the line on which the sign is placed is deleted, the sign is moved to the +next line (or the last line of the buffer, if there is no next line). When +the delete is undone the sign does not move back. + ============================================================================== 2. Commands *sign-commands* *:sig* *:sign* diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 875f3f2c08..7ea6021d5a 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -558,7 +558,7 @@ When the Myspell files are updated you can merge the differences: nvim -d xx_YY.orig.dic xx_YY.new.dic 3. Take over the changes you like in xx_YY.dic. You may also need to change xx_YY.aff. -4. Rename xx_YY.new.dic to xx_YY.orig.dic and xx_YY.new.aff to xx_YY.new.aff. +4. Rename xx_YY.new.dic to xx_YY.orig.dic and xx_YY.new.aff to xx_YY.orig.aff. SPELL FILE VERSIONS *E770* *E771* *E772* diff --git a/runtime/ftplugin/bash.vim b/runtime/ftplugin/bash.vim new file mode 100644 index 0000000000..a3d01fc2ad --- /dev/null +++ b/runtime/ftplugin/bash.vim @@ -0,0 +1,31 @@ +" Vim filetype plugin file +" Language: bash +" Maintainer: Bram Moolenaar +" Last Changed: 2019 Jan 12 +" +" This is not a real filetype plugin. It allows for someone to set 'filetype' +" to "bash" in the modeline, and gets the effect of filetype "sh" with +" b:is_bash set. Idea from Mahmode Al-Qudsi. + +if exists("b:did_ftplugin") + finish +endif + +let b:is_bash = 1 +if exists("b:is_sh") + unlet b:is_sh +endif +if exists("b:is_kornshell") + unlet b:is_kornshell +endif + +" Setting 'filetype' here directly won't work, since we are being invoked +" through an autocommand. Do it later, on the BufWinEnter event. +augroup bash_filetype + au BufWinEnter * call SetBashFt() +augroup END + +func SetBashFt() + au! bash_filetype + set ft=sh +endfunc diff --git a/runtime/ftplugin/eruby.vim b/runtime/ftplugin/eruby.vim index 32f3fb868f..3c18bada78 100644 --- a/runtime/ftplugin/eruby.vim +++ b/runtime/ftplugin/eruby.vim @@ -3,6 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -27,7 +28,7 @@ elseif !exists("b:eruby_subtype") let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$") let b:eruby_subtype = matchstr(s:lines,'eruby_subtype=\zs\w\+') if b:eruby_subtype == '' - let b:eruby_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.erb\|\.eruby\|\.erubis\)\+$','',''),'\.\zs\w\+\%(\ze+\w\+\)\=$') + let b:eruby_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.erb\|\.eruby\|\.erubis\|\.example\)\+$','',''),'\.\zs\w\+\%(\ze+\w\+\)\=$') endif if b:eruby_subtype == 'rhtml' let b:eruby_subtype = 'html' @@ -45,7 +46,7 @@ elseif !exists("b:eruby_subtype") endif endif -if exists("b:eruby_subtype") && b:eruby_subtype != '' +if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=? 'eruby' exe "runtime! ftplugin/".b:eruby_subtype.".vim ftplugin/".b:eruby_subtype."_*.vim ftplugin/".b:eruby_subtype."/*.vim" else runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim @@ -66,6 +67,21 @@ if exists("b:match_words") unlet b:match_words endif +let s:cfilemap = v:version >= 704 ? maparg('<Plug><cfile>', 'c', 0, 1) : {} +if !get(s:cfilemap, 'buffer') || !s:cfilemap.expr || s:cfilemap.rhs =~# 'ErubyAtCursor()' + let s:cfilemap = {} +endif +if !has_key(s:cfilemap, 'rhs') + let s:cfilemap.rhs = "substitute(&l:inex =~# '\\<v:fname\\>' && len(expand('<cfile>')) ? eval(substitute(&l:inex, '\\<v:fname\\>', '\\=string(expand(\"<cfile>\"))', 'g')) : '', '^$', \"\\022\\006\",'')" +endif +let s:ctagmap = v:version >= 704 ? maparg('<Plug><ctag>', 'c', 0, 1) : {} +if !get(s:ctagmap, 'buffer') || !s:ctagmap.expr || s:ctagmap.rhs =~# 'ErubyAtCursor()' + let s:ctagmap = {} +endif +let s:include = &l:include +let s:path = &l:path +let s:suffixesadd = &l:suffixesadd + runtime! ftplugin/ruby.vim ftplugin/ruby_*.vim ftplugin/ruby/*.vim let b:did_ftplugin = 1 @@ -80,6 +96,15 @@ if exists("b:match_words") let s:match_words = b:match_words . ',' . s:match_words endif +if len(s:include) + let &l:include = s:include +endif +let &l:path = s:path . (s:path =~# ',$\|^$' ? '' : ',') . &l:path +let &l:suffixesadd = s:suffixesadd . (s:suffixesadd =~# ',$\|^$' ? '' : ',') . &l:suffixesadd +exe 'cmap <buffer><script><expr> <Plug><cfile> ErubyAtCursor() ? ' . maparg('<Plug><cfile>', 'c') . ' : ' . s:cfilemap.rhs +exe 'cmap <buffer><script><expr> <Plug><ctag> ErubyAtCursor() ? ' . maparg('<Plug><ctag>', 'c') . ' : ' . get(s:ctagmap, 'rhs', '"\022\027"') +unlet s:cfilemap s:ctagmap s:include s:path s:suffixesadd + " Change the browse dialog on Win32 to show mainly eRuby-related files if has("gui_win32") let b:browsefilter="eRuby Files (*.erb, *.rhtml)\t*.erb;*.rhtml\n" . s:browsefilter @@ -99,4 +124,9 @@ let b:undo_ftplugin = "setl cms< " let &cpo = s:save_cpo unlet s:save_cpo +function! ErubyAtCursor() abort + let groups = map(['erubyBlock', 'erubyComment', 'erubyExpression', 'erubyOneLiner'], 'hlID(v:val)') + return !empty(filter(synstack(line('.'), col('.')), 'index(groups, v:val) >= 0')) +endfunction + " vim: nowrap sw=2 sts=2 ts=8: diff --git a/runtime/ftplugin/ruby.vim b/runtime/ftplugin/ruby.vim index 84fb9930a4..054c35e5fa 100644 --- a/runtime/ftplugin/ruby.vim +++ b/runtime/ftplugin/ruby.vim @@ -2,8 +2,8 @@ " Language: Ruby " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby -" Release Coordinator: Doug Kearns <dougkearns@gmail.com> -" ---------------------------------------------------------------------------- +" Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 if (exists("b:did_ftplugin")) finish @@ -44,19 +44,12 @@ endif setlocal formatoptions-=t formatoptions+=croql setlocal include=^\\s*\\<\\(load\\>\\\|require\\>\\\|autoload\\s*:\\=[\"']\\=\\h\\w*[\"']\\=,\\) -setlocal includeexpr=substitute(substitute(v:fname,'::','/','g'),'\%(\.rb\)\=$','.rb','') setlocal suffixesadd=.rb if exists("&ofu") && has("ruby") setlocal omnifunc=rubycomplete#Complete endif -" To activate, :set ballooneval -if has('balloon_eval') && exists('+balloonexpr') - setlocal balloonexpr=RubyBalloonexpr() -endif - - " TODO: "setlocal define=^\\s*def @@ -69,7 +62,7 @@ endif function! s:query_path(root) abort let code = "print $:.join %q{,}" - if &shell =~# 'sh' + if &shell =~# 'sh' && empty(&shellxquote) let prefix = 'env PATH='.shellescape($PATH).' ' else let prefix = '' @@ -141,44 +134,54 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") \ "All Files (*.*)\t*.*\n" endif -let b:undo_ftplugin = "setl fo< inc< inex< sua< def< com< cms< path< tags< kp<" +let b:undo_ftplugin = "setl inc= sua= path= tags= fo< com< cms< kp=" \."| unlet! b:browsefilter b:match_ignorecase b:match_words b:match_skip" \."| if exists('&ofu') && has('ruby') | setl ofu< | endif" - \."| if has('balloon_eval') && exists('+bexpr') | setl bexpr< | endif" + +if get(g:, 'ruby_recommended_style', 1) + setlocal shiftwidth=2 softtabstop=2 expandtab + let b:undo_ftplugin .= ' | setl sw< sts< et<' +endif + +" To activate, :set ballooneval +if exists('+balloonexpr') && get(g:, 'ruby_balloonexpr') + setlocal balloonexpr=RubyBalloonexpr() + let b:undo_ftplugin .= "| setl bexpr=" +endif function! s:map(mode, flags, map) abort let from = matchstr(a:map, '\S\+') if empty(mapcheck(from, a:mode)) - exe a:mode.'map' '<buffer>'.(a:0 ? a:1 : '') a:map + exe a:mode.'map' '<buffer>' a:map let b:undo_ftplugin .= '|sil! '.a:mode.'unmap <buffer> '.from endif endfunction -cmap <buffer><script><expr> <Plug><cword> substitute(RubyCursorIdentifier(),'^$',"\022\027",'') +cmap <buffer><script><expr> <Plug><ctag> substitute(RubyCursorTag(),'^$',"\022\027",'') cmap <buffer><script><expr> <Plug><cfile> substitute(RubyCursorFile(),'^$',"\022\006",'') -let b:undo_ftplugin .= "| sil! cunmap <buffer> <Plug><cword>| sil! cunmap <buffer> <Plug><cfile>" +let b:undo_ftplugin .= "| sil! cunmap <buffer> <Plug><ctag>| sil! cunmap <buffer> <Plug><cfile>" if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps") nmap <buffer><script> <SID>: :<C-U> nmap <buffer><script> <SID>c: :<C-U><C-R>=v:count ? v:count : ''<CR> - nnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>','rubyDefine','b','n')<CR> - nnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>','rubyDefine','','n')<CR> - nnoremap <silent> <buffer> [M :<C-U>call <SID>searchsyn('\<end\>','rubyDefine','b','n')<CR> - nnoremap <silent> <buffer> ]M :<C-U>call <SID>searchsyn('\<end\>','rubyDefine','','n')<CR> - xnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>','rubyDefine','b','v')<CR> - xnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>','rubyDefine','','v')<CR> - xnoremap <silent> <buffer> [M :<C-U>call <SID>searchsyn('\<end\>','rubyDefine','b','v')<CR> - xnoremap <silent> <buffer> ]M :<C-U>call <SID>searchsyn('\<end\>','rubyDefine','','v')<CR> - - nnoremap <silent> <buffer> [[ :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','b','n')<CR> - nnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','','n')<CR> - nnoremap <silent> <buffer> [] :<C-U>call <SID>searchsyn('\<end\>','rubyModule\<Bar>rubyClass','b','n')<CR> - nnoremap <silent> <buffer> ][ :<C-U>call <SID>searchsyn('\<end\>','rubyModule\<Bar>rubyClass','','n')<CR> - xnoremap <silent> <buffer> [[ :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','b','v')<CR> - xnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>','rubyModule\<Bar>rubyClass','','v')<CR> - xnoremap <silent> <buffer> [] :<C-U>call <SID>searchsyn('\<end\>','rubyModule\<Bar>rubyClass','b','v')<CR> - xnoremap <silent> <buffer> ][ :<C-U>call <SID>searchsyn('\<end\>','rubyModule\<Bar>rubyClass','','v')<CR> + nnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'b','n')<CR> + nnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'','n')<CR> + nnoremap <silent> <buffer> [M :<C-U>call <SID>searchsyn('\<end\>',['rubyDefine'],'b','n')<CR> + nnoremap <silent> <buffer> ]M :<C-U>call <SID>searchsyn('\<end\>',['rubyDefine'],'','n')<CR> + xnoremap <silent> <buffer> [m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'b','v')<CR> + xnoremap <silent> <buffer> ]m :<C-U>call <SID>searchsyn('\<def\>',['rubyDefine'],'','v')<CR> + xnoremap <silent> <buffer> [M :<C-U>call <SID>searchsyn('\<end\>',['rubyDefine'],'b','v')<CR> + xnoremap <silent> <buffer> ]M :<C-U>call <SID>searchsyn('\<end\>',['rubyDefine'],'','v')<CR> + + nnoremap <silent> <buffer> [[ :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>',['rubyModule','rubyClass'],'b','n')<CR> + nnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>',['rubyModule','rubyClass'],'','n')<CR> + nnoremap <silent> <buffer> [] :<C-U>call <SID>searchsyn('\<end\>',['rubyModule','rubyClass'],'b','n')<CR> + nnoremap <silent> <buffer> ][ :<C-U>call <SID>searchsyn('\<end\>',['rubyModule','rubyClass'],'','n')<CR> + xnoremap <silent> <buffer> [[ :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>',['rubyModule','rubyClass'],'b','v')<CR> + xnoremap <silent> <buffer> ]] :<C-U>call <SID>searchsyn('\<\%(class\<Bar>module\)\>',['rubyModule','rubyClass'],'','v')<CR> + xnoremap <silent> <buffer> [] :<C-U>call <SID>searchsyn('\<end\>',['rubyModule','rubyClass'],'b','v')<CR> + xnoremap <silent> <buffer> ][ :<C-U>call <SID>searchsyn('\<end\>',['rubyModule','rubyClass'],'','v')<CR> let b:undo_ftplugin = b:undo_ftplugin \."| sil! exe 'unmap <buffer> [[' | sil! exe 'unmap <buffer> ]]' | sil! exe 'unmap <buffer> []' | sil! exe 'unmap <buffer> ]['" @@ -204,19 +207,18 @@ if !exists("g:no_plugin_maps") && !exists("g:no_ruby_maps") \."| sil! exe 'xunmap <buffer> iM' | sil! exe 'xunmap <buffer> aM'" endif - call s:map('c', '', '<C-R><C-W> <Plug><cword>') call s:map('c', '', '<C-R><C-F> <Plug><cfile>') cmap <buffer><script><expr> <SID>tagzv &foldopen =~# 'tag' ? '<Bar>norm! zv' : '' - call s:map('n', '<silent>', '<C-]> <SID>:exe v:count1."tag <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', 'g<C-]> <SID>:exe "tjump <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', 'g] <SID>:exe "tselect <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', '<C-W>] <SID>:exe v:count1."stag <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', '<C-W><C-]> <SID>:exe v:count1."stag <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', '<C-W>g<C-]> <SID>:exe "stjump <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', '<C-W>g] <SID>:exe "stselect <Plug><cword>"<SID>tagzv<CR>') - call s:map('n', '<silent>', '<C-W>} <SID>:exe v:count1."ptag <Plug><cword>"<CR>') - call s:map('n', '<silent>', '<C-W>g} <SID>:exe "ptjump <Plug><cword>"<CR>') + call s:map('n', '<silent>', '<C-]> <SID>:exe v:count1."tag <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', 'g<C-]> <SID>:exe "tjump <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', 'g] <SID>:exe "tselect <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', '<C-W>] <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', '<C-W><C-]> <SID>:exe v:count1."stag <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', '<C-W>g<C-]> <SID>:exe "stjump <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', '<C-W>g] <SID>:exe "stselect <Plug><ctag>"<SID>tagzv<CR>') + call s:map('n', '<silent>', '<C-W>} <SID>:exe v:count1."ptag <Plug><ctag>"<CR>') + call s:map('n', '<silent>', '<C-W>g} <SID>:exe "ptjump <Plug><ctag>"<CR>') call s:map('n', '<silent>', 'gf <SID>c:find <Plug><cfile><CR>') call s:map('n', '<silent>', '<C-W>f <SID>c:sfind <Plug><cfile><CR>') @@ -288,12 +290,13 @@ function! s:searchsyn(pattern, syn, flags, mode) abort norm! gv endif let i = 0 + call map(a:syn, 'hlID(v:val)') while i < cnt let i = i + 1 let line = line('.') let col = col('.') let pos = search(a:pattern,'W'.a:flags) - while pos != 0 && s:synname() !~# a:syn + while pos != 0 && index(a:syn, s:synid()) < 0 let pos = search(a:pattern,'W'.a:flags) endwhile if pos == 0 @@ -303,8 +306,8 @@ function! s:searchsyn(pattern, syn, flags, mode) abort endwhile endfunction -function! s:synname() abort - return synIDattr(synID(line('.'),col('.'),0),'name') +function! s:synid() abort + return synID(line('.'),col('.'),0) endfunction function! s:wrap_i(back,forward) abort @@ -349,6 +352,10 @@ function! RubyCursorIdentifier() abort return stripped == '' ? expand("<cword>") : stripped endfunction +function! RubyCursorTag() abort + return substitute(RubyCursorIdentifier(), '^[$@]*', '', '') +endfunction + function! RubyCursorFile() abort let isfname = &isfname try @@ -360,8 +367,9 @@ function! RubyCursorFile() abort let pre = matchstr(strpart(getline('.'), 0, col('.')-1), '.*\f\@<!') let post = matchstr(strpart(getline('.'), col('.')), '\f\@!.*') let ext = getline('.') =~# '^\s*\%(require\%(_relative\)\=\|autoload\)\>' && cfile !~# '\.rb$' ? '.rb' : '' - if s:synname() ==# 'rubyConstant' + if s:synid() ==# hlID('rubyConstant') let cfile = substitute(cfile,'\.\w\+[?!=]\=$','','') + let cfile = substitute(cfile,'^::','','') let cfile = substitute(cfile,'::','/','g') let cfile = substitute(cfile,'\(\u\+\)\(\u\l\)','\1_\2', 'g') let cfile = substitute(cfile,'\(\l\|\d\)\(\u\)','\1_\2', 'g') diff --git a/runtime/ftplugin/text.vim b/runtime/ftplugin/text.vim index ec84ac6f79..c4cbcb54c3 100644 --- a/runtime/ftplugin/text.vim +++ b/runtime/ftplugin/text.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: Text " Maintainer: David Barnett <daviebdawg+vim@gmail.com> -" Last Change: 2014 Jul 09 +" Last Change: 2019 Jan 10 if exists('b:did_ftplugin') finish @@ -12,6 +12,7 @@ let b:undo_ftplugin = 'setlocal comments< commentstring<' " We intentionally don't set formatoptions-=t since text should wrap as text. -" Pseudo comment leaders to indent bulleted lists. -setlocal comments=fb:-,fb:* +" Pseudo comment leaders to indent bulleted lists with '-' and '*'. And allow +" for Mail quoted text with '>'. +setlocal comments=fb:-,fb:*,n:> setlocal commentstring= diff --git a/runtime/indent/eruby.vim b/runtime/indent/eruby.vim index 5058325495..6ff15ab958 100644 --- a/runtime/indent/eruby.vim +++ b/runtime/indent/eruby.vim @@ -3,6 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 if exists("b:did_indent") finish @@ -12,7 +13,7 @@ runtime! indent/ruby.vim unlet! b:did_indent setlocal indentexpr= -if exists("b:eruby_subtype") +if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=# 'eruby' exe "runtime! indent/".b:eruby_subtype.".vim" else runtime! indent/html.vim @@ -47,7 +48,11 @@ set cpo&vim function! GetErubyIndent(...) " The value of a single shift-width - let sw = shiftwidth() + if exists('*shiftwidth') + let sw = shiftwidth() + else + let sw = &sw + endif if a:0 && a:1 == '.' let v:lnum = line('.') @@ -91,6 +96,7 @@ function! GetErubyIndent(...) let ind = ind + sw endif if line !~# '^\s*<%' && line =~# '%>\s*$' && line !~# '^\s*end\>' + \ && synID(v:lnum, match(cline, '\S') + 1, 1) != hlID('htmlEndTag') let ind = ind - sw endif if cline =~# '^\s*[-=]\=%>\s*$' diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim index d8733db305..5c420d7543 100644 --- a/runtime/indent/ruby.vim +++ b/runtime/indent/ruby.vim @@ -1,8 +1,10 @@ " Vim indent file " Language: Ruby -" Maintainer: Nikolai Weibull <now at bitwi.se> +" Maintainer: Andrew Radev <andrey.radev@gmail.com> +" Previous Maintainer: Nikolai Weibull <now at bitwi.se> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 " 0. Initialization {{{1 " ================= @@ -18,6 +20,11 @@ if !exists('g:ruby_indent_access_modifier_style') let g:ruby_indent_access_modifier_style = 'normal' endif +if !exists('g:ruby_indent_assignment_style') + " Possible values: "variable", "hanging" + let g:ruby_indent_assignment_style = 'hanging' +endif + if !exists('g:ruby_indent_block_style') " Possible values: "expression", "do" let g:ruby_indent_block_style = 'expression' @@ -42,28 +49,27 @@ set cpo&vim " 1. Variables {{{1 " ============ -" Regex of syntax group names that are or delimit strings/symbols or are comments. -let s:syng_strcom = '\<ruby\%(Regexp\|RegexpDelimiter\|RegexpEscape' . - \ '\|Symbol\|String\|StringDelimiter\|StringEscape\|ASCIICode' . - \ '\|Interpolation\|InterpolationDelimiter\|NoInterpolation\|Comment\|Documentation\)\>' - -" Regex of syntax group names that are strings. +" Syntax group names that are strings. let s:syng_string = - \ '\<ruby\%(String\|Interpolation\|NoInterpolation\|StringEscape\)\>' + \ ['String', 'Interpolation', 'InterpolationDelimiter', 'NoInterpolation', 'StringEscape'] + +" Syntax group names that are strings or documentation. +let s:syng_stringdoc = s:syng_string + ['Documentation'] -" Regex of syntax group names that are strings or documentation. -let s:syng_stringdoc = - \'\<ruby\%(String\|Interpolation\|NoInterpolation\|StringEscape\|Documentation\)\>' +" Syntax group names that are or delimit strings/symbols/regexes or are comments. +let s:syng_strcom = s:syng_stringdoc + + \ ['Regexp', 'RegexpDelimiter', 'RegexpEscape', + \ 'Symbol', 'StringDelimiter', 'ASCIICode', 'Comment'] " Expression used to check whether we should skip a match with searchpair(). let s:skip_expr = - \ "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" + \ 'index(map('.string(s:syng_strcom).',"hlID(''ruby''.v:val)"), synID(line("."),col("."),1)) >= 0' " Regex used for words that, at the start of a line, add a level of indent. let s:ruby_indent_keywords = \ '^\s*\zs\<\%(module\|class\|if\|for' . \ '\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure\|rescue' . - \ '\|\%(public\|protected\|private\)\=\s*def\):\@!\>' . + \ '\|\%(\K\k*[!?]\?\)\=\s*def\):\@!\>' . \ '\|\%([=,*/%+-]\|<<\|>>\|:\s\)\s*\zs' . \ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>' @@ -77,7 +83,7 @@ let s:ruby_deindent_keywords = let s:end_start_regex = \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' . \ '\<\%(module\|class\|if\|for\|while\|until\|case\|unless\|begin' . - \ '\|\%(public\|protected\|private\)\=\s*def\):\@!\>' . + \ '\|\%(\K\k*[!?]\?\)\=\s*def\):\@!\>' . \ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>' " Regex that defines the middle-match for the 'end' keyword. @@ -142,31 +148,562 @@ let s:block_continuation_regex = '^\s*[^])}\t ].*'.s:block_regex " Regex that describes a leading operator (only a method call's dot for now) let s:leading_operator_regex = '^\s*[.]' -" 2. Auxiliary Functions {{{1 +" 2. GetRubyIndent Function {{{1 +" ========================= + +function! GetRubyIndent(...) abort + " 2.1. Setup {{{2 + " ---------- + + let indent_info = {} + + " The value of a single shift-width + if exists('*shiftwidth') + let indent_info.sw = shiftwidth() + else + let indent_info.sw = &sw + endif + + " For the current line, use the first argument if given, else v:lnum + let indent_info.clnum = a:0 ? a:1 : v:lnum + let indent_info.cline = getline(indent_info.clnum) + + " Set up variables for restoring position in file. Could use clnum here. + let indent_info.col = col('.') + + " 2.2. Work on the current line {{{2 + " ----------------------------- + let indent_callback_names = [ + \ 's:AccessModifier', + \ 's:ClosingBracketOnEmptyLine', + \ 's:BlockComment', + \ 's:DeindentingKeyword', + \ 's:MultilineStringOrLineComment', + \ 's:ClosingHeredocDelimiter', + \ 's:LeadingOperator', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " 2.3. Work on the previous line. {{{2 + " ------------------------------- + + " Special case: we don't need the real s:PrevNonBlankNonString for an empty + " line inside a string. And that call can be quite expensive in that + " particular situation. + let indent_callback_names = [ + \ 's:EmptyInsideString', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " Previous line number + let indent_info.plnum = s:PrevNonBlankNonString(indent_info.clnum - 1) + let indent_info.pline = getline(indent_info.plnum) + + let indent_callback_names = [ + \ 's:StartOfFile', + \ 's:AfterAccessModifier', + \ 's:ContinuedLine', + \ 's:AfterBlockOpening', + \ 's:AfterHangingSplat', + \ 's:AfterUnbalancedBracket', + \ 's:AfterLeadingOperator', + \ 's:AfterEndKeyword', + \ 's:AfterIndentKeyword', + \ ] + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " 2.4. Work on the MSL line. {{{2 + " -------------------------- + let indent_callback_names = [ + \ 's:PreviousNotMSL', + \ 's:IndentingKeywordInMSL', + \ 's:ContinuedHangingOperator', + \ ] + + " Most Significant line based on the previous one -- in case it's a + " contination of something above + let indent_info.plnum_msl = s:GetMSL(indent_info.plnum) + + for callback_name in indent_callback_names +" Decho "Running: ".callback_name + let indent = call(function(callback_name), [indent_info]) + + if indent >= 0 +" Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) + return indent + endif + endfor + + " }}}2 + + " By default, just return the previous line's indent +" Decho "Default case matched" + return indent(indent_info.plnum) +endfunction + +" 3. Indenting Logic Callbacks {{{1 +" ============================ + +function! s:AccessModifier(cline_info) abort + let info = a:cline_info + + " If this line is an access modifier keyword, align according to the closest + " class declaration. + if g:ruby_indent_access_modifier_style == 'indent' + if s:Match(info.clnum, s:access_modifier_regex) + let class_lnum = s:FindContainingClass() + if class_lnum > 0 + return indent(class_lnum) + info.sw + endif + endif + elseif g:ruby_indent_access_modifier_style == 'outdent' + if s:Match(info.clnum, s:access_modifier_regex) + let class_lnum = s:FindContainingClass() + if class_lnum > 0 + return indent(class_lnum) + endif + endif + endif + + return -1 +endfunction + +function! s:ClosingBracketOnEmptyLine(cline_info) abort + let info = a:cline_info + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + let col = matchend(info.cline, '^\s*[]})]') + + if col > 0 && !s:IsInStringOrComment(info.clnum, col) + call cursor(info.clnum, col) + let closing_bracket = info.cline[col - 1] + let bracket_pair = strpart('(){}[]', stridx(')}]', closing_bracket) * 2, 2) + + if searchpair(escape(bracket_pair[0], '\['), '', bracket_pair[1], 'bW', s:skip_expr) > 0 + if closing_bracket == ')' && col('.') != col('$') - 1 + let ind = virtcol('.') - 1 + elseif g:ruby_indent_block_style == 'do' + let ind = indent(line('.')) + else " g:ruby_indent_block_style == 'expression' + let ind = indent(s:GetMSL(line('.'))) + endif + endif + + return ind + endif + + return -1 +endfunction + +function! s:BlockComment(cline_info) abort + " If we have a =begin or =end set indent to first column. + if match(a:cline_info.cline, '^\s*\%(=begin\|=end\)$') != -1 + return 0 + endif + return -1 +endfunction + +function! s:DeindentingKeyword(cline_info) abort + let info = a:cline_info + + " If we have a deindenting keyword, find its match and indent to its level. + " TODO: this is messy + if s:Match(info.clnum, s:ruby_deindent_keywords) + call cursor(info.clnum, 1) + + if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let msl = s:GetMSL(line('.')) + let line = getline(line('.')) + + if s:IsAssignment(line, col('.')) && + \ strpart(line, col('.') - 1, 2) !~ 'do' + " assignment to case/begin/etc, on the same line + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = virtcol('.') - 1 + else + " align with variable + let ind = indent(line('.')) + endif + elseif g:ruby_indent_block_style == 'do' + " align to line of the "do", not to the MSL + let ind = indent(line('.')) + elseif getline(msl) =~ '=\s*\(#.*\)\=$' + " in the case of assignment to the MSL, align to the starting line, + " not to the MSL + let ind = indent(line('.')) + else + " align to the MSL + let ind = indent(msl) + endif + endif + return ind + endif + + return -1 +endfunction + +function! s:MultilineStringOrLineComment(cline_info) abort + let info = a:cline_info + + " If we are in a multi-line string or line-comment, don't do anything to it. + if s:IsInStringOrDocumentation(info.clnum, matchend(info.cline, '^\s*') + 1) + return indent(info.clnum) + endif + return -1 +endfunction + +function! s:ClosingHeredocDelimiter(cline_info) abort + let info = a:cline_info + + " If we are at the closing delimiter of a "<<" heredoc-style string, set the + " indent to 0. + if info.cline =~ '^\k\+\s*$' + \ && s:IsInStringDelimiter(info.clnum, 1) + \ && search('\V<<'.info.cline, 'nbW') > 0 + return 0 + endif + + return -1 +endfunction + +function! s:LeadingOperator(cline_info) abort + " If the current line starts with a leading operator, add a level of indent. + if s:Match(a:cline_info.clnum, s:leading_operator_regex) + return indent(s:GetMSL(a:cline_info.clnum)) + a:cline_info.sw + endif + return -1 +endfunction + +function! s:EmptyInsideString(pline_info) abort + " If the line is empty and inside a string (the previous line is a string, + " too), use the previous line's indent + let info = a:pline_info + + let plnum = prevnonblank(info.clnum - 1) + let pline = getline(plnum) + + if info.cline =~ '^\s*$' + \ && s:IsInStringOrComment(plnum, 1) + \ && s:IsInStringOrComment(plnum, strlen(pline)) + return indent(plnum) + endif + return -1 +endfunction + +function! s:StartOfFile(pline_info) abort + " At the start of the file use zero indent. + if a:pline_info.plnum == 0 + return 0 + endif + return -1 +endfunction + +function! s:AfterAccessModifier(pline_info) abort + let info = a:pline_info + + if g:ruby_indent_access_modifier_style == 'indent' + " If the previous line was a private/protected keyword, add a + " level of indent. + if s:Match(info.plnum, s:indent_access_modifier_regex) + return indent(info.plnum) + info.sw + endif + elseif g:ruby_indent_access_modifier_style == 'outdent' + " If the previous line was a private/protected/public keyword, add + " a level of indent, since the keyword has been out-dented. + if s:Match(info.plnum, s:access_modifier_regex) + return indent(info.plnum) + info.sw + endif + endif + return -1 +endfunction + +" Example: +" +" if foo || bar || +" baz || bing +" puts "foo" +" end +" +function! s:ContinuedLine(pline_info) abort + let info = a:pline_info + + let col = s:Match(info.plnum, s:ruby_indent_keywords) + if s:Match(info.plnum, s:continuable_regex) && + \ s:Match(info.plnum, s:continuation_regex) + if col > 0 && s:IsAssignment(info.pline, col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col - 1 + else + " align with variable + let ind = indent(info.plnum) + endif + else + let ind = indent(s:GetMSL(info.plnum)) + endif + return ind + info.sw + info.sw + endif + return -1 +endfunction + +function! s:AfterBlockOpening(pline_info) abort + let info = a:pline_info + + " If the previous line ended with a block opening, add a level of indent. + if s:Match(info.plnum, s:block_regex) + if g:ruby_indent_block_style == 'do' + " don't align to the msl, align to the "do" + let ind = indent(info.plnum) + info.sw + else + let plnum_msl = s:GetMSL(info.plnum) + + if getline(plnum_msl) =~ '=\s*\(#.*\)\=$' + " in the case of assignment to the msl, align to the starting line, + " not to the msl + let ind = indent(info.plnum) + info.sw + else + let ind = indent(plnum_msl) + info.sw + endif + endif + + return ind + endif + + return -1 +endfunction + +function! s:AfterLeadingOperator(pline_info) abort + " If the previous line started with a leading operator, use its MSL's level + " of indent + if s:Match(a:pline_info.plnum, s:leading_operator_regex) + return indent(s:GetMSL(a:pline_info.plnum)) + endif + return -1 +endfunction + +function! s:AfterHangingSplat(pline_info) abort + let info = a:pline_info + + " If the previous line ended with the "*" of a splat, add a level of indent + if info.pline =~ s:splat_regex + return indent(info.plnum) + info.sw + endif + return -1 +endfunction + +function! s:AfterUnbalancedBracket(pline_info) abort + let info = a:pline_info + + " If the previous line contained unclosed opening brackets and we are still + " in them, find the rightmost one and add indent depending on the bracket + " type. + " + " If it contained hanging closing brackets, find the rightmost one, find its + " match and indent according to that. + if info.pline =~ '[[({]' || info.pline =~ '[])}]\s*\%(#.*\)\=$' + let [opening, closing] = s:ExtraBrackets(info.plnum) + + if opening.pos != -1 + if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 + if col('.') + 1 == col('$') + return indent(info.plnum) + info.sw + else + return virtcol('.') + endif + else + let nonspace = matchend(info.pline, '\S', opening.pos + 1) - 1 + return nonspace > 0 ? nonspace : indent(info.plnum) + info.sw + endif + elseif closing.pos != -1 + call cursor(info.plnum, closing.pos + 1) + normal! % + + if s:Match(line('.'), s:ruby_indent_keywords) + return indent('.') + info.sw + else + return indent(s:GetMSL(line('.'))) + endif + else + call cursor(info.clnum, info.col) + end + endif + + return -1 +endfunction + +function! s:AfterEndKeyword(pline_info) abort + let info = a:pline_info + " If the previous line ended with an "end", match that "end"s beginning's + " indent. + let col = s:Match(info.plnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') + if col > 0 + call cursor(info.plnum, col) + if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', + \ s:end_skip_expr) > 0 + let n = line('.') + let ind = indent('.') + let msl = s:GetMSL(n) + if msl != n + let ind = indent(msl) + end + return ind + endif + end + return -1 +endfunction + +function! s:AfterIndentKeyword(pline_info) abort + let info = a:pline_info + let col = s:Match(info.plnum, s:ruby_indent_keywords) + + if col > 0 + call cursor(info.plnum, col) + let ind = virtcol('.') - 1 + info.sw + " TODO: make this better (we need to count them) (or, if a searchpair + " fails, we know that something is lacking an end and thus we indent a + " level + if s:Match(info.plnum, s:end_end_regex) + let ind = indent('.') + elseif s:IsAssignment(info.pline, col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col + info.sw - 1 + else + " align with variable + let ind = indent(info.plnum) + info.sw + endif + endif + return ind + endif + + return -1 +endfunction + +function! s:PreviousNotMSL(msl_info) abort + let info = a:msl_info + + " If the previous line wasn't a MSL + if info.plnum != info.plnum_msl + " If previous line ends bracket and begins non-bracket continuation decrease indent by 1. + if s:Match(info.plnum, s:bracket_switch_continuation_regex) + " TODO (2016-10-07) Wrong/unused? How could it be "1"? + return indent(info.plnum) - 1 + " If previous line is a continuation return its indent. + " TODO: the || s:IsInString() thing worries me a bit. + elseif s:Match(info.plnum, s:non_bracket_continuation_regex) || s:IsInString(info.plnum, strlen(line)) + return indent(info.plnum) + endif + endif + + return -1 +endfunction + +function! s:IndentingKeywordInMSL(msl_info) abort + let info = a:msl_info + " If the MSL line had an indenting keyword in it, add a level of indent. + " TODO: this does not take into account contrived things such as + " module Foo; class Bar; end + let col = s:Match(info.plnum_msl, s:ruby_indent_keywords) + if col > 0 + let ind = indent(info.plnum_msl) + info.sw + if s:Match(info.plnum_msl, s:end_end_regex) + let ind = ind - info.sw + elseif s:IsAssignment(getline(info.plnum_msl), col) + if g:ruby_indent_assignment_style == 'hanging' + " hanging indent + let ind = col + info.sw - 1 + else + " align with variable + let ind = indent(info.plnum_msl) + info.sw + endif + endif + return ind + endif + return -1 +endfunction + +function! s:ContinuedHangingOperator(msl_info) abort + let info = a:msl_info + + " If the previous line ended with [*+/.,-=], but wasn't a block ending or a + " closing bracket, indent one extra level. + if s:Match(info.plnum_msl, s:non_bracket_continuation_regex) && !s:Match(info.plnum_msl, '^\s*\([\])}]\|end\)') + if info.plnum_msl == info.plnum + let ind = indent(info.plnum_msl) + info.sw + else + let ind = indent(info.plnum_msl) + endif + return ind + endif + + return -1 +endfunction + +" 4. Auxiliary Functions {{{1 " ====================== +function! s:IsInRubyGroup(groups, lnum, col) abort + let ids = map(copy(a:groups), 'hlID("ruby".v:val)') + return index(ids, synID(a:lnum, a:col, 1)) >= 0 +endfunction + " Check if the character at lnum:col is inside a string, comment, or is ascii. -function s:IsInStringOrComment(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom +function! s:IsInStringOrComment(lnum, col) abort + return s:IsInRubyGroup(s:syng_strcom, a:lnum, a:col) endfunction " Check if the character at lnum:col is inside a string. -function s:IsInString(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string +function! s:IsInString(lnum, col) abort + return s:IsInRubyGroup(s:syng_string, a:lnum, a:col) endfunction " Check if the character at lnum:col is inside a string or documentation. -function s:IsInStringOrDocumentation(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_stringdoc +function! s:IsInStringOrDocumentation(lnum, col) abort + return s:IsInRubyGroup(s:syng_stringdoc, a:lnum, a:col) endfunction " Check if the character at lnum:col is inside a string delimiter -function s:IsInStringDelimiter(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'rubyStringDelimiter' +function! s:IsInStringDelimiter(lnum, col) abort + return s:IsInRubyGroup(['StringDelimiter'], a:lnum, a:col) +endfunction + +function! s:IsAssignment(str, pos) abort + return strpart(a:str, 0, a:pos - 1) =~ '=\s*$' endfunction " Find line above 'lnum' that isn't empty, in a comment, or in a string. -function s:PrevNonBlankNonString(lnum) +function! s:PrevNonBlankNonString(lnum) abort let in_block = 0 let lnum = prevnonblank(a:lnum) while lnum > 0 @@ -191,10 +728,9 @@ function s:PrevNonBlankNonString(lnum) endfunction " Find line above 'lnum' that started the continuation 'lnum' may be part of. -function s:GetMSL(lnum) +function! s:GetMSL(lnum) abort " Start on the line we're at and use its indent. let msl = a:lnum - let msl_body = getline(msl) let lnum = s:PrevNonBlankNonString(a:lnum - 1) while lnum > 0 " If we have a continuation line, or we're in a string, use line as MSL. @@ -291,14 +827,13 @@ function s:GetMSL(lnum) endif endif - let msl_body = getline(msl) let lnum = s:PrevNonBlankNonString(lnum - 1) endwhile return msl endfunction " Check if line 'lnum' has more opening brackets than closing ones. -function s:ExtraBrackets(lnum) +function! s:ExtraBrackets(lnum) abort let opening = {'parentheses': [], 'braces': [], 'brackets': []} let closing = {'parentheses': [], 'braces': [], 'brackets': []} @@ -360,7 +895,7 @@ function s:ExtraBrackets(lnum) return [rightmost_opening, rightmost_closing] endfunction -function s:Match(lnum, regex) +function! s:Match(lnum, regex) abort let line = getline(a:lnum) let offset = match(line, '\C'.a:regex) let col = offset + 1 @@ -380,7 +915,7 @@ endfunction " Locates the containing class/module's definition line, ignoring nested classes " along the way. " -function! s:FindContainingClass() +function! s:FindContainingClass() abort let saved_position = getpos('.') while searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', @@ -396,297 +931,6 @@ function! s:FindContainingClass() return 0 endfunction -" 3. GetRubyIndent Function {{{1 -" ========================= - -function GetRubyIndent(...) - " 3.1. Setup {{{2 - " ---------- - - " The value of a single shift-width - let sw = shiftwidth() - - " For the current line, use the first argument if given, else v:lnum - let clnum = a:0 ? a:1 : v:lnum - - " Set up variables for restoring position in file. Could use clnum here. - let vcol = col('.') - - " 3.2. Work on the current line {{{2 - " ----------------------------- - - " Get the current line. - let line = getline(clnum) - let ind = -1 - - " If this line is an access modifier keyword, align according to the closest - " class declaration. - if g:ruby_indent_access_modifier_style == 'indent' - if s:Match(clnum, s:access_modifier_regex) - let class_line = s:FindContainingClass() - if class_line > 0 - return indent(class_line) + sw - endif - endif - elseif g:ruby_indent_access_modifier_style == 'outdent' - if s:Match(clnum, s:access_modifier_regex) - let class_line = s:FindContainingClass() - if class_line > 0 - return indent(class_line) - endif - endif - endif - - " If we got a closing bracket on an empty line, find its match and indent - " according to it. For parentheses we indent to its column - 1, for the - " others we indent to the containing line's MSL's level. Return -1 if fail. - let col = matchend(line, '^\s*[]})]') - if col > 0 && !s:IsInStringOrComment(clnum, col) - call cursor(clnum, col) - let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) - if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 - if line[col-1]==')' && col('.') != col('$') - 1 - let ind = virtcol('.') - 1 - elseif g:ruby_indent_block_style == 'do' - let ind = indent(line('.')) - else " g:ruby_indent_block_style == 'expression' - let ind = indent(s:GetMSL(line('.'))) - endif - endif - return ind - endif - - " If we have a =begin or =end set indent to first column. - if match(line, '^\s*\%(=begin\|=end\)$') != -1 - return 0 - endif - - " If we have a deindenting keyword, find its match and indent to its level. - " TODO: this is messy - if s:Match(clnum, s:ruby_deindent_keywords) - call cursor(clnum, 1) - if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', - \ s:end_skip_expr) > 0 - let msl = s:GetMSL(line('.')) - let line = getline(line('.')) - - if strpart(line, 0, col('.') - 1) =~ '=\s*$' && - \ strpart(line, col('.') - 1, 2) !~ 'do' - " assignment to case/begin/etc, on the same line, hanging indent - let ind = virtcol('.') - 1 - elseif g:ruby_indent_block_style == 'do' - " align to line of the "do", not to the MSL - let ind = indent(line('.')) - elseif getline(msl) =~ '=\s*\(#.*\)\=$' - " in the case of assignment to the MSL, align to the starting line, - " not to the MSL - let ind = indent(line('.')) - else - " align to the MSL - let ind = indent(msl) - endif - endif - return ind - endif - - " If we are in a multi-line string or line-comment, don't do anything to it. - if s:IsInStringOrDocumentation(clnum, matchend(line, '^\s*') + 1) - return indent('.') - endif - - " If we are at the closing delimiter of a "<<" heredoc-style string, set the - " indent to 0. - if line =~ '^\k\+\s*$' - \ && s:IsInStringDelimiter(clnum, 1) - \ && search('\V<<'.line, 'nbW') > 0 - return 0 - endif - - " If the current line starts with a leading operator, add a level of indent. - if s:Match(clnum, s:leading_operator_regex) - return indent(s:GetMSL(clnum)) + sw - endif - - " 3.3. Work on the previous line. {{{2 - " ------------------------------- - - " Find a non-blank, non-multi-line string line above the current line. - let lnum = s:PrevNonBlankNonString(clnum - 1) - - " If the line is empty and inside a string, use the previous line. - if line =~ '^\s*$' && lnum != prevnonblank(clnum - 1) - return indent(prevnonblank(clnum)) - endif - - " At the start of the file use zero indent. - if lnum == 0 - return 0 - endif - - " Set up variables for the previous line. - let line = getline(lnum) - let ind = indent(lnum) - - if g:ruby_indent_access_modifier_style == 'indent' - " If the previous line was a private/protected keyword, add a - " level of indent. - if s:Match(lnum, s:indent_access_modifier_regex) - return indent(lnum) + sw - endif - elseif g:ruby_indent_access_modifier_style == 'outdent' - " If the previous line was a private/protected/public keyword, add - " a level of indent, since the keyword has been out-dented. - if s:Match(lnum, s:access_modifier_regex) - return indent(lnum) + sw - endif - endif - - if s:Match(lnum, s:continuable_regex) && s:Match(lnum, s:continuation_regex) - return indent(s:GetMSL(lnum)) + sw + sw - endif - - " If the previous line ended with a block opening, add a level of indent. - if s:Match(lnum, s:block_regex) - let msl = s:GetMSL(lnum) - - if g:ruby_indent_block_style == 'do' - " don't align to the msl, align to the "do" - let ind = indent(lnum) + sw - elseif getline(msl) =~ '=\s*\(#.*\)\=$' - " in the case of assignment to the msl, align to the starting line, - " not to the msl - let ind = indent(lnum) + sw - else - let ind = indent(msl) + sw - endif - return ind - endif - - " If the previous line started with a leading operator, use its MSL's level - " of indent - if s:Match(lnum, s:leading_operator_regex) - return indent(s:GetMSL(lnum)) - endif - - " If the previous line ended with the "*" of a splat, add a level of indent - if line =~ s:splat_regex - return indent(lnum) + sw - endif - - " If the previous line contained unclosed opening brackets and we are still - " in them, find the rightmost one and add indent depending on the bracket - " type. - " - " If it contained hanging closing brackets, find the rightmost one, find its - " match and indent according to that. - if line =~ '[[({]' || line =~ '[])}]\s*\%(#.*\)\=$' - let [opening, closing] = s:ExtraBrackets(lnum) - - if opening.pos != -1 - if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 - if col('.') + 1 == col('$') - return ind + sw - else - return virtcol('.') - endif - else - let nonspace = matchend(line, '\S', opening.pos + 1) - 1 - return nonspace > 0 ? nonspace : ind + sw - endif - elseif closing.pos != -1 - call cursor(lnum, closing.pos + 1) - normal! % - - if s:Match(line('.'), s:ruby_indent_keywords) - return indent('.') + sw - else - return indent(s:GetMSL(line('.'))) - endif - else - call cursor(clnum, vcol) - end - endif - - " If the previous line ended with an "end", match that "end"s beginning's - " indent. - let col = s:Match(lnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') - if col > 0 - call cursor(lnum, col) - if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', - \ s:end_skip_expr) > 0 - let n = line('.') - let ind = indent('.') - let msl = s:GetMSL(n) - if msl != n - let ind = indent(msl) - end - return ind - endif - end - - let col = s:Match(lnum, s:ruby_indent_keywords) - if col > 0 - call cursor(lnum, col) - let ind = virtcol('.') - 1 + sw - " TODO: make this better (we need to count them) (or, if a searchpair - " fails, we know that something is lacking an end and thus we indent a - " level - if s:Match(lnum, s:end_end_regex) - let ind = indent('.') - endif - return ind - endif - - " 3.4. Work on the MSL line. {{{2 - " -------------------------- - - " Set up variables to use and search for MSL to the previous line. - let p_lnum = lnum - let lnum = s:GetMSL(lnum) - - " If the previous line wasn't a MSL. - if p_lnum != lnum - " If previous line ends bracket and begins non-bracket continuation decrease indent by 1. - if s:Match(p_lnum, s:bracket_switch_continuation_regex) - return ind - 1 - " If previous line is a continuation return its indent. - " TODO: the || s:IsInString() thing worries me a bit. - elseif s:Match(p_lnum, s:non_bracket_continuation_regex) || s:IsInString(p_lnum,strlen(line)) - return ind - endif - endif - - " Set up more variables, now that we know we wasn't continuation bound. - let line = getline(lnum) - let msl_ind = indent(lnum) - - " If the MSL line had an indenting keyword in it, add a level of indent. - " TODO: this does not take into account contrived things such as - " module Foo; class Bar; end - if s:Match(lnum, s:ruby_indent_keywords) - let ind = msl_ind + sw - if s:Match(lnum, s:end_end_regex) - let ind = ind - sw - endif - return ind - endif - - " If the previous line ended with [*+/.,-=], but wasn't a block ending or a - " closing bracket, indent one extra level. - if s:Match(lnum, s:non_bracket_continuation_regex) && !s:Match(lnum, '^\s*\([\])}]\|end\)') - if lnum == p_lnum - let ind = msl_ind + sw - else - let ind = msl_ind - endif - return ind - endif - - " }}}2 - - return ind -endfunction - " }}}1 let &cpo = s:cpo_save diff --git a/runtime/syntax/eruby.vim b/runtime/syntax/eruby.vim index 4e175bcc25..6bb24fe562 100644 --- a/runtime/syntax/eruby.vim +++ b/runtime/syntax/eruby.vim @@ -3,8 +3,9 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2018 Jul 04 -if exists("b:current_syntax") +if &syntax !~# '\<eruby\>' || get(b:, 'current_syntax') =~# '\<eruby\>' finish endif @@ -18,11 +19,13 @@ endif if &filetype =~ '^eruby\.' let b:eruby_subtype = matchstr(&filetype,'^eruby\.\zs\w\+') +elseif &filetype =~ '^.*\.eruby\>' + let b:eruby_subtype = matchstr(&filetype,'^.\{-\}\ze\.eruby\>') elseif !exists("b:eruby_subtype") && main_syntax == 'eruby' let s:lines = getline(1)."\n".getline(2)."\n".getline(3)."\n".getline(4)."\n".getline(5)."\n".getline("$") let b:eruby_subtype = matchstr(s:lines,'eruby_subtype=\zs\w\+') if b:eruby_subtype == '' - let b:eruby_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.erb\|\.eruby\|\.erubis\)\+$','',''),'\.\zs\w\+\%(\ze+\w\+\)\=$') + let b:eruby_subtype = matchstr(substitute(expand("%:t"),'\c\%(\.erb\|\.eruby\|\.erubis\|\.example\)\+$','',''),'\.\zs\w\+\%(\ze+\w\+\)\=$') endif if b:eruby_subtype == 'rhtml' let b:eruby_subtype = 'html' @@ -41,16 +44,20 @@ elseif !exists("b:eruby_subtype") && main_syntax == 'eruby' endif if !exists("b:eruby_nest_level") - let b:eruby_nest_level = strlen(substitute(substitute(substitute(expand("%:t"),'@','','g'),'\c\.\%(erb\|rhtml\)\>','@','g'),'[^@]','','g')) + if &syntax =~# '\<eruby\.eruby\>' + let b:eruby_nest_level = strlen(substitute(substitute(&filetype,'\C\<eruby\>','@','g'),'[^@]','','g')) + else + let b:eruby_nest_level = strlen(substitute(substitute(substitute(expand("%:t"),'@','','g'),'\c\.\%(erb\|rhtml\)\>','@','g'),'[^@]','','g')) + endif endif if !b:eruby_nest_level let b:eruby_nest_level = 1 endif -if exists("b:eruby_subtype") && b:eruby_subtype != '' +if get(b:, 'eruby_subtype', '') !~# '^\%(eruby\)\=$' && &syntax =~# '^eruby\>' exe "runtime! syntax/".b:eruby_subtype.".vim" - unlet! b:current_syntax endif +unlet! b:current_syntax syn include @rubyTop syntax/ruby.vim syn cluster erubyRegions contains=erubyOneLiner,erubyBlock,erubyExpression,erubyComment @@ -65,7 +72,7 @@ exe 'syn region erubyComment matchgroup=erubyDelimiter start="<%\{1,'.b:erub hi def link erubyDelimiter PreProc hi def link erubyComment Comment -let b:current_syntax = 'eruby' +let b:current_syntax = matchstr(&syntax, '^.*\<eruby\>') if main_syntax == 'eruby' unlet main_syntax diff --git a/runtime/syntax/ruby.vim b/runtime/syntax/ruby.vim index ca7f51b1ea..8b88378e60 100644 --- a/runtime/syntax/ruby.vim +++ b/runtime/syntax/ruby.vim @@ -3,6 +3,7 @@ " Maintainer: Doug Kearns <dougkearns@gmail.com> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2018 Jul 09 " ---------------------------------------------------------------------------- " " Previous Maintainer: Mirko Nasato @@ -45,7 +46,7 @@ function! s:foldable(...) abort return 0 endfunction " }}} -syn cluster rubyNotTop contains=@rubyExtendedStringSpecial,@rubyRegexpSpecial,@rubyDeclaration,rubyConditional,rubyExceptional,rubyMethodExceptional,rubyTodo +syn cluster rubyNotTop contains=@rubyExtendedStringSpecial,@rubyRegexpSpecial,@rubyDeclaration,rubyConditional,rubyExceptional,rubyMethodExceptional,rubyTodo,rubyModuleName,rubyClassName,rubySymbolDelimiter " Whitespace Errors {{{1 if exists("ruby_space_errors") @@ -122,21 +123,24 @@ syn match rubyFloat "\%(\%(\w\|[]})\"']\s*\)\@<!-\)\=\<\%(0\|[1-9]\d*\%(_\d\+\)* syn match rubyLocalVariableOrMethod "\<[_[:lower:]][_[:alnum:]]*[?!=]\=" contains=NONE display transparent syn match rubyBlockArgument "&[_[:lower:]][_[:alnum:]]" contains=NONE display transparent +syn match rubyClassName "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\u\%(\w\|[^\x00-\x7F]\)*\>\%(\s*(\)\@!" contained +syn match rubyModuleName "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\u\%(\w\|[^\x00-\x7F]\)*\>\%(\s*(\)\@!" contained syn match rubyConstant "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\u\%(\w\|[^\x00-\x7F]\)*\>\%(\s*(\)\@!" syn match rubyClassVariable "@@\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*" display syn match rubyInstanceVariable "@\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*" display syn match rubyGlobalVariable "$\%(\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\|-.\)" -syn match rubySymbol "[]})\"':]\@1<!:\%(\^\|\~@\|\~\|<<\|<=>\|<=\|<\|===\|[=!]=\|[=!]\~\|!@\|!\|>>\|>=\|>\||\|-@\|-\|/\|\[]=\|\[]\|\*\*\|\*\|&\|%\|+@\|+\|`\)" -syn match rubySymbol "[]})\"':]\@1<!:\$\%(-.\|[`~<=>_,;:!?/.'"@$*\&+0]\)" -syn match rubySymbol "[]})\"':]\@1<!:\%(\$\|@@\=\)\=\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*" -syn match rubySymbol "[]})\"':]\@1<!:\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\%([?!=]>\@!\)\=" +syn match rubySymbolDelimiter ":" contained +syn match rubySymbol "[]})\"':]\@1<!:\%(\^\|\~@\|\~\|<<\|<=>\|<=\|<\|===\|[=!]=\|[=!]\~\|!@\|!\|>>\|>=\|>\||\|-@\|-\|/\|\[]=\|\[]\|\*\*\|\*\|&\|%\|+@\|+\|`\)" contains=rubySymbolDelimiter +syn match rubySymbol "[]})\"':]\@1<!:\$\%(-.\|[`~<=>_,;:!?/.'"@$*\&+0]\)" contains=rubySymbolDelimiter +syn match rubySymbol "[]})\"':]\@1<!:\%(\$\|@@\=\)\=\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*" contains=rubySymbolDelimiter +syn match rubySymbol "[]})\"':]\@1<!:\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\%([?!=]>\@!\)\=" contains=rubySymbolDelimiter if s:foldable(':') - syn region rubySymbol start="[]})\"':]\@1<!:'" end="'" skip="\\\\\|\\'" contains=rubyQuoteEscape fold - syn region rubySymbol start="[]})\"':]\@1<!:\"" end="\"" skip="\\\\\|\\\"" contains=@rubyStringSpecial fold + syn region rubySymbol matchgroup=rubySymbolDelimiter start="[]})\"':]\@1<!:'" end="'" skip="\\\\\|\\'" contains=rubyQuoteEscape fold + syn region rubySymbol matchgroup=rubySymbolDelimiter start="[]})\"':]\@1<!:\"" end="\"" skip="\\\\\|\\\"" contains=@rubyStringSpecial fold else - syn region rubySymbol start="[]})\"':]\@1<!:'" end="'" skip="\\\\\|\\'" contains=rubyQuoteEscape - syn region rubySymbol start="[]})\"':]\@1<!:\"" end="\"" skip="\\\\\|\\\"" contains=@rubyStringSpecial + syn region rubySymbol matchgroup=rubySymbolDelimiter start="[]})\"':]\@1<!:'" end="'" skip="\\\\\|\\'" contains=rubyQuoteEscape + syn region rubySymbol matchgroup=rubySymbolDelimiter start="[]})\"':]\@1<!:\"" end="\"" skip="\\\\\|\\\"" contains=@rubyStringSpecial endif syn match rubyCapitalizedMethod "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\u\%(\w\|[^\x00-\x7F]\)*\>\%(\s*(\)*\s*(\@=" @@ -157,10 +161,10 @@ syn match rubyPredefinedConstant "\%(\%(^\|[^.]\)\.\s*\)\@<!\<\%(RUBY_\%(VERSION " Normal Regular Expression {{{1 if s:foldable('/') syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\%(^\|\<\%(and\|or\|while\|until\|unless\|if\|elsif\|when\|not\|then\|else\)\|[;\~=!|&(,{[<>?:*+-]\)\s*\)\@<=/" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial fold - syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\h\k*\s\+\)\@<=/[ \t=]\@!" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial fold + syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\h\k*\s\+\)\@<=/\%([ \t=]\|$\)\@!" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial fold else syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\%(^\|\<\%(and\|or\|while\|until\|unless\|if\|elsif\|when\|not\|then\|else\)\|[;\~=!|&(,{[<>?:*+-]\)\s*\)\@<=/" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial - syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\h\k*\s\+\)\@<=/[ \t=]\@!" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial + syn region rubyRegexp matchgroup=rubyRegexpDelimiter start="\%(\h\k*\s\+\)\@<=/\%([ \t=]\|$\)\@!" end="/[iomxneus]*" skip="\\\\\|\\/" contains=@rubyRegexpSpecial endif " Generalized Regular Expression {{{1 @@ -275,10 +279,10 @@ else endif " Here Document {{{1 -syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\s*\|\%([]})"'.]\|::\)\)\_s*\|\w\)\@<!<<[-~]\=\zs\%(\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\)+ end=+$+ oneline contains=ALLBUT,@rubyNotTop -syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\s*\|\%([]})"'.]\|::\)\)\_s*\|\w\)\@<!<<[-~]\=\zs"\%([^"]*\)"+ end=+$+ oneline contains=ALLBUT,@rubyNotTop -syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\s*\|\%([]})"'.]\|::\)\)\_s*\|\w\)\@<!<<[-~]\=\zs'\%([^']*\)'+ end=+$+ oneline contains=ALLBUT,@rubyNotTop -syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\s*\|\%([]})"'.]\|::\)\)\_s*\|\w\)\@<!<<[-~]\=\zs`\%([^`]*\)`+ end=+$+ oneline contains=ALLBUT,@rubyNotTop +syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\|::\)\_s*\|\%([]})"'.]\)\s\|\w\)\@<!<<[-~]\=\zs\%(\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\)+ end=+$+ oneline contains=ALLBUT,@rubyNotTop +syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\|::\)\_s*\|\%([]})"'.]\)\s\|\w\)\@<!<<[-~]\=\zs"\%([^"]*\)"+ end=+$+ oneline contains=ALLBUT,@rubyNotTop +syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\|::\)\_s*\|\%([]})"'.]\)\s\|\w\)\@<!<<[-~]\=\zs'\%([^']*\)'+ end=+$+ oneline contains=ALLBUT,@rubyNotTop +syn region rubyHeredocStart matchgroup=rubyStringDelimiter start=+\%(\%(class\|::\)\_s*\|\%([]})"'.]\)\s\|\w\)\@<!<<[-~]\=\zs`\%([^`]*\)`+ end=+$+ oneline contains=ALLBUT,@rubyNotTop if s:foldable('<<') syn region rubyString start=+\%(\%(class\|::\)\_s*\|\%([]})"'.]\)\s\|\w\)\@<!<<\z(\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\)\ze\%(.*<<[-~]\=['`"]\=\h\)\@!+hs=s+2 matchgroup=rubyStringDelimiter end=+^\z1$+ contains=rubyHeredocStart,rubyHeredoc,@rubyStringSpecial fold keepend @@ -305,19 +309,19 @@ endif " eRuby Config {{{1 if exists('main_syntax') && main_syntax == 'eruby' let b:ruby_no_expensive = 1 -end +endif " Module, Class, Method and Alias Declarations {{{1 syn match rubyAliasDeclaration "[^[:space:];#.()]\+" contained contains=rubySymbol,rubyGlobalVariable,rubyPredefinedVariable nextgroup=rubyAliasDeclaration2 skipwhite syn match rubyAliasDeclaration2 "[^[:space:];#.()]\+" contained contains=rubySymbol,rubyGlobalVariable,rubyPredefinedVariable syn match rubyMethodDeclaration "[^[:space:];#(]\+" contained contains=rubyConstant,rubyBoolean,rubyPseudoVariable,rubyInstanceVariable,rubyClassVariable,rubyGlobalVariable -syn match rubyClassDeclaration "[^[:space:];#<]\+" contained contains=rubyConstant,rubyOperator -syn match rubyModuleDeclaration "[^[:space:];#<]\+" contained contains=rubyConstant,rubyOperator -syn match rubyFunction "\<[_[:alpha:]][_[:alnum:]]*[?!=]\=[[:alnum:]_.:?!=]\@!" contained containedin=rubyMethodDeclaration -syn match rubyFunction "\%(\s\|^\)\@1<=[_[:alpha:]][_[:alnum:]]*[?!=]\=\%(\s\|$\)\@=" contained containedin=rubyAliasDeclaration,rubyAliasDeclaration2 -syn match rubyFunction "\%([[:space:].]\|^\)\@2<=\%(\[\]=\=\|\*\*\|[-+!~]@\=\|[*/%|&^~]\|<<\|>>\|[<>]=\=\|<=>\|===\|[=!]=\|[=!]\~\|!\|`\)\%([[:space:];#(]\|$\)\@=" contained containedin=rubyAliasDeclaration,rubyAliasDeclaration2,rubyMethodDeclaration +syn match rubyClassDeclaration "[^[:space:];#<]\+" contained contains=rubyClassName,rubyOperator +syn match rubyModuleDeclaration "[^[:space:];#<]\+" contained contains=rubyModuleName,rubyOperator +syn match rubyMethodName "\<[_[:alpha:]][_[:alnum:]]*[?!=]\=[[:alnum:]_.:?!=]\@!" contained containedin=rubyMethodDeclaration +syn match rubyMethodName "\%(\s\|^\)\@1<=[_[:alpha:]][_[:alnum:]]*[?!=]\=\%(\s\|$\)\@=" contained containedin=rubyAliasDeclaration,rubyAliasDeclaration2 +syn match rubyMethodName "\%([[:space:].]\|^\)\@2<=\%(\[\]=\=\|\*\*\|[-+!~]@\=\|[*/%|&^~]\|<<\|>>\|[<>]=\=\|<=>\|===\|[=!]=\|[=!]\~\|!\|`\)\%([[:space:];#(]\|$\)\@=" contained containedin=rubyAliasDeclaration,rubyAliasDeclaration2,rubyMethodDeclaration -syn cluster rubyDeclaration contains=rubyAliasDeclaration,rubyAliasDeclaration2,rubyMethodDeclaration,rubyModuleDeclaration,rubyClassDeclaration,rubyFunction,rubyBlockParameter +syn cluster rubyDeclaration contains=rubyAliasDeclaration,rubyAliasDeclaration2,rubyMethodDeclaration,rubyModuleDeclaration,rubyClassDeclaration,rubyMethodName,rubyBlockParameter " Keywords {{{1 " Note: the following keywords have already been defined: @@ -335,7 +339,7 @@ syn match rubyBeginEnd "\<\%(BEGIN\|END\)\>[?!]\@!" if !exists("b:ruby_no_expensive") && !exists("ruby_no_expensive") syn match rubyDefine "\<alias\>" nextgroup=rubyAliasDeclaration skipwhite skipnl syn match rubyDefine "\<def\>" nextgroup=rubyMethodDeclaration skipwhite skipnl - syn match rubyDefine "\<undef\>" nextgroup=rubyFunction skipwhite skipnl + syn match rubyDefine "\<undef\>" nextgroup=rubyMethodName skipwhite skipnl syn match rubyClass "\<class\>" nextgroup=rubyClassDeclaration skipwhite skipnl syn match rubyModule "\<module\>" nextgroup=rubyModuleDeclaration skipwhite skipnl @@ -377,8 +381,6 @@ if !exists("b:ruby_no_expensive") && !exists("ruby_no_expensive") if s:foldable('[') syn region rubyArrayLiteral matchgroup=rubyArrayDelimiter start="\%(\w\|[\]})]\)\@<!\[" end="]" contains=ALLBUT,@rubyNotTop fold - else - syn region rubyArrayLiteral matchgroup=rubyArrayDelimiter start="\%(\w\|[\]})]\)\@<!\[" end="]" contains=ALLBUT,@rubyNotTop endif " statements without 'do' @@ -437,10 +439,12 @@ if !exists("ruby_no_special_methods") syn match rubyControl "\<\%(exit!\|\%(abort\|at_exit\|exit\|fork\|loop\|trap\)\>[?!]\@!\)" syn keyword rubyEval eval class_eval instance_eval module_eval syn keyword rubyException raise fail catch throw - " false positive with 'include?' - syn match rubyInclude "\<include\>[?!]\@!" - syn keyword rubyInclude autoload extend load prepend refine require require_relative using + syn keyword rubyInclude autoload gem load require require_relative syn keyword rubyKeyword callcc caller lambda proc + " false positive with 'include?' + syn match rubyMacro "\<include\>[?!]\@!" + syn keyword rubyMacro extend prepend refine using + syn keyword rubyMacro alias_method define_method define_singleton_method remove_method undef_method endif " Comments and Documentation {{{1 @@ -461,7 +465,7 @@ syn match rubyKeywordAsMethod "\(defined?\|exit!\)\@!\<[_[:lower:]][_[:alnum:]]* " More Symbols {{{1 syn match rubySymbol "\%([{(,]\_s*\)\zs\l\w*[!?]\=::\@!"he=e-1 -syn match rubySymbol "[]})\"':]\@1<!\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],]\@="he=e-1 +syn match rubySymbol "[]})\"':]\@1<!\<\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],]\@="he=e-1 syn match rubySymbol "\%([{(,]\_s*\)\zs[[:space:],{]\l\w*[!?]\=::\@!"hs=s+1,he=e-1 syn match rubySymbol "[[:space:],{(]\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*[!?]\=:[[:space:],]\@="hs=s+1,he=e-1 @@ -477,6 +481,10 @@ hi def link rubyClass rubyDefine hi def link rubyModule rubyDefine hi def link rubyMethodExceptional rubyDefine hi def link rubyDefine Define +hi def link rubyAccess rubyMacro +hi def link rubyAttribute rubyMacro +hi def link rubyMacro Macro +hi def link rubyMethodName rubyFunction hi def link rubyFunction Function hi def link rubyConditional Conditional hi def link rubyConditionalModifier rubyConditional @@ -498,8 +506,9 @@ else endif hi def link rubyClassVariable rubyIdentifier hi def link rubyConstant Type +hi def link rubyClassName rubyConstant +hi def link rubyModuleName rubyConstant hi def link rubyGlobalVariable rubyIdentifier -hi def link rubyBlockParameter rubyIdentifier hi def link rubyInstanceVariable rubyIdentifier hi def link rubyPredefinedIdentifier rubyIdentifier hi def link rubyPredefinedConstant rubyPredefinedIdentifier @@ -508,8 +517,6 @@ hi def link rubySymbol Constant hi def link rubyKeyword Keyword hi def link rubyOperator Operator hi def link rubyBeginEnd Statement -hi def link rubyAccess Statement -hi def link rubyAttribute Statement hi def link rubyEval Statement hi def link rubyPseudoVariable Constant hi def link rubyCapitalizedMethod rubyLocalVariableOrMethod |