diff --git a/Filelist b/Filelist index d6cbce7196..d95c8f4142 100644 --- a/Filelist +++ b/Filelist @@ -495,7 +495,6 @@ RT_ALL = \ runtime/macros/life/click.me \ runtime/macros/life/life.vim \ runtime/macros/matchit.vim \ - runtime/macros/matchit.txt \ runtime/macros/maze/README.txt \ runtime/macros/maze/[mM]akefile \ runtime/macros/maze/main.aap \ @@ -525,6 +524,9 @@ RT_ALL = \ runtime/tutor/tutor \ runtime/tutor/tutor.vim \ runtime/vimrc_example.vim \ + runtime/pack/dist/opt/matchit/plugin/matchit.vim \ + runtime/pack/dist/opt/matchit/doc/matchit.txt \ + runtime/pack/dist/opt/matchit/doc/tags \ # runtime files for all distributions without CR-NL translation RT_ALL_BIN = \ diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index f71cf42cff..b36f91f50b 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -1,4 +1,4 @@ -*usr_05.txt* For Vim version 7.4. Last change: 2012 Nov 20 +*usr_05.txt* For Vim version 7.4. Last change: 2016 Mar 25 VIM USER MANUAL - by Bram Moolenaar @@ -12,10 +12,11 @@ Vim's capabilities. Or define your own macros. |05.1| The vimrc file |05.2| The example vimrc file explained |05.3| Simple mappings -|05.4| Adding a plugin -|05.5| Adding a help file -|05.6| The option window -|05.7| Often used options +|05.4| Adding a package +|05.5| Adding a plugin +|05.6| Adding a help file +|05.7| The option window +|05.8| Often used options Next chapter: |usr_06.txt| Using syntax highlighting Previous chapter: |usr_04.txt| Making small changes @@ -263,7 +264,46 @@ The ":map" command (with no arguments) lists your current mappings. At least the ones for Normal mode. More about mappings in section |40.1|. ============================================================================== -*05.4* Adding a plugin *add-plugin* *plugin* +*05.4* Adding a package *add-package* *matchit-install* + +A package is a set of files that you can add to Vim. There are two kinds of +packages: optional and automatically loaded on startup. + +The Vim distribution comes with a few packages that you can optionally use. +For example, the matchit plugin. This plugin makes the "%" command jump to +matching HTML tags, if/else/endif in Vim scripts, etc. Very useful, although +it's not backwards compatible (that's why it is not enabled by default). + +To start using the matchit plugin, add one line to your vimrc file: > + packadd matchit + +That's all! You can also type the command to try it out. Now you can find +help about this plugin: > + :help matchit + +This works, because when `:packadd` loaded the plugin it also added the +package directory in 'runtimepath', so that the help file can be found. + +You can find packages on the Internet in various places. It usually comes as +an archive or as a repository. For an archive you can follow these steps: + 1. create the package directory: > + mkdir -p ~/.vim/pack/fancy +< "fancy" can be any name of your liking. Use one that describes the + package. + 2. unpack the archive in that directory. This assumes the top + directory in the archive is "start": > + cd ~/.vim/pack/fancy + unzip /tmp/fancy.zip +< If the archive layout is different make sure that you end up with a + path like this: + ~/.vim/pack/fancy/start/fancytext/plugin/fancy.vim ~ + Here "fancytext" is the name of the package, it can be anything + else. + +More information about packages can be found here: |packages|. + +============================================================================== +*05.5* Adding a plugin *add-plugin* *plugin* Vim's functionality can be extended by adding plugins. A plugin is nothing more than a Vim script file that is loaded automatically when Vim starts. You @@ -415,23 +455,19 @@ Further reading: |new-filetype| How to detect a new file type. ============================================================================== -*05.5* Adding a help file *add-local-help* *matchit-install* +*05.6* Adding a help file *add-local-help* If you are lucky, the plugin you installed also comes with a help file. We will explain how to install the help file, so that you can easily find help for your new plugin. - Let us use the "matchit.vim" plugin as an example (it is included with -Vim). This plugin makes the "%" command jump to matching HTML tags, -if/else/endif in Vim scripts, etc. Very useful, although it's not backwards -compatible (that's why it is not enabled by default). - This plugin comes with documentation: "matchit.txt". Let's first copy the -plugin to the right directory. This time we will do it from inside Vim, so -that we can use $VIMRUNTIME. (You may skip some of the "mkdir" commands if -you already have the directory.) > + Let us use the "doit.vim" plugin as an example. This plugin comes with +documentation: "doit.txt". Let's first copy the plugin to the right +directory. This time we will do it from inside Vim. (You may skip some of +the "mkdir" commands if you already have the directory.) > :!mkdir ~/.vim :!mkdir ~/.vim/plugin - :!cp $VIMRUNTIME/macros/matchit.vim ~/.vim/plugin + :!cp /tmp/doit.vim ~/.vim/plugin The "cp" command is for Unix, on MS-DOS you can use "copy". @@ -441,7 +477,7 @@ Now create a "doc" directory in one of the directories in 'runtimepath'. > Copy the help file to the "doc" directory. > - :!cp $VIMRUNTIME/macros/matchit.txt ~/.vim/doc + :!cp /tmp/doit.txt ~/.vim/doc Now comes the trick, which allows you to jump to the subjects in the new help file: Generate the local tags file with the |:helptags| command. > @@ -450,10 +486,10 @@ file: Generate the local tags file with the |:helptags| command. > Now you can use the > - :help g% + :help doit -command to find help for "g%" in the help file you just added. You can see an -entry for the local help file when you do: > +command to find help for "doit" in the help file you just added. You can see +an entry for the local help file when you do: > :help local-additions @@ -464,7 +500,7 @@ them through the tag. For writing a local help file, see |write-local-help|. ============================================================================== -*05.6* The option window +*05.7* The option window If you are looking for an option that does what you want, you can search in the help files here: |options|. Another way is by using this command: > @@ -503,7 +539,7 @@ border. This is what the 'scrolloff' option does, it specifies an offset from the window border where scrolling starts. ============================================================================== -*05.7* Often used options +*05.8* Often used options There are an awful lot of options. Most of them you will hardly ever use. Some of the more useful ones will be mentioned here. Don't forget you can diff --git a/runtime/doc/usr_toc.txt b/runtime/doc/usr_toc.txt index d98a999aa4..b0dc9390a3 100644 --- a/runtime/doc/usr_toc.txt +++ b/runtime/doc/usr_toc.txt @@ -1,4 +1,4 @@ -*usr_toc.txt* For Vim version 7.4. Last change: 2010 Jul 20 +*usr_toc.txt* For Vim version 7.4. Last change: 2016 Mar 25 VIM USER MANUAL - by Bram Moolenaar @@ -104,10 +104,11 @@ Read this from start to end to learn the essential commands. |05.1| The vimrc file |05.2| The example vimrc file explained |05.3| Simple mappings - |05.4| Adding a plugin - |05.5| Adding a help file - |05.6| The option window - |05.7| Often used options + |05.4| Adding a package + |05.5| Adding a plugin + |05.6| Adding a help file + |05.7| The option window + |05.8| Often used options |usr_06.txt| Using syntax highlighting |06.1| Switching it on diff --git a/runtime/macros/README.txt b/runtime/macros/README.txt index ce61cd361a..f599c0a750 100644 --- a/runtime/macros/README.txt +++ b/runtime/macros/README.txt @@ -15,8 +15,6 @@ dvorak for when you use a Dvorak keyboard justify.vim user function for justifying text -matchit.vim + matchit.txt make % match if-fi, HTML tags, and much more - less.sh + less.vim make Vim work like less (or more) shellmenu.vim menus for editing shell scripts in the GUI version @@ -26,5 +24,9 @@ swapmous.vim swap left and right mouse buttons editexisting.vim when editing a file that is already edited with another Vim instance -This one is only for Unix. It can be found in the extra archive: +This one is only for Unix. file_select.vim macros that make a handy file selector + +The matchit plugin has been moved to an optional package. To load it put this +line in your vimrc file: + :packadd matchit diff --git a/runtime/macros/matchit.vim b/runtime/macros/matchit.vim index 70867b1f93..9cfe2e532f 100644 --- a/runtime/macros/matchit.vim +++ b/runtime/macros/matchit.vim @@ -1,813 +1,3 @@ -" matchit.vim: (global plugin) Extended "%" matching -" Last Change: Fri Jan 25 10:00 AM 2008 EST -" Maintainer: Benji Fisher PhD -" Version: 1.13.2, for Vim 6.3+ -" URL: http://www.vim.org/script.php?script_id=39 - -" Documentation: -" The documentation is in a separate file, matchit.txt . - -" Credits: -" Vim editor by Bram Moolenaar (Thanks, Bram!) -" Original script and design by Raul Segura Acevedo -" Support for comments by Douglas Potts -" Support for back references and other improvements by Benji Fisher -" Support for many languages by Johannes Zellner -" Suggestions for improvement, bug reports, and support for additional -" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark -" Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner. - -" Debugging: -" If you'd like to try the built-in debugging commands... -" :MatchDebug to activate debugging for the current buffer -" This saves the values of several key script variables as buffer-local -" variables. See the MatchDebug() function, below, for details. - -" TODO: I should think about multi-line patterns for b:match_words. -" This would require an option: how many lines to scan (default 1). -" This would be useful for Python, maybe also for *ML. -" TODO: Maybe I should add a menu so that people will actually use some of -" the features that I have implemented. -" TODO: Eliminate the MultiMatch function. Add yet another argument to -" Match_wrapper() instead. -" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' -" TODO: Make backrefs safer by using '\V' (very no-magic). -" TODO: Add a level of indirection, so that custom % scripts can use my -" work but extend it. - -" allow user to prevent loading -" and prevent duplicate loading -if exists("loaded_matchit") || &cp - finish -endif -let loaded_matchit = 1 -let s:last_mps = "" -let s:last_words = ":" - -let s:save_cpo = &cpo -set cpo&vim - -nnoremap % :call Match_wrapper('',1,'n') -nnoremap g% :call Match_wrapper('',0,'n') -vnoremap % :call Match_wrapper('',1,'v') m'gv`` -vnoremap g% :call Match_wrapper('',0,'v') m'gv`` -onoremap % v:call Match_wrapper('',1,'o') -onoremap g% v:call Match_wrapper('',0,'o') - -" Analogues of [{ and ]} using matching patterns: -nnoremap [% :call MultiMatch("bW", "n") -nnoremap ]% :call MultiMatch("W", "n") -vmap [% [%m'gv`` -vmap ]% ]%m'gv`` -" vnoremap [% :call MultiMatch("bW", "v") m'gv`` -" vnoremap ]% :call MultiMatch("W", "v") m'gv`` -onoremap [% v:call MultiMatch("bW", "o") -onoremap ]% v:call MultiMatch("W", "o") - -" text object: -vmap a% [%v]% - -" Auto-complete mappings: (not yet "ready for prime time") -" TODO Read :help write-plugin for the "right" way to let the user -" specify a key binding. -" let g:match_auto = '' -" let g:match_autoCR = '' -" if exists("g:match_auto") -" execute "inoremap " . g:match_auto . ' x"=Autocomplete()Pls' -" endif -" if exists("g:match_autoCR") -" execute "inoremap " . g:match_autoCR . ' =Autocomplete()' -" endif -" if exists("g:match_gthhoh") -" execute "inoremap " . g:match_gthhoh . ' :call Gthhoh()' -" endif " gthhoh = "Get the heck out of here!" - -let s:notslash = '\\\@" - endif - " In s:CleanUp(), we may need to check whether the cursor moved forward. - let startline = line(".") - let startcol = col(".") - " Use default behavior if called with a count. - if v:count - exe "normal! " . v:count . "%" - return s:CleanUp(restore_options, a:mode, startline, startcol) - end - - " First step: if not already done, set the script variables - " s:do_BR flag for whether there are backrefs - " s:pat parsed version of b:match_words - " s:all regexp based on s:pat and the default groups - " - if !exists("b:match_words") || b:match_words == "" - let match_words = "" - " Allow b:match_words = "GetVimMatchWords()" . - elseif b:match_words =~ ":" - let match_words = b:match_words - else - execute "let match_words =" b:match_words - endif -" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion! - if (match_words != s:last_words) || (&mps != s:last_mps) || - \ exists("b:match_debug") - let s:last_words = match_words - let s:last_mps = &mps - " The next several lines were here before - " BF started messing with this script. - " quote the special chars in 'matchpairs', replace [,:] with \| and then - " append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif) - " let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+', - " \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>' - let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . - \ '\/\*:\*\/,#\s*if\%(def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' - " s:all = pattern with all the keywords - let match_words = match_words . (strlen(match_words) ? "," : "") . default - if match_words !~ s:notslash . '\\\d' - let s:do_BR = 0 - let s:pat = match_words - else - let s:do_BR = 1 - let s:pat = s:ParseWords(match_words) - endif - let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g') - let s:all = '\%(' . s:all . '\)' - " let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)' - if exists("b:match_debug") - let b:match_pat = s:pat - endif - endif - - " Second step: set the following local variables: - " matchline = line on which the cursor started - " curcol = number of characters before match - " prefix = regexp for start of line to start of match - " suffix = regexp for end of match to end of line - " Require match to end on or after the cursor and prefer it to - " start on or before the cursor. - let matchline = getline(startline) - if a:word != '' - " word given - if a:word !~ s:all - echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE - return s:CleanUp(restore_options, a:mode, startline, startcol) - endif - let matchline = a:word - let curcol = 0 - let prefix = '^\%(' - let suffix = '\)$' - " Now the case when "word" is not given - else " Find the match that ends on or after the cursor and set curcol. - let regexp = s:Wholematch(matchline, s:all, startcol-1) - let curcol = match(matchline, regexp) - " If there is no match, give up. - if curcol == -1 - return s:CleanUp(restore_options, a:mode, startline, startcol) - endif - let endcol = matchend(matchline, regexp) - let suf = strlen(matchline) - endcol - let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(') - let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$') - endif - if exists("b:match_debug") - let b:match_match = matchstr(matchline, regexp) - let b:match_col = curcol+1 - endif - - " Third step: Find the group and single word that match, and the original - " (backref) versions of these. Then, resolve the backrefs. - " Set the following local variable: - " group = colon-separated list of patterns, one of which matches - " = ini:mid:fin or ini:fin - " - " Reconstruct the version with unresolved backrefs. - let patBR = substitute(match_words.',', - \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g') - let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g') - " Now, set group and groupBR to the matching group: 'if:endif' or - " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns - " group . "," . groupBR, and we pick it apart. - let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) - let i = matchend(group, s:notslash . ",") - let groupBR = strpart(group, i) - let group = strpart(group, 0, i-1) - " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix - if s:do_BR " Do the hard part: resolve those backrefs! - let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) - endif - if exists("b:match_debug") - let b:match_wholeBR = groupBR - let i = matchend(groupBR, s:notslash . ":") - let b:match_iniBR = strpart(groupBR, 0, i-1) - endif - - " Fourth step: Set the arguments for searchpair(). - let i = matchend(group, s:notslash . ":") - let j = matchend(group, '.*' . s:notslash . ":") - let ini = strpart(group, 0, i-1) - let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g') - let fin = strpart(group, j) - "Un-escape the remaining , and : characters. - let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') - let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') - let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') - " searchpair() requires that these patterns avoid \(\) groups. - let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g') - let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g') - let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g') - " Set mid. This is optimized for readability, not micro-efficiency! - if a:forward && matchline =~ prefix . fin . suffix - \ || !a:forward && matchline =~ prefix . ini . suffix - let mid = "" - endif - " Set flag. This is optimized for readability, not micro-efficiency! - if a:forward && matchline =~ prefix . fin . suffix - \ || !a:forward && matchline !~ prefix . ini . suffix - let flag = "bW" - else - let flag = "W" - endif - " Set skip. - if exists("b:match_skip") - let skip = b:match_skip - elseif exists("b:match_comment") " backwards compatibility and testing! - let skip = "r:" . b:match_comment - else - let skip = 's:comment\|string' - endif - let skip = s:ParseSkip(skip) - if exists("b:match_debug") - let b:match_ini = ini - let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin - endif - - " Fifth step: actually start moving the cursor and call searchpair(). - " Later, :execute restore_cursor to get to the original screen. - let restore_cursor = virtcol(".") . "|" - normal! g0 - let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor - normal! H - let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor - execute restore_cursor - call cursor(0, curcol + 1) - " normal! 0 - " if curcol - " execute "normal!" . curcol . "l" - " endif - if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) - let skip = "0" - else - execute "if " . skip . "| let skip = '0' | endif" - endif - let sp_return = searchpair(ini, mid, fin, flag, skip) - let final_position = "call cursor(" . line(".") . "," . col(".") . ")" - " Restore cursor position and original screen. - execute restore_cursor - normal! m' - if sp_return > 0 - execute final_position - endif - return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin) -endfun - -" Restore options and do some special handling for Operator-pending mode. -" The optional argument is the tail of the matching group. -fun! s:CleanUp(options, mode, startline, startcol, ...) - execute "set" a:options - " Open folds, if appropriate. - if a:mode != "o" - if &foldopen =~ "percent" - normal! zv - endif - " In Operator-pending mode, we want to include the whole match - " (for example, d%). - " This is only a problem if we end up moving in the forward direction. - elseif (a:startline < line(".")) || - \ (a:startline == line(".") && a:startcol < col(".")) - if a:0 - " Check whether the match is a single character. If not, move to the - " end of the match. - let matchline = getline(".") - let currcol = col(".") - let regexp = s:Wholematch(matchline, a:1, currcol-1) - let endcol = matchend(matchline, regexp) - if endcol > currcol " This is NOT off by one! - call cursor(0, endcol) - endif - endif " a:0 - endif " a:mode != "o" && etc. - return 0 -endfun - -" Example (simplified HTML patterns): if -" a:groupBR = '<\(\k\+\)>:' -" a:prefix = '^.\{3}\(' -" a:group = '<\(\k\+\)>:' -" a:suffix = '\).\{2}$' -" a:matchline = "12312" or "12312" -" then extract "tag" from a:matchline and return ":" . -fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) - if a:matchline !~ a:prefix . - \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix - return a:group - endif - let i = matchend(a:groupBR, s:notslash . ':') - let ini = strpart(a:groupBR, 0, i-1) - let tailBR = strpart(a:groupBR, i) - let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix, - \ a:groupBR) - let i = matchend(word, s:notslash . ":") - let wordBR = strpart(word, i) - let word = strpart(word, 0, i-1) - " Now, a:matchline =~ a:prefix . word . a:suffix - if wordBR != ini - let table = s:Resolve(ini, wordBR, "table") - else - " let table = "----------" - let table = "" - let d = 0 - while d < 10 - if tailBR =~ s:notslash . '\\' . d - " let table[d] = d - let table = table . d - else - let table = table . "-" - endif - let d = d + 1 - endwhile - endif - let d = 9 - while d - if table[d] != "-" - let backref = substitute(a:matchline, a:prefix.word.a:suffix, - \ '\'.table[d], "") - " Are there any other characters that should be escaped? - let backref = escape(backref, '*,:') - execute s:Ref(ini, d, "start", "len") - let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len) - let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d, - \ escape(backref, '\\&'), 'g') - endif - let d = d-1 - endwhile - if exists("b:match_debug") - if s:do_BR - let b:match_table = table - let b:match_word = word - else - let b:match_table = "" - let b:match_word = "" - endif - endif - return ini . ":" . tailBR -endfun - -" Input a comma-separated list of groups with backrefs, such as -" a:groups = '\(foo\):end\1,\(bar\):end\1' -" and return a comma-separated list of groups with backrefs replaced: -" return '\(foo\):end\(foo\),\(bar\):end\(bar\)' -fun! s:ParseWords(groups) - let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g') - let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g') - let parsed = "" - while groups =~ '[^,:]' - let i = matchend(groups, s:notslash . ':') - let j = matchend(groups, s:notslash . ',') - let ini = strpart(groups, 0, i-1) - let tail = strpart(groups, i, j-i-1) . ":" - let groups = strpart(groups, j) - let parsed = parsed . ini - let i = matchend(tail, s:notslash . ':') - while i != -1 - " In 'if:else:endif', ini='if' and word='else' and then word='endif'. - let word = strpart(tail, 0, i-1) - let tail = strpart(tail, i) - let i = matchend(tail, s:notslash . ':') - let parsed = parsed . ":" . s:Resolve(ini, word, "word") - endwhile " Now, tail has been used up. - let parsed = parsed . "," - endwhile " groups =~ '[^,:]' - let parsed = substitute(parsed, ',$', '', '') - return parsed -endfun - -" TODO I think this can be simplified and/or made more efficient. -" TODO What should I do if a:start is out of range? -" Return a regexp that matches all of a:string, such that -" matchstr(a:string, regexp) represents the match for a:pat that starts -" as close to a:start as possible, before being preferred to after, and -" ends after a:start . -" Usage: -" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1) -" let i = match(getline("."), regexp) -" let j = matchend(getline("."), regexp) -" let match = matchstr(getline("."), regexp) -fun! s:Wholematch(string, pat, start) - let group = '\%(' . a:pat . '\)' - let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^') - let len = strlen(a:string) - let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$') - if a:string !~ prefix . group . suffix - let prefix = '' - endif - return prefix . group . suffix -endfun - -" No extra arguments: s:Ref(string, d) will -" find the d'th occurrence of '\(' and return it, along with everything up -" to and including the matching '\)'. -" One argument: s:Ref(string, d, "start") returns the index of the start -" of the d'th '\(' and any other argument returns the length of the group. -" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be -" executed, having the effect of -" :let foo = s:Ref(string, d, "start") -" :let bar = s:Ref(string, d, "len") -fun! s:Ref(string, d, ...) - let len = strlen(a:string) - if a:d == 0 - let start = 0 - else - let cnt = a:d - let match = a:string - while cnt - let cnt = cnt - 1 - let index = matchend(match, s:notslash . '\\(') - if index == -1 - return "" - endif - let match = strpart(match, index) - endwhile - let start = len - strlen(match) - if a:0 == 1 && a:1 == "start" - return start - 2 - endif - let cnt = 1 - while cnt - let index = matchend(match, s:notslash . '\\(\|\\)') - 1 - if index == -2 - return "" - endif - " Increment if an open, decrement if a ')': - let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')' - " let cnt = stridx('0(', match[index]) + cnt - let match = strpart(match, index+1) - endwhile - let start = start - 2 - let len = len - start - strlen(match) - endif - if a:0 == 1 - return len - elseif a:0 == 2 - return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len - else - return strpart(a:string, start, len) - endif -endfun - -" Count the number of disjoint copies of pattern in string. -" If the pattern is a literal string and contains no '0' or '1' characters -" then s:Count(string, pattern, '0', '1') should be faster than -" s:Count(string, pattern). -fun! s:Count(string, pattern, ...) - let pat = escape(a:pattern, '\\') - if a:0 > 1 - let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g") - let foo = substitute(a:string, pat, a:2, "g") - let foo = substitute(foo, '[^' . a:2 . ']', "", "g") - return strlen(foo) - endif - let result = 0 - let foo = a:string - let index = matchend(foo, pat) - while index != -1 - let result = result + 1 - let foo = strpart(foo, index) - let index = matchend(foo, pat) - endwhile - return result -endfun - -" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where -" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first -" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this -" indicates that all other instances of '\1' in target are to be replaced -" by '\3'. The hard part is dealing with nesting... -" Note that ":" is an illegal character for source and target, -" unless it is preceded by "\". -fun! s:Resolve(source, target, output) - let word = a:target - let i = matchend(word, s:notslash . '\\\d') - 1 - let table = "----------" - while i != -2 " There are back references to be replaced. - let d = word[i] - let backref = s:Ref(a:source, d) - " The idea is to replace '\d' with backref. Before we do this, - " replace any \(\) groups in backref with :1, :2, ... if they - " correspond to the first, second, ... group already inserted - " into backref. Later, replace :1 with \1 and so on. The group - " number w+b within backref corresponds to the group number - " s within a:source. - " w = number of '\(' in word before the current one - let w = s:Count( - \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1') - let b = 1 " number of the current '\(' in backref - let s = d " number of the current '\(' in a:source - while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1') - \ && s < 10 - if table[s] == "-" - if w + b < 10 - " let table[s] = w + b - let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1) - endif - let b = b + 1 - let s = s + 1 - else - execute s:Ref(backref, b, "start", "len") - let ref = strpart(backref, start, len) - let backref = strpart(backref, 0, start) . ":". table[s] - \ . strpart(backref, start+len) - let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1') - endif - endwhile - let word = strpart(word, 0, i-1) . backref . strpart(word, i+1) - let i = matchend(word, s:notslash . '\\\d') - 1 - endwhile - let word = substitute(word, s:notslash . '\zs:', '\\', 'g') - if a:output == "table" - return table - elseif a:output == "word" - return word - else - return table . word - endif -endfun - -" Assume a:comma = ",". Then the format for a:patterns and a:1 is -" a:patterns = ",,..." -" a:1 = ",,..." -" If is the first pattern that matches a:string then return -" if no optional arguments are given; return , if a:1 is given. -fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) - let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma) - let i = matchend(tail, s:notslash . a:comma) - if a:0 - let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma) - let j = matchend(alttail, s:notslash . a:comma) - endif - let current = strpart(tail, 0, i-1) - if a:branch == "" - let currpat = current - else - let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') - endif - while a:string !~ a:prefix . currpat . a:suffix - let tail = strpart(tail, i) - let i = matchend(tail, s:notslash . a:comma) - if i == -1 - return -1 - endif - let current = strpart(tail, 0, i-1) - if a:branch == "" - let currpat = current - else - let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') - endif - if a:0 - let alttail = strpart(alttail, j) - let j = matchend(alttail, s:notslash . a:comma) - endif - endwhile - if a:0 - let current = current . a:comma . strpart(alttail, 0, j-1) - endif - return current -endfun - -" Call this function to turn on debugging information. Every time the main -" script is run, buffer variables will be saved. These can be used directly -" or viewed using the menu items below. -if !exists(":MatchDebug") - command! -nargs=0 MatchDebug call s:Match_debug() -endif - -fun! s:Match_debug() - let b:match_debug = 1 " Save debugging information. - " pat = all of b:match_words with backrefs parsed - amenu &Matchit.&pat :echo b:match_pat - " match = bit of text that is recognized as a match - amenu &Matchit.&match :echo b:match_match - " curcol = cursor column of the start of the matching text - amenu &Matchit.&curcol :echo b:match_col - " wholeBR = matching group, original version - amenu &Matchit.wh&oleBR :echo b:match_wholeBR - " iniBR = 'if' piece, original version - amenu &Matchit.ini&BR :echo b:match_iniBR - " ini = 'if' piece, with all backrefs resolved from match - amenu &Matchit.&ini :echo b:match_ini - " tail = 'else\|endif' piece, with all backrefs resolved from match - amenu &Matchit.&tail :echo b:match_tail - " fin = 'endif' piece, with all backrefs resolved from match - amenu &Matchit.&word :echo b:match_word - " '\'.d in ini refers to the same thing as '\'.table[d] in word. - amenu &Matchit.t&able :echo '0:' . b:match_table . ':9' -endfun - -" Jump to the nearest unmatched "(" or "if" or "" if a:spflag == "bW" -" or the nearest unmatched "" or "endif" or ")" if a:spflag == "W". -" Return a "mark" for the original position, so that -" let m = MultiMatch("bW", "n") ... execute m -" will return to the original position. If there is a problem, do not -" move the cursor and return "", unless a count is given, in which case -" go up or down as many levels as possible and again return "". -" TODO This relies on the same patterns as % matching. It might be a good -" idea to give it its own matching patterns. -fun! s:MultiMatch(spflag, mode) - if !exists("b:match_words") || b:match_words == "" - return "" - end - let restore_options = (&ic ? "" : "no") . "ignorecase" - if exists("b:match_ignorecase") - let &ignorecase = b:match_ignorecase - endif - let startline = line(".") - let startcol = col(".") - - " First step: if not already done, set the script variables - " s:do_BR flag for whether there are backrefs - " s:pat parsed version of b:match_words - " s:all regexp based on s:pat and the default groups - " This part is copied and slightly modified from s:Match_wrapper(). - let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . - \ '\/\*:\*\/,#\s*if\%(def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' - " Allow b:match_words = "GetVimMatchWords()" . - if b:match_words =~ ":" - let match_words = b:match_words - else - execute "let match_words =" b:match_words - endif - if (match_words != s:last_words) || (&mps != s:last_mps) || - \ exists("b:match_debug") - let s:last_words = match_words - let s:last_mps = &mps - if match_words !~ s:notslash . '\\\d' - let s:do_BR = 0 - let s:pat = match_words - else - let s:do_BR = 1 - let s:pat = s:ParseWords(match_words) - endif - let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default, - \ '[,:]\+','\\|','g') . '\)' - if exists("b:match_debug") - let b:match_pat = s:pat - endif - endif - - " Second step: figure out the patterns for searchpair() - " and save the screen, cursor position, and 'ignorecase'. - " - TODO: A lot of this is copied from s:Match_wrapper(). - " - maybe even more functionality should be split off - " - into separate functions! - let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default - let open = substitute(s:pat . cdefault, - \ s:notslash . '\zs:.\{-}' . s:notslash . ',', '\\),\\(', 'g') - let open = '\(' . substitute(open, s:notslash . '\zs:.*$', '\\)', '') - let close = substitute(s:pat . cdefault, - \ s:notslash . '\zs,.\{-}' . s:notslash . ':', '\\),\\(', 'g') - let close = substitute(close, '^.\{-}' . s:notslash . ':', '\\(', '') . '\)' - if exists("b:match_skip") - let skip = b:match_skip - elseif exists("b:match_comment") " backwards compatibility and testing! - let skip = "r:" . b:match_comment - else - let skip = 's:comment\|string' - endif - let skip = s:ParseSkip(skip) - " let restore_cursor = line(".") . "G" . virtcol(".") . "|" - " normal! H - " let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor - let restore_cursor = virtcol(".") . "|" - normal! g0 - let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor - normal! H - let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor - execute restore_cursor - - " Third step: call searchpair(). - " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. - let openpat = substitute(open, '\(\\\@" or ... -" and return "endif" or "endwhile" or "" or ... . -" For now, this uses b:match_words and the same script variables -" as s:Match_wrapper() . Later, it may get its own patterns, -" either from a buffer variable or passed as arguments. -" fun! s:Autocomplete() -" echo "autocomplete not yet implemented :-(" -" if !exists("b:match_words") || b:match_words == "" -" return "" -" end -" let startpos = s:MultiMatch("bW") -" -" if startpos == "" -" return "" -" endif -" " - TODO: figure out whether 'if' or '' matched, and construct -" " - the appropriate closing. -" let matchline = getline(".") -" let curcol = col(".") - 1 -" " - TODO: Change the s:all argument if there is a new set of match pats. -" let regexp = s:Wholematch(matchline, s:all, curcol) -" let suf = strlen(matchline) - matchend(matchline, regexp) -" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') -" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') -" " Reconstruct the version with unresolved backrefs. -" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g') -" let patBR = substitute(patBR, ':\{2,}', ':', "g") -" " Now, set group and groupBR to the matching group: 'if:endif' or -" " 'while:endwhile' or whatever. -" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) -" let i = matchend(group, s:notslash . ",") -" let groupBR = strpart(group, i) -" let group = strpart(group, 0, i-1) -" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix -" if s:do_BR -" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) -" endif -" " let g:group = group -" -" " - TODO: Construct the closing from group. -" let fake = "end" . expand("") -" execute startpos -" return fake -" endfun - -" Close all open structures. "Get the heck out of here!" -" fun! s:Gthhoh() -" let close = s:Autocomplete() -" while strlen(close) -" put=close -" let close = s:Autocomplete() -" endwhile -" endfun - -" Parse special strings as typical skip arguments for searchpair(): -" s:foo becomes (current syntax item) =~ foo -" S:foo becomes (current syntax item) !~ foo -" r:foo becomes (line before cursor) =~ foo -" R:foo becomes (line before cursor) !~ foo -fun! s:ParseSkip(str) - let skip = a:str - if skip[1] == ":" - if skip[0] == "s" - let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" . - \ strpart(skip,2) . "'" - elseif skip[0] == "S" - let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" . - \ strpart(skip,2) . "'" - elseif skip[0] == "r" - let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'" - elseif skip[0] == "R" - let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'" - endif - endif - return skip -endfun - -let &cpo = s:save_cpo -unlet s:save_cpo - -" vim:sts=2:sw=2: +" Load the matchit package. +" For those users who were loading the matchit plugin from here. +packadd matchit diff --git a/runtime/macros/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt similarity index 100% rename from runtime/macros/matchit.txt rename to runtime/pack/dist/opt/matchit/doc/matchit.txt diff --git a/runtime/pack/dist/opt/matchit/doc/tags b/runtime/pack/dist/opt/matchit/doc/tags new file mode 100644 index 0000000000..4ccdc8721c --- /dev/null +++ b/runtime/pack/dist/opt/matchit/doc/tags @@ -0,0 +1,50 @@ +:MatchDebug matchit.txt /*:MatchDebug* +MatchError matchit.txt /*MatchError* +[% matchit.txt /*[%* +]% matchit.txt /*]%* +b:match_col matchit.txt /*b:match_col* +b:match_debug matchit.txt /*b:match_debug* +b:match_ignorecase matchit.txt /*b:match_ignorecase* +b:match_ini matchit.txt /*b:match_ini* +b:match_iniBR matchit.txt /*b:match_iniBR* +b:match_match matchit.txt /*b:match_match* +b:match_pat matchit.txt /*b:match_pat* +b:match_skip matchit.txt /*b:match_skip* +b:match_table matchit.txt /*b:match_table* +b:match_tail matchit.txt /*b:match_tail* +b:match_wholeBR matchit.txt /*b:match_wholeBR* +b:match_word matchit.txt /*b:match_word* +b:match_words matchit.txt /*b:match_words* +g% matchit.txt /*g%* +matchit matchit.txt /*matchit* +matchit-% matchit.txt /*matchit-%* +matchit-\1 matchit.txt /*matchit-\\1* +matchit-activate matchit.txt /*matchit-activate* +matchit-backref matchit.txt /*matchit-backref* +matchit-bugs matchit.txt /*matchit-bugs* +matchit-choose matchit.txt /*matchit-choose* +matchit-configure matchit.txt /*matchit-configure* +matchit-debug matchit.txt /*matchit-debug* +matchit-details matchit.txt /*matchit-details* +matchit-highlight matchit.txt /*matchit-highlight* +matchit-hl matchit.txt /*matchit-hl* +matchit-intro matchit.txt /*matchit-intro* +matchit-languages matchit.txt /*matchit-languages* +matchit-modes matchit.txt /*matchit-modes* +matchit-newlang matchit.txt /*matchit-newlang* +matchit-o_% matchit.txt /*matchit-o_%* +matchit-parse matchit.txt /*matchit-parse* +matchit-s:notend matchit.txt /*matchit-s:notend* +matchit-s:sol matchit.txt /*matchit-s:sol* +matchit-spaces matchit.txt /*matchit-spaces* +matchit-troubleshoot matchit.txt /*matchit-troubleshoot* +matchit-v_% matchit.txt /*matchit-v_%* +matchit.txt matchit.txt /*matchit.txt* +matchit.vim matchit.txt /*matchit.vim* +o_[% matchit.txt /*o_[%* +o_]% matchit.txt /*o_]%* +o_g% matchit.txt /*o_g%* +v_[% matchit.txt /*v_[%* +v_]% matchit.txt /*v_]%* +v_a% matchit.txt /*v_a%* +v_g% matchit.txt /*v_g%* diff --git a/runtime/pack/dist/opt/matchit/plugin/matchit.vim b/runtime/pack/dist/opt/matchit/plugin/matchit.vim new file mode 100644 index 0000000000..70867b1f93 --- /dev/null +++ b/runtime/pack/dist/opt/matchit/plugin/matchit.vim @@ -0,0 +1,813 @@ +" matchit.vim: (global plugin) Extended "%" matching +" Last Change: Fri Jan 25 10:00 AM 2008 EST +" Maintainer: Benji Fisher PhD +" Version: 1.13.2, for Vim 6.3+ +" URL: http://www.vim.org/script.php?script_id=39 + +" Documentation: +" The documentation is in a separate file, matchit.txt . + +" Credits: +" Vim editor by Bram Moolenaar (Thanks, Bram!) +" Original script and design by Raul Segura Acevedo +" Support for comments by Douglas Potts +" Support for back references and other improvements by Benji Fisher +" Support for many languages by Johannes Zellner +" Suggestions for improvement, bug reports, and support for additional +" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark +" Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner. + +" Debugging: +" If you'd like to try the built-in debugging commands... +" :MatchDebug to activate debugging for the current buffer +" This saves the values of several key script variables as buffer-local +" variables. See the MatchDebug() function, below, for details. + +" TODO: I should think about multi-line patterns for b:match_words. +" This would require an option: how many lines to scan (default 1). +" This would be useful for Python, maybe also for *ML. +" TODO: Maybe I should add a menu so that people will actually use some of +" the features that I have implemented. +" TODO: Eliminate the MultiMatch function. Add yet another argument to +" Match_wrapper() instead. +" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +" TODO: Make backrefs safer by using '\V' (very no-magic). +" TODO: Add a level of indirection, so that custom % scripts can use my +" work but extend it. + +" allow user to prevent loading +" and prevent duplicate loading +if exists("loaded_matchit") || &cp + finish +endif +let loaded_matchit = 1 +let s:last_mps = "" +let s:last_words = ":" + +let s:save_cpo = &cpo +set cpo&vim + +nnoremap % :call Match_wrapper('',1,'n') +nnoremap g% :call Match_wrapper('',0,'n') +vnoremap % :call Match_wrapper('',1,'v') m'gv`` +vnoremap g% :call Match_wrapper('',0,'v') m'gv`` +onoremap % v:call Match_wrapper('',1,'o') +onoremap g% v:call Match_wrapper('',0,'o') + +" Analogues of [{ and ]} using matching patterns: +nnoremap [% :call MultiMatch("bW", "n") +nnoremap ]% :call MultiMatch("W", "n") +vmap [% [%m'gv`` +vmap ]% ]%m'gv`` +" vnoremap [% :call MultiMatch("bW", "v") m'gv`` +" vnoremap ]% :call MultiMatch("W", "v") m'gv`` +onoremap [% v:call MultiMatch("bW", "o") +onoremap ]% v:call MultiMatch("W", "o") + +" text object: +vmap a% [%v]% + +" Auto-complete mappings: (not yet "ready for prime time") +" TODO Read :help write-plugin for the "right" way to let the user +" specify a key binding. +" let g:match_auto = '' +" let g:match_autoCR = '' +" if exists("g:match_auto") +" execute "inoremap " . g:match_auto . ' x"=Autocomplete()Pls' +" endif +" if exists("g:match_autoCR") +" execute "inoremap " . g:match_autoCR . ' =Autocomplete()' +" endif +" if exists("g:match_gthhoh") +" execute "inoremap " . g:match_gthhoh . ' :call Gthhoh()' +" endif " gthhoh = "Get the heck out of here!" + +let s:notslash = '\\\@" + endif + " In s:CleanUp(), we may need to check whether the cursor moved forward. + let startline = line(".") + let startcol = col(".") + " Use default behavior if called with a count. + if v:count + exe "normal! " . v:count . "%" + return s:CleanUp(restore_options, a:mode, startline, startcol) + end + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + " + if !exists("b:match_words") || b:match_words == "" + let match_words = "" + " Allow b:match_words = "GetVimMatchWords()" . + elseif b:match_words =~ ":" + let match_words = b:match_words + else + execute "let match_words =" b:match_words + endif +" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion! + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let s:last_words = match_words + let s:last_mps = &mps + " The next several lines were here before + " BF started messing with this script. + " quote the special chars in 'matchpairs', replace [,:] with \| and then + " append the builtin pairs (/*, */, #if, #ifdef, #else, #elif, #endif) + " let default = substitute(escape(&mps, '[$^.*~\\/?]'), '[,:]\+', + " \ '\\|', 'g').'\|\/\*\|\*\/\|#if\>\|#ifdef\>\|#else\>\|#elif\>\|#endif\>' + let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . + \ '\/\*:\*\/,#\s*if\%(def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + " s:all = pattern with all the keywords + let match_words = match_words . (strlen(match_words) ? "," : "") . default + if match_words !~ s:notslash . '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + let s:all = substitute(s:pat, s:notslash . '\zs[,:]\+', '\\|', 'g') + let s:all = '\%(' . s:all . '\)' + " let s:all = '\%(' . substitute(s:all, '\\\ze[,:]', '', 'g') . '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + endif + + " Second step: set the following local variables: + " matchline = line on which the cursor started + " curcol = number of characters before match + " prefix = regexp for start of line to start of match + " suffix = regexp for end of match to end of line + " Require match to end on or after the cursor and prefer it to + " start on or before the cursor. + let matchline = getline(startline) + if a:word != '' + " word given + if a:word !~ s:all + echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE + return s:CleanUp(restore_options, a:mode, startline, startcol) + endif + let matchline = a:word + let curcol = 0 + let prefix = '^\%(' + let suffix = '\)$' + " Now the case when "word" is not given + else " Find the match that ends on or after the cursor and set curcol. + let regexp = s:Wholematch(matchline, s:all, startcol-1) + let curcol = match(matchline, regexp) + " If there is no match, give up. + if curcol == -1 + return s:CleanUp(restore_options, a:mode, startline, startcol) + endif + let endcol = matchend(matchline, regexp) + let suf = strlen(matchline) - endcol + let prefix = (curcol ? '^.*\%' . (curcol + 1) . 'c\%(' : '^\%(') + let suffix = (suf ? '\)\%' . (endcol + 1) . 'c.*$' : '\)$') + endif + if exists("b:match_debug") + let b:match_match = matchstr(matchline, regexp) + let b:match_col = curcol+1 + endif + + " Third step: Find the group and single word that match, and the original + " (backref) versions of these. Then, resolve the backrefs. + " Set the following local variable: + " group = colon-separated list of patterns, one of which matches + " = ini:mid:fin or ini:fin + " + " Reconstruct the version with unresolved backrefs. + let patBR = substitute(match_words.',', + \ s:notslash.'\zs[,:]*,[,:]*', ',', 'g') + let patBR = substitute(patBR, s:notslash.'\zs:\{2,}', ':', 'g') + " Now, set group and groupBR to the matching group: 'if:endif' or + " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns + " group . "," . groupBR, and we pick it apart. + let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) + let i = matchend(group, s:notslash . ",") + let groupBR = strpart(group, i) + let group = strpart(group, 0, i-1) + " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix + if s:do_BR " Do the hard part: resolve those backrefs! + let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) + endif + if exists("b:match_debug") + let b:match_wholeBR = groupBR + let i = matchend(groupBR, s:notslash . ":") + let b:match_iniBR = strpart(groupBR, 0, i-1) + endif + + " Fourth step: Set the arguments for searchpair(). + let i = matchend(group, s:notslash . ":") + let j = matchend(group, '.*' . s:notslash . ":") + let ini = strpart(group, 0, i-1) + let mid = substitute(strpart(group, i,j-i-1), s:notslash.'\zs:', '\\|', 'g') + let fin = strpart(group, j) + "Un-escape the remaining , and : characters. + let ini = substitute(ini, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') + let mid = substitute(mid, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') + let fin = substitute(fin, s:notslash . '\zs\\\(:\|,\)', '\1', 'g') + " searchpair() requires that these patterns avoid \(\) groups. + let ini = substitute(ini, s:notslash . '\zs\\(', '\\%(', 'g') + let mid = substitute(mid, s:notslash . '\zs\\(', '\\%(', 'g') + let fin = substitute(fin, s:notslash . '\zs\\(', '\\%(', 'g') + " Set mid. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix . fin . suffix + \ || !a:forward && matchline =~ prefix . ini . suffix + let mid = "" + endif + " Set flag. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix . fin . suffix + \ || !a:forward && matchline !~ prefix . ini . suffix + let flag = "bW" + else + let flag = "W" + endif + " Set skip. + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" . b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + if exists("b:match_debug") + let b:match_ini = ini + let b:match_tail = (strlen(mid) ? mid.'\|' : '') . fin + endif + + " Fifth step: actually start moving the cursor and call searchpair(). + " Later, :execute restore_cursor to get to the original screen. + let restore_cursor = virtcol(".") . "|" + normal! g0 + let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor + normal! H + let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + execute restore_cursor + call cursor(0, curcol + 1) + " normal! 0 + " if curcol + " execute "normal!" . curcol . "l" + " endif + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = "0" + else + execute "if " . skip . "| let skip = '0' | endif" + endif + let sp_return = searchpair(ini, mid, fin, flag, skip) + let final_position = "call cursor(" . line(".") . "," . col(".") . ")" + " Restore cursor position and original screen. + execute restore_cursor + normal! m' + if sp_return > 0 + execute final_position + endif + return s:CleanUp(restore_options, a:mode, startline, startcol, mid.'\|'.fin) +endfun + +" Restore options and do some special handling for Operator-pending mode. +" The optional argument is the tail of the matching group. +fun! s:CleanUp(options, mode, startline, startcol, ...) + execute "set" a:options + " Open folds, if appropriate. + if a:mode != "o" + if &foldopen =~ "percent" + normal! zv + endif + " In Operator-pending mode, we want to include the whole match + " (for example, d%). + " This is only a problem if we end up moving in the forward direction. + elseif (a:startline < line(".")) || + \ (a:startline == line(".") && a:startcol < col(".")) + if a:0 + " Check whether the match is a single character. If not, move to the + " end of the match. + let matchline = getline(".") + let currcol = col(".") + let regexp = s:Wholematch(matchline, a:1, currcol-1) + let endcol = matchend(matchline, regexp) + if endcol > currcol " This is NOT off by one! + call cursor(0, endcol) + endif + endif " a:0 + endif " a:mode != "o" && etc. + return 0 +endfun + +" Example (simplified HTML patterns): if +" a:groupBR = '<\(\k\+\)>:' +" a:prefix = '^.\{3}\(' +" a:group = '<\(\k\+\)>:' +" a:suffix = '\).\{2}$' +" a:matchline = "12312" or "12312" +" then extract "tag" from a:matchline and return ":" . +fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) + if a:matchline !~ a:prefix . + \ substitute(a:group, s:notslash . '\zs:', '\\|', 'g') . a:suffix + return a:group + endif + let i = matchend(a:groupBR, s:notslash . ':') + let ini = strpart(a:groupBR, 0, i-1) + let tailBR = strpart(a:groupBR, i) + let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix, + \ a:groupBR) + let i = matchend(word, s:notslash . ":") + let wordBR = strpart(word, i) + let word = strpart(word, 0, i-1) + " Now, a:matchline =~ a:prefix . word . a:suffix + if wordBR != ini + let table = s:Resolve(ini, wordBR, "table") + else + " let table = "----------" + let table = "" + let d = 0 + while d < 10 + if tailBR =~ s:notslash . '\\' . d + " let table[d] = d + let table = table . d + else + let table = table . "-" + endif + let d = d + 1 + endwhile + endif + let d = 9 + while d + if table[d] != "-" + let backref = substitute(a:matchline, a:prefix.word.a:suffix, + \ '\'.table[d], "") + " Are there any other characters that should be escaped? + let backref = escape(backref, '*,:') + execute s:Ref(ini, d, "start", "len") + let ini = strpart(ini, 0, start) . backref . strpart(ini, start+len) + let tailBR = substitute(tailBR, s:notslash . '\zs\\' . d, + \ escape(backref, '\\&'), 'g') + endif + let d = d-1 + endwhile + if exists("b:match_debug") + if s:do_BR + let b:match_table = table + let b:match_word = word + else + let b:match_table = "" + let b:match_word = "" + endif + endif + return ini . ":" . tailBR +endfun + +" Input a comma-separated list of groups with backrefs, such as +" a:groups = '\(foo\):end\1,\(bar\):end\1' +" and return a comma-separated list of groups with backrefs replaced: +" return '\(foo\):end\(foo\),\(bar\):end\(bar\)' +fun! s:ParseWords(groups) + let groups = substitute(a:groups.",", s:notslash.'\zs[,:]*,[,:]*', ',', 'g') + let groups = substitute(groups, s:notslash . '\zs:\{2,}', ':', 'g') + let parsed = "" + while groups =~ '[^,:]' + let i = matchend(groups, s:notslash . ':') + let j = matchend(groups, s:notslash . ',') + let ini = strpart(groups, 0, i-1) + let tail = strpart(groups, i, j-i-1) . ":" + let groups = strpart(groups, j) + let parsed = parsed . ini + let i = matchend(tail, s:notslash . ':') + while i != -1 + " In 'if:else:endif', ini='if' and word='else' and then word='endif'. + let word = strpart(tail, 0, i-1) + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash . ':') + let parsed = parsed . ":" . s:Resolve(ini, word, "word") + endwhile " Now, tail has been used up. + let parsed = parsed . "," + endwhile " groups =~ '[^,:]' + let parsed = substitute(parsed, ',$', '', '') + return parsed +endfun + +" TODO I think this can be simplified and/or made more efficient. +" TODO What should I do if a:start is out of range? +" Return a regexp that matches all of a:string, such that +" matchstr(a:string, regexp) represents the match for a:pat that starts +" as close to a:start as possible, before being preferred to after, and +" ends after a:start . +" Usage: +" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1) +" let i = match(getline("."), regexp) +" let j = matchend(getline("."), regexp) +" let match = matchstr(getline("."), regexp) +fun! s:Wholematch(string, pat, start) + let group = '\%(' . a:pat . '\)' + let prefix = (a:start ? '\(^.*\%<' . (a:start + 2) . 'c\)\zs' : '^') + let len = strlen(a:string) + let suffix = (a:start+1 < len ? '\(\%>'.(a:start+1).'c.*$\)\@=' : '$') + if a:string !~ prefix . group . suffix + let prefix = '' + endif + return prefix . group . suffix +endfun + +" No extra arguments: s:Ref(string, d) will +" find the d'th occurrence of '\(' and return it, along with everything up +" to and including the matching '\)'. +" One argument: s:Ref(string, d, "start") returns the index of the start +" of the d'th '\(' and any other argument returns the length of the group. +" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be +" executed, having the effect of +" :let foo = s:Ref(string, d, "start") +" :let bar = s:Ref(string, d, "len") +fun! s:Ref(string, d, ...) + let len = strlen(a:string) + if a:d == 0 + let start = 0 + else + let cnt = a:d + let match = a:string + while cnt + let cnt = cnt - 1 + let index = matchend(match, s:notslash . '\\(') + if index == -1 + return "" + endif + let match = strpart(match, index) + endwhile + let start = len - strlen(match) + if a:0 == 1 && a:1 == "start" + return start - 2 + endif + let cnt = 1 + while cnt + let index = matchend(match, s:notslash . '\\(\|\\)') - 1 + if index == -2 + return "" + endif + " Increment if an open, decrement if a ')': + let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')' + " let cnt = stridx('0(', match[index]) + cnt + let match = strpart(match, index+1) + endwhile + let start = start - 2 + let len = len - start - strlen(match) + endif + if a:0 == 1 + return len + elseif a:0 == 2 + return "let " . a:1 . "=" . start . "| let " . a:2 . "=" . len + else + return strpart(a:string, start, len) + endif +endfun + +" Count the number of disjoint copies of pattern in string. +" If the pattern is a literal string and contains no '0' or '1' characters +" then s:Count(string, pattern, '0', '1') should be faster than +" s:Count(string, pattern). +fun! s:Count(string, pattern, ...) + let pat = escape(a:pattern, '\\') + if a:0 > 1 + let foo = substitute(a:string, '[^'.a:pattern.']', "a:1", "g") + let foo = substitute(a:string, pat, a:2, "g") + let foo = substitute(foo, '[^' . a:2 . ']', "", "g") + return strlen(foo) + endif + let result = 0 + let foo = a:string + let index = matchend(foo, pat) + while index != -1 + let result = result + 1 + let foo = strpart(foo, index) + let index = matchend(foo, pat) + endwhile + return result +endfun + +" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where +" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first +" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this +" indicates that all other instances of '\1' in target are to be replaced +" by '\3'. The hard part is dealing with nesting... +" Note that ":" is an illegal character for source and target, +" unless it is preceded by "\". +fun! s:Resolve(source, target, output) + let word = a:target + let i = matchend(word, s:notslash . '\\\d') - 1 + let table = "----------" + while i != -2 " There are back references to be replaced. + let d = word[i] + let backref = s:Ref(a:source, d) + " The idea is to replace '\d' with backref. Before we do this, + " replace any \(\) groups in backref with :1, :2, ... if they + " correspond to the first, second, ... group already inserted + " into backref. Later, replace :1 with \1 and so on. The group + " number w+b within backref corresponds to the group number + " s within a:source. + " w = number of '\(' in word before the current one + let w = s:Count( + \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1') + let b = 1 " number of the current '\(' in backref + let s = d " number of the current '\(' in a:source + while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1') + \ && s < 10 + if table[s] == "-" + if w + b < 10 + " let table[s] = w + b + let table = strpart(table, 0, s) . (w+b) . strpart(table, s+1) + endif + let b = b + 1 + let s = s + 1 + else + execute s:Ref(backref, b, "start", "len") + let ref = strpart(backref, start, len) + let backref = strpart(backref, 0, start) . ":". table[s] + \ . strpart(backref, start+len) + let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1') + endif + endwhile + let word = strpart(word, 0, i-1) . backref . strpart(word, i+1) + let i = matchend(word, s:notslash . '\\\d') - 1 + endwhile + let word = substitute(word, s:notslash . '\zs:', '\\', 'g') + if a:output == "table" + return table + elseif a:output == "word" + return word + else + return table . word + endif +endfun + +" Assume a:comma = ",". Then the format for a:patterns and a:1 is +" a:patterns = ",,..." +" a:1 = ",,..." +" If is the first pattern that matches a:string then return +" if no optional arguments are given; return , if a:1 is given. +fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) + let tail = (a:patterns =~ a:comma."$" ? a:patterns : a:patterns . a:comma) + let i = matchend(tail, s:notslash . a:comma) + if a:0 + let alttail = (a:1 =~ a:comma."$" ? a:1 : a:1 . a:comma) + let j = matchend(alttail, s:notslash . a:comma) + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') + endif + while a:string !~ a:prefix . currpat . a:suffix + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash . a:comma) + if i == -1 + return -1 + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, s:notslash . a:branch, '\\|', 'g') + endif + if a:0 + let alttail = strpart(alttail, j) + let j = matchend(alttail, s:notslash . a:comma) + endif + endwhile + if a:0 + let current = current . a:comma . strpart(alttail, 0, j-1) + endif + return current +endfun + +" Call this function to turn on debugging information. Every time the main +" script is run, buffer variables will be saved. These can be used directly +" or viewed using the menu items below. +if !exists(":MatchDebug") + command! -nargs=0 MatchDebug call s:Match_debug() +endif + +fun! s:Match_debug() + let b:match_debug = 1 " Save debugging information. + " pat = all of b:match_words with backrefs parsed + amenu &Matchit.&pat :echo b:match_pat + " match = bit of text that is recognized as a match + amenu &Matchit.&match :echo b:match_match + " curcol = cursor column of the start of the matching text + amenu &Matchit.&curcol :echo b:match_col + " wholeBR = matching group, original version + amenu &Matchit.wh&oleBR :echo b:match_wholeBR + " iniBR = 'if' piece, original version + amenu &Matchit.ini&BR :echo b:match_iniBR + " ini = 'if' piece, with all backrefs resolved from match + amenu &Matchit.&ini :echo b:match_ini + " tail = 'else\|endif' piece, with all backrefs resolved from match + amenu &Matchit.&tail :echo b:match_tail + " fin = 'endif' piece, with all backrefs resolved from match + amenu &Matchit.&word :echo b:match_word + " '\'.d in ini refers to the same thing as '\'.table[d] in word. + amenu &Matchit.t&able :echo '0:' . b:match_table . ':9' +endfun + +" Jump to the nearest unmatched "(" or "if" or "" if a:spflag == "bW" +" or the nearest unmatched "" or "endif" or ")" if a:spflag == "W". +" Return a "mark" for the original position, so that +" let m = MultiMatch("bW", "n") ... execute m +" will return to the original position. If there is a problem, do not +" move the cursor and return "", unless a count is given, in which case +" go up or down as many levels as possible and again return "". +" TODO This relies on the same patterns as % matching. It might be a good +" idea to give it its own matching patterns. +fun! s:MultiMatch(spflag, mode) + if !exists("b:match_words") || b:match_words == "" + return "" + end + let restore_options = (&ic ? "" : "no") . "ignorecase" + if exists("b:match_ignorecase") + let &ignorecase = b:match_ignorecase + endif + let startline = line(".") + let startcol = col(".") + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + " This part is copied and slightly modified from s:Match_wrapper(). + let default = escape(&mps, '[$^.*~\\/?]') . (strlen(&mps) ? "," : "") . + \ '\/\*:\*\/,#\s*if\%(def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + " Allow b:match_words = "GetVimMatchWords()" . + if b:match_words =~ ":" + let match_words = b:match_words + else + execute "let match_words =" b:match_words + endif + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let s:last_words = match_words + let s:last_mps = &mps + if match_words !~ s:notslash . '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + let s:all = '\%(' . substitute(s:pat . (strlen(s:pat)?",":"") . default, + \ '[,:]\+','\\|','g') . '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + endif + + " Second step: figure out the patterns for searchpair() + " and save the screen, cursor position, and 'ignorecase'. + " - TODO: A lot of this is copied from s:Match_wrapper(). + " - maybe even more functionality should be split off + " - into separate functions! + let cdefault = (s:pat =~ '[^,]$' ? "," : "") . default + let open = substitute(s:pat . cdefault, + \ s:notslash . '\zs:.\{-}' . s:notslash . ',', '\\),\\(', 'g') + let open = '\(' . substitute(open, s:notslash . '\zs:.*$', '\\)', '') + let close = substitute(s:pat . cdefault, + \ s:notslash . '\zs,.\{-}' . s:notslash . ':', '\\),\\(', 'g') + let close = substitute(close, '^.\{-}' . s:notslash . ':', '\\(', '') . '\)' + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" . b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + " let restore_cursor = line(".") . "G" . virtcol(".") . "|" + " normal! H + " let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + let restore_cursor = virtcol(".") . "|" + normal! g0 + let restore_cursor = line(".") . "G" . virtcol(".") . "|zs" . restore_cursor + normal! H + let restore_cursor = "normal!" . line(".") . "Gzt" . restore_cursor + execute restore_cursor + + " Third step: call searchpair(). + " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. + let openpat = substitute(open, '\(\\\@" or ... +" and return "endif" or "endwhile" or "" or ... . +" For now, this uses b:match_words and the same script variables +" as s:Match_wrapper() . Later, it may get its own patterns, +" either from a buffer variable or passed as arguments. +" fun! s:Autocomplete() +" echo "autocomplete not yet implemented :-(" +" if !exists("b:match_words") || b:match_words == "" +" return "" +" end +" let startpos = s:MultiMatch("bW") +" +" if startpos == "" +" return "" +" endif +" " - TODO: figure out whether 'if' or '' matched, and construct +" " - the appropriate closing. +" let matchline = getline(".") +" let curcol = col(".") - 1 +" " - TODO: Change the s:all argument if there is a new set of match pats. +" let regexp = s:Wholematch(matchline, s:all, curcol) +" let suf = strlen(matchline) - matchend(matchline, regexp) +" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') +" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') +" " Reconstruct the version with unresolved backrefs. +" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g') +" let patBR = substitute(patBR, ':\{2,}', ':', "g") +" " Now, set group and groupBR to the matching group: 'if:endif' or +" " 'while:endwhile' or whatever. +" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) +" let i = matchend(group, s:notslash . ",") +" let groupBR = strpart(group, i) +" let group = strpart(group, 0, i-1) +" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix +" if s:do_BR +" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) +" endif +" " let g:group = group +" +" " - TODO: Construct the closing from group. +" let fake = "end" . expand("") +" execute startpos +" return fake +" endfun + +" Close all open structures. "Get the heck out of here!" +" fun! s:Gthhoh() +" let close = s:Autocomplete() +" while strlen(close) +" put=close +" let close = s:Autocomplete() +" endwhile +" endfun + +" Parse special strings as typical skip arguments for searchpair(): +" s:foo becomes (current syntax item) =~ foo +" S:foo becomes (current syntax item) !~ foo +" r:foo becomes (line before cursor) =~ foo +" R:foo becomes (line before cursor) !~ foo +fun! s:ParseSkip(str) + let skip = a:str + if skip[1] == ":" + if skip[0] == "s" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" . + \ strpart(skip,2) . "'" + elseif skip[0] == "S" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" . + \ strpart(skip,2) . "'" + elseif skip[0] == "r" + let skip = "strpart(getline('.'),0,col('.'))=~'" . strpart(skip,2). "'" + elseif skip[0] == "R" + let skip = "strpart(getline('.'),0,col('.'))!~'" . strpart(skip,2). "'" + endif + endif + return skip +endfun + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:sts=2:sw=2: diff --git a/runtime/vimrc_example.vim b/runtime/vimrc_example.vim index f75dee3369..9cb66ee270 100644 --- a/runtime/vimrc_example.vim +++ b/runtime/vimrc_example.vim @@ -1,7 +1,7 @@ " An example for a vimrc file. " " Maintainer: Bram Moolenaar -" Last change: 2015 Mar 24 +" Last change: 2016 Mar 25 " " To use it, copy it to " for Unix and OS/2: ~/.vimrc @@ -100,3 +100,10 @@ if has('langmap') && exists('+langnoremap') " compatible). set langnoremap endif + + +" Add optional packages. +" +" The matchit plugin makes the % command work better, but it is not backwards +" compatible. +packadd matchit diff --git a/src/Makefile b/src/Makefile index 1cfdd9ab5e..002ce577e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -130,6 +130,7 @@ # make installlinks only installs the Vim binary links # make installmanlinks only installs the Vim manpage links # make installmacros only installs the Vim macros +# make installpack only installs the packages # make installtutorbin only installs the Vim tutor program # make installtutor only installs the Vim tutor files # make installspell only installs the spell files @@ -1008,6 +1009,7 @@ LANGSUBDIR = /lang COMPSUBDIR = /compiler KMAPSUBDIR = /keymap MACROSUBDIR = /macros +PACKSUBDIR = /pack TOOLSSUBDIR = /tools TUTORSUBDIR = /tutor SPELLSUBDIR = /spell @@ -1029,6 +1031,7 @@ PODIR = po ### COMPSUBLOC location for compiler files ### KMAPSUBLOC location for keymap files ### MACROSUBLOC location for macro files +### PACKSUBLOC location for packages ### TOOLSSUBLOC location for tools files ### TUTORSUBLOC location for tutor files ### SPELLSUBLOC location for spell files @@ -1050,6 +1053,7 @@ LANGSUBLOC = $(VIMRTLOC)$(LANGSUBDIR) COMPSUBLOC = $(VIMRTLOC)$(COMPSUBDIR) KMAPSUBLOC = $(VIMRTLOC)$(KMAPSUBDIR) MACROSUBLOC = $(VIMRTLOC)$(MACROSUBDIR) +PACKSUBLOC = $(VIMRTLOC)$(PACKSUBDIR) TOOLSSUBLOC = $(VIMRTLOC)$(TOOLSSUBDIR) TUTORSUBLOC = $(VIMRTLOC)$(TUTORSUBDIR) SPELLSUBLOC = $(VIMRTLOC)$(SPELLSUBDIR) @@ -1155,6 +1159,9 @@ FTPLUGSOURCE = ../runtime/ftplugin # Where to copy the macro files from MACROSOURCE = ../runtime/macros +# Where to copy the package files from +PACKSOURCE = ../runtime/pack + # Where to copy the tools files from TOOLSSOURCE = ../runtime/tools @@ -1447,6 +1454,7 @@ DEST_LANG = $(DESTDIR)$(LANGSUBLOC) DEST_COMP = $(DESTDIR)$(COMPSUBLOC) DEST_KMAP = $(DESTDIR)$(KMAPSUBLOC) DEST_MACRO = $(DESTDIR)$(MACROSUBLOC) +DEST_PACK = $(DESTDIR)$(PACKSUBLOC) DEST_TOOLS = $(DESTDIR)$(TOOLSSUBLOC) DEST_TUTOR = $(DESTDIR)$(TUTORSUBLOC) DEST_SPELL = $(DESTDIR)$(SPELLSUBLOC) @@ -2124,7 +2132,7 @@ INSTALLMANARGS = $(VIMLOC) $(SCRIPTLOC) $(VIMRCLOC) $(HELPSOURCE) $(MANMOD) \ $(VIMNAME) $(VIMDIFFNAME) $(EVIMNAME) # Install most of the runtime files -installruntime: installrtbase installmacros installtutor installspell +installruntime: installrtbase installmacros installpack installtutor installspell # install the help files; first adjust the contents for the final location installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(DEST_RT) \ @@ -2223,6 +2231,11 @@ installmacros: $(DEST_VIM) $(DEST_RT) $(DEST_MACRO) rm -rf $$cvs; \ fi +installpack: $(DEST_VIM) $(DEST_RT) $(DEST_PACK) + $(INSTALL_DATA_R) $(PACKSOURCE)/* $(DEST_PACK) + chmod $(DIRMOD) `find $(DEST_PACK) -type d -print` + chmod $(FILEMOD) `find $(DEST_PACK) -type f -print` + # install the tutor files installtutorbin: $(DEST_VIM) $(INSTALL_DATA) vimtutor $(DEST_BIN)/$(VIMNAME)tutor @@ -2372,8 +2385,8 @@ $(HELPSOURCE)/vim.1 $(MACROSOURCE) $(TOOLSSOURCE): $(DESTDIR)$(exec_prefix) $(DEST_BIN) \ $(DEST_VIM) $(DEST_RT) $(DEST_HELP) \ $(DEST_PRINT) $(DEST_COL) $(DEST_SYN) $(DEST_IND) $(DEST_FTP) \ - $(DEST_LANG) $(DEST_KMAP) $(DEST_COMP) \ - $(DEST_MACRO) $(DEST_TOOLS) $(DEST_TUTOR) $(DEST_SPELL) \ + $(DEST_LANG) $(DEST_KMAP) $(DEST_COMP) $(DEST_MACRO) \ + $(DEST_PACK) $(DEST_TOOLS) $(DEST_TUTOR) $(DEST_SPELL) \ $(DEST_AUTO) $(DEST_AUTO)/xml $(DEST_PLUG): -$(SHELL) ./mkinstalldirs $@ -chmod $(DIRMOD) $@ @@ -2518,6 +2531,7 @@ uninstall_runtime: -rm -f $(DEST_SYN)/*.vim $(DEST_SYN)/README.txt -rm -f $(DEST_IND)/*.vim $(DEST_IND)/README.txt -rm -rf $(DEST_MACRO) + -rm -rf $(DEST_PACK) -rm -rf $(DEST_TUTOR) -rm -rf $(DEST_SPELL) -rm -rf $(DEST_TOOLS) diff --git a/src/eval.c b/src/eval.c index ed1b004930..2b50a5ccb8 100644 --- a/src/eval.c +++ b/src/eval.c @@ -292,13 +292,12 @@ typedef struct #define VV_RO 2 /* read-only */ #define VV_RO_SBX 4 /* read-only in the sandbox */ -#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}}, {0} +#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}} static struct vimvar { char *vv_name; /* name of variable, without v: */ - dictitem_T vv_di; /* value and name for key */ - char vv_filler[16]; /* space for LONGEST name below!!! */ + dictitem16_T vv_di; /* value and name for key (max 16 chars!) */ char vv_flags; /* VV_COMPAT, VV_RO, VV_RO_SBX */ } vimvars[VV_LEN] = { @@ -26467,9 +26466,11 @@ repeat: { /* vim_strsave_shellescape() needs a NUL terminated string. */ c = (*fnamep)[*fnamelen]; - (*fnamep)[*fnamelen] = NUL; + if (c != NUL) + (*fnamep)[*fnamelen] = NUL; p = vim_strsave_shellescape(*fnamep, FALSE, FALSE); - (*fnamep)[*fnamelen] = c; + if (c != NUL) + (*fnamep)[*fnamelen] = c; if (p == NULL) return -1; vim_free(*bufp); diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c index 6376fb5c0e..9dc0dbdb03 100644 --- a/src/if_xcmdsrv.c +++ b/src/if_xcmdsrv.c @@ -607,6 +607,7 @@ ServerWait( { while (XCheckWindowEvent(dpy, commWindow, PropertyChangeMask, &event)) serverEventProc(dpy, &event, 1); + server_parse_messages(); if (endCond(endData) != 0) break; diff --git a/src/misc1.c b/src/misc1.c index 1ab6ff4dfd..21bc9787e4 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -9743,18 +9743,6 @@ pstrcmp(const void *a, const void *b) return (pathcmp(*(char **)a, *(char **)b, -1)); } -# ifndef WIN3264 - static void -namelowcpy( - char_u *d, - char_u *s) -{ - while (*s) - *d++ = TOLOWER_LOC(*s++); - *d = NUL; -} -# endif - /* * Recursively expand one path component into all matching files and/or * directories. Adds matches to "gap". Handles "*", "?", "[a-z]", "**", etc. @@ -9783,16 +9771,12 @@ dos_expandpath( int len; int starstar = FALSE; static int stardepth = 0; /* depth for "**" expansion */ -#ifdef WIN3264 WIN32_FIND_DATA fb; HANDLE hFind = (HANDLE)0; # ifdef FEAT_MBYTE WIN32_FIND_DATAW wfb; WCHAR *wn = NULL; /* UCS-2 name, NULL when not used. */ # endif -#else - struct ffblk fb; -#endif char_u *matchname; int ok; @@ -9833,7 +9817,7 @@ dos_expandpath( else if (path_end >= path + wildoff && vim_strchr((char_u *)"*?[~", *path_end) != NULL) e = p; -#ifdef FEAT_MBYTE +# ifdef FEAT_MBYTE if (has_mbyte) { len = (*mb_ptr2len)(path_end); @@ -9842,7 +9826,7 @@ dos_expandpath( path_end += len; } else -#endif +# endif *p++ = *path_end++; } e = p; @@ -9903,7 +9887,6 @@ dos_expandpath( /* Scan all files in the directory with "dir/ *.*" */ STRCPY(s, "*.*"); -#ifdef WIN3264 # ifdef FEAT_MBYTE if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { @@ -9927,24 +9910,15 @@ dos_expandpath( # endif hFind = FindFirstFile((LPCSTR)buf, &fb); ok = (hFind != INVALID_HANDLE_VALUE); -#else - /* If we are expanding wildcards we try both files and directories */ - ok = (findfirst((char *)buf, &fb, - (*path_end != NUL || (flags & EW_DIR)) ? FA_DIREC : 0) == 0); -#endif while (ok) { -#ifdef WIN3264 # ifdef FEAT_MBYTE if (wn != NULL) p = utf16_to_enc(wfb.cFileName, NULL); /* p is allocated here */ else # endif p = (char_u *)fb.cFileName; -#else - p = (char_u *)fb.ff_name; -#endif /* Ignore entries starting with a dot, unless when asked for. Accept * all entries found with "matchname". */ if ((p[0] != '.' || starts_with_dot @@ -9956,11 +9930,7 @@ dos_expandpath( || ((flags & EW_NOTWILD) && fnamencmp(path + (s - buf), p, e - s) == 0))) { -#ifdef WIN3264 STRCPY(s, p); -#else - namelowcpy(s, p); -#endif len = (int)STRLEN(buf); if (starstar && stardepth < 100) @@ -9992,7 +9962,6 @@ dos_expandpath( } } -#ifdef WIN3264 # ifdef FEAT_MBYTE if (wn != NULL) { @@ -10002,16 +9971,12 @@ dos_expandpath( else # endif ok = FindNextFile(hFind, &fb); -#else - ok = (findnext(&fb) == 0); -#endif /* If no more matches and no match was used, try expanding the name * itself. Finds the long name of a short filename. */ if (!ok && matchname != NULL && gap->ga_len == start_len) { STRCPY(s, matchname); -#ifdef WIN3264 FindClose(hFind); # ifdef FEAT_MBYTE if (wn != NULL) @@ -10025,21 +9990,15 @@ dos_expandpath( # endif hFind = FindFirstFile((LPCSTR)buf, &fb); ok = (hFind != INVALID_HANDLE_VALUE); -#else - ok = (findfirst((char *)buf, &fb, - (*path_end != NUL || (flags & EW_DIR)) ? FA_DIREC : 0) == 0); -#endif vim_free(matchname); matchname = NULL; } } -#ifdef WIN3264 FindClose(hFind); # ifdef FEAT_MBYTE vim_free(wn); # endif -#endif vim_free(buf); vim_regfree(regmatch.regprog); vim_free(matchname); diff --git a/src/quickfix.c b/src/quickfix.c index a2506e1a32..c2ff55a8c0 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -1027,6 +1027,8 @@ qf_add_entry( /* first element in the list */ { qi->qf_lists[qi->qf_curlist].qf_start = qfp; + qi->qf_lists[qi->qf_curlist].qf_ptr = qfp; + qi->qf_lists[qi->qf_curlist].qf_index = 0; qfp->qf_prev = qfp; /* first element points to itself */ } else @@ -4113,7 +4115,8 @@ set_errorlist( else qi->qf_lists[qi->qf_curlist].qf_nonevalid = FALSE; qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start; - qi->qf_lists[qi->qf_curlist].qf_index = 1; + if (qi->qf_lists[qi->qf_curlist].qf_count > 0) + qi->qf_lists[qi->qf_curlist].qf_index = 1; #ifdef FEAT_WINDOWS qf_update_buffer(qi); diff --git a/src/structs.h b/src/structs.h index 63295454b0..a970c68fad 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1221,6 +1221,15 @@ struct dictitem_S }; typedef struct dictitem_S dictitem_T; +/* A dictitem with a 16 character key (plus NUL). */ +struct dictitem16_S +{ + typval_T di_tv; /* type and value of the variable */ + char_u di_flags; /* flags (only used for variable) */ + char_u di_key[17]; /* key */ +}; +typedef struct dictitem16_S dictitem16_T; + #define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */ #define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */ #define DI_FLAGS_FIX 4 /* "di_flags" value: fixed: no :unlet or remove() */ diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 455ca7b61c..98cf98562f 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -85,7 +85,6 @@ SCRIPTS_ALL = \ test102.out \ test103.out \ test104.out \ - test105.out \ test107.out \ test108.out \ test_autocmd_option.out \ diff --git a/src/testdir/test105.in b/src/testdir/test105.in deleted file mode 100644 index 4cc0261445..0000000000 --- a/src/testdir/test105.in +++ /dev/null @@ -1,47 +0,0 @@ -Test filename modifiers vim: set ft=vim : - -STARTTEST -:source small.vim -:%delete _ -:set shell=sh -:set shellslash -:let tab="\t" -:command -nargs=1 Put :let expr= | $put =expr.tab.strtrans(string(eval(expr))) -:let $HOME=fnamemodify('.', ':p:h:h') -:Put fnamemodify('.', ':p' )[-1:] -:Put fnamemodify('.', ':p:h' )[-1:] -:Put fnamemodify('test.out', ':p' )[-1:] -:Put fnamemodify('test.out', ':.' ) -:Put fnamemodify('../testdir/a', ':.' ) -:Put fnamemodify('test.out', ':~' ) -:Put fnamemodify('../testdir/a', ':~' ) -:Put fnamemodify('../testdir/a', ':t' ) -:Put fnamemodify('.', ':p:t' ) -:Put fnamemodify('test.out', ':p:t' ) -:Put fnamemodify('test.out', ':p:e' ) -:Put fnamemodify('test.out', ':p:t:e' ) -:Put fnamemodify('abc.fb2.tar.gz', ':r' ) -:Put fnamemodify('abc.fb2.tar.gz', ':r:r' ) -:Put fnamemodify('abc.fb2.tar.gz', ':r:r:r' ) -:Put substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(testdir/.*\)', '\1', '') -:Put fnamemodify('abc.fb2.tar.gz', ':e' ) -:Put fnamemodify('abc.fb2.tar.gz', ':e:e' ) -:Put fnamemodify('abc.fb2.tar.gz', ':e:e:e' ) -:Put fnamemodify('abc.fb2.tar.gz', ':e:e:e:e') -:Put fnamemodify('abc.fb2.tar.gz', ':e:e:r' ) -:Put fnamemodify('abc def', ':S' ) -:Put fnamemodify('abc" "def', ':S' ) -:Put fnamemodify('abc"%"def', ':S' ) -:Put fnamemodify('abc'' ''def', ':S' ) -:Put fnamemodify('abc''%''def', ':S' ) -:Put fnamemodify("abc\ndef", ':S' ) -:Put expand('%:r:S') == shellescape(expand('%:r')) -:Put join([expand('%:r'), expand('%:r:S'), expand('%')], ',') -:set shell=tcsh -:Put fnamemodify("abc\ndef", ':S' ) -:$put ='vim: ts=8' -:1 delete _ -:w! test.out -:qa! -ENDTEST - diff --git a/src/testdir/test105.ok b/src/testdir/test105.ok deleted file mode 100644 index 6642bfe4fb..0000000000 --- a/src/testdir/test105.ok +++ /dev/null @@ -1,31 +0,0 @@ -fnamemodify('.', ':p' )[-1:] '/' -fnamemodify('.', ':p:h' )[-1:] 'r' -fnamemodify('test.out', ':p' )[-1:] 't' -fnamemodify('test.out', ':.' ) 'test.out' -fnamemodify('../testdir/a', ':.' ) 'a' -fnamemodify('test.out', ':~' ) '~/testdir/test.out' -fnamemodify('../testdir/a', ':~' ) '~/testdir/a' -fnamemodify('../testdir/a', ':t' ) 'a' -fnamemodify('.', ':p:t' ) '' -fnamemodify('test.out', ':p:t' ) 'test.out' -fnamemodify('test.out', ':p:e' ) 'out' -fnamemodify('test.out', ':p:t:e' ) 'out' -fnamemodify('abc.fb2.tar.gz', ':r' ) 'abc.fb2.tar' -fnamemodify('abc.fb2.tar.gz', ':r:r' ) 'abc.fb2' -fnamemodify('abc.fb2.tar.gz', ':r:r:r' ) 'abc' -substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(testdir/.*\)', '\1', '') 'testdir/abc.fb2' -fnamemodify('abc.fb2.tar.gz', ':e' ) 'gz' -fnamemodify('abc.fb2.tar.gz', ':e:e' ) 'tar.gz' -fnamemodify('abc.fb2.tar.gz', ':e:e:e' ) 'fb2.tar.gz' -fnamemodify('abc.fb2.tar.gz', ':e:e:e:e') 'fb2.tar.gz' -fnamemodify('abc.fb2.tar.gz', ':e:e:r' ) 'tar' -fnamemodify('abc def', ':S' ) '''abc def''' -fnamemodify('abc" "def', ':S' ) '''abc" "def''' -fnamemodify('abc"%"def', ':S' ) '''abc"%"def''' -fnamemodify('abc'' ''def', ':S' ) '''abc''\'''' ''\''''def''' -fnamemodify('abc''%''def', ':S' ) '''abc''\''''%''\''''def''' -fnamemodify("abc\ndef", ':S' ) '''abc^@def''' -expand('%:r:S') == shellescape(expand('%:r')) 1 -join([expand('%:r'), expand('%:r:S'), expand('%')], ',') 'test105,''test105'',test105.in' -fnamemodify("abc\ndef", ':S' ) '''abc\^@def''' -vim: ts=8 diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index 37cdc8b605..fcf358b534 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -8,6 +8,7 @@ source test_ex_undo.vim source test_expr.vim source test_expand.vim source test_feedkeys.vim +source test_fnamemodify.vim source test_file_perm.vim source test_glob2regpat.vim source test_help_tagjump.vim diff --git a/src/testdir/test_fnamemodify.vim b/src/testdir/test_fnamemodify.vim new file mode 100644 index 0000000000..2267e18e3b --- /dev/null +++ b/src/testdir/test_fnamemodify.vim @@ -0,0 +1,49 @@ +" Test filename modifiers. + +func Test_fnamemodify() + let $HOME = fnamemodify('.', ':p:h:h') + set shell=sh + + call assert_equal('/', fnamemodify('.', ':p')[-1:]) + call assert_equal('r', fnamemodify('.', ':p:h')[-1:]) + call assert_equal('t', fnamemodify('test.out', ':p')[-1:]) + call assert_equal('test.out', fnamemodify('test.out', ':.')) + call assert_equal('a', fnamemodify('../testdir/a', ':.')) + call assert_equal('~/testdir/test.out', fnamemodify('test.out', ':~')) + call assert_equal('~/testdir/a', fnamemodify('../testdir/a', ':~')) + call assert_equal('a', fnamemodify('../testdir/a', ':t')) + call assert_equal('', fnamemodify('.', ':p:t')) + call assert_equal('test.out', fnamemodify('test.out', ':p:t')) + call assert_equal('out', fnamemodify('test.out', ':p:e')) + call assert_equal('out', fnamemodify('test.out', ':p:t:e')) + call assert_equal('abc.fb2.tar', fnamemodify('abc.fb2.tar.gz', ':r')) + call assert_equal('abc.fb2', fnamemodify('abc.fb2.tar.gz', ':r:r')) + call assert_equal('abc', fnamemodify('abc.fb2.tar.gz', ':r:r:r')) + call assert_equal('testdir/abc.fb2', substitute(fnamemodify('abc.fb2.tar.gz', ':p:r:r'), '.*\(testdir/.*\)', '\1', '')) + call assert_equal('gz', fnamemodify('abc.fb2.tar.gz', ':e')) + call assert_equal('tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e')) + call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) + call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + + call assert_equal('''abc def''', fnamemodify('abc def', ':S')) + call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S')) + call assert_equal('''abc"%"def''', fnamemodify('abc"%"def', ':S')) + call assert_equal('''abc''\'''' ''\''''def''', fnamemodify('abc'' ''def', ':S')) + call assert_equal('''abc''\''''%''\''''def''', fnamemodify('abc''%''def', ':S')) + call assert_equal(expand('%:r:S'), shellescape(expand('%:r'))) + sp test_alot.vim + call assert_equal('test_alot,''test_alot'',test_alot.vim', join([expand('%:r'), expand('%:r:S'), expand('%')], ',')) + quit + + call assert_equal("'abc\ndef'", fnamemodify("abc\ndef", ':S')) + set shell=tcsh + call assert_equal("'abc\\\ndef'", fnamemodify("abc\ndef", ':S')) + set shell& +endfunc + +func Test_expand() + new + call assert_equal("", expand('%:S')) + quit +endfunc diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 667ece4e67..3908ce0938 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -679,3 +679,16 @@ function Test_quickfix_was_changed_by_autocmd() call XquickfixChangedByAutocmd('c') call XquickfixChangedByAutocmd('l') endfunction + +func Test_caddbuffer_to_empty() + helpgr quickfix + call setqflist([], 'r') + cad + try + cn + catch + " number of matches is unknown + call assert_true(v:exception =~ 'E553:') + endtry + quit! +endfunc diff --git a/src/version.c b/src/version.c index 35831b843a..5001310396 100644 --- a/src/version.c +++ b/src/version.c @@ -763,6 +763,24 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1655, +/**/ + 1654, +/**/ + 1653, +/**/ + 1652, +/**/ + 1651, +/**/ + 1650, +/**/ + 1649, +/**/ + 1648, +/**/ + 1647, /**/ 1646, /**/