diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:09:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:09:20 +0000 |
commit | 029f72b1a93430b24b88eb3a72c6114d9f149737 (patch) | |
tree | 765d5c2041967f9c6fef195fe343d9234a030e90 /runtime/pack/dist | |
parent | Initial commit. (diff) | |
download | vim-029f72b1a93430b24b88eb3a72c6114d9f149737.tar.xz vim-029f72b1a93430b24b88eb3a72c6114d9f149737.zip |
Adding upstream version 2:9.1.0016.upstream/2%9.1.0016
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'runtime/pack/dist')
29 files changed, 6316 insertions, 0 deletions
diff --git a/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim b/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim new file mode 100644 index 0000000..7a71de4 --- /dev/null +++ b/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim @@ -0,0 +1,72 @@ +vim9script + +# cfilter.vim: Plugin to filter entries from a quickfix/location list +# Last Change: August 16, 2023 +# Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +# Version: 2.0 +# +# Commands to filter the quickfix list: +# :Cfilter[!] /{pat}/ +# Create a new quickfix list from entries matching {pat} in the current +# quickfix list. Both the file name and the text of the entries are +# matched against {pat}. If ! is supplied, then entries not matching +# {pat} are used. The pattern can be optionally enclosed using one of +# the following characters: ', ", /. If the pattern is empty, then the +# last used search pattern is used. +# :Lfilter[!] /{pat}/ +# Same as :Cfilter but operates on the current location list. +# + +def Qf_filter(qf: bool, searchpat: string, bang: string) + var Xgetlist: func + var Xsetlist: func + var cmd: string + var firstchar: string + var lastchar: string + var pat: string + var title: string + var Cond: func + var items: list<any> + + if qf + Xgetlist = function('getqflist') + Xsetlist = function('setqflist') + cmd = $':Cfilter{bang}' + else + Xgetlist = function('getloclist', [0]) + Xsetlist = function('setloclist', [0]) + cmd = $':Lfilter{bang}' + endif + + firstchar = searchpat[0] + lastchar = searchpat[-1 :] + if firstchar == lastchar && + (firstchar == '/' || firstchar == '"' || firstchar == "'") + pat = searchpat[1 : -2] + if pat == '' + # Use the last search pattern + pat = @/ + endif + else + pat = searchpat + endif + + if pat == '' + return + endif + + if bang == '!' + Cond = (_, val) => val.text !~# pat && bufname(val.bufnr) !~# pat + else + Cond = (_, val) => val.text =~# pat || bufname(val.bufnr) =~# pat + endif + + items = filter(Xgetlist(), Cond) + title = $'{cmd} /{pat}/' + Xsetlist([], ' ', {title: title, items: items}) +enddef + +command! -nargs=+ -bang Cfilter Qf_filter(true, <q-args>, <q-bang>) +command! -nargs=+ -bang Lfilter Qf_filter(false, <q-args>, <q-bang>) + +# vim: shiftwidth=2 sts=2 expandtab diff --git a/runtime/pack/dist/opt/dvorak/dvorak/disable.vim b/runtime/pack/dist/opt/dvorak/dvorak/disable.vim new file mode 100644 index 0000000..1e9b070 --- /dev/null +++ b/runtime/pack/dist/opt/dvorak/dvorak/disable.vim @@ -0,0 +1,72 @@ +" Back to Qwerty keyboard after using Dvorak. + +iunmap a +iunmap b +iunmap c +iunmap d +iunmap e +iunmap f +iunmap g +iunmap h +iunmap i +iunmap j +iunmap k +iunmap l +iunmap m +iunmap n +iunmap o +iunmap p +iunmap q +iunmap r +iunmap s +iunmap t +iunmap u +iunmap v +iunmap w +iunmap x +iunmap y +iunmap z +iunmap ; +iunmap ' +iunmap " +iunmap , +iunmap . +iunmap / +iunmap A +iunmap B +iunmap C +iunmap D +iunmap E +iunmap F +iunmap G +iunmap H +iunmap I +iunmap J +iunmap K +iunmap L +iunmap M +iunmap N +iunmap O +iunmap P +iunmap Q +iunmap R +iunmap S +iunmap T +iunmap U +iunmap V +iunmap W +iunmap X +iunmap Y +iunmap Z +iunmap < +iunmap > +iunmap ? +iunmap : +iunmap [ +iunmap ] +iunmap { +iunmap } +iunmap - +iunmap _ +iunmap = +iunmap + diff --git a/runtime/pack/dist/opt/dvorak/dvorak/enable.vim b/runtime/pack/dist/opt/dvorak/dvorak/enable.vim new file mode 100644 index 0000000..8ff363f --- /dev/null +++ b/runtime/pack/dist/opt/dvorak/dvorak/enable.vim @@ -0,0 +1,77 @@ +" Dvorak keyboard, only in Insert mode. +" +" Change "inoremap" to "map!" to also use in Ex mode. +" Also change disable.vim then: "iunmap" to "unmap!". +" +" You may want to add a list of map's too. + +inoremap a a +inoremap b x +inoremap c j +inoremap d e +inoremap e . +inoremap f u +inoremap g i +inoremap h d +inoremap i c +inoremap j h +inoremap k t +inoremap l n +inoremap m m +inoremap n b +inoremap o r +inoremap p l +inoremap q ' +inoremap r p +inoremap s o +inoremap t y +inoremap u g +inoremap v k +inoremap w , +inoremap x q +inoremap y f +inoremap z ; +inoremap ; s +inoremap ' - +inoremap " _ +inoremap , w +inoremap . v +inoremap / z +inoremap A A +inoremap B X +inoremap C J +inoremap D E +inoremap E > +inoremap F U +inoremap G I +inoremap H D +inoremap I C +inoremap J H +inoremap K T +inoremap L N +inoremap M M +inoremap N B +inoremap O R +inoremap P L +inoremap Q " +inoremap R P +inoremap S O +inoremap T Y +inoremap U G +inoremap V K +inoremap W < +inoremap X Q +inoremap Y F +inoremap Z : +inoremap < W +inoremap > V +inoremap ? Z +inoremap : S +inoremap [ / +inoremap ] = +inoremap { ? +inoremap } + +inoremap - [ +inoremap _ { +inoremap = ] +inoremap + } diff --git a/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim b/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim new file mode 100644 index 0000000..c8d5d5c --- /dev/null +++ b/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim @@ -0,0 +1,16 @@ +" When using a dvorak keyboard this file may be of help to you. +" These mappings have been made by Lawrence Kesteloot <kesteloo@cs.unc.edu>. +" What they do is that the most often used keys, like hjkl, are put in a more +" easy to use position. +" It may take some time to learn using this. + +if exists("g:loaded_dvorak_plugin") + finish +endif +let g:loaded_dvorak_plugin = 1 + +" Key to go into dvorak mode: +map ,d :runtime dvorak/enable.vim<CR> + +" Key to get out of dvorak mode: +map ,q :runtime dvorak/disable.vim<CR> diff --git a/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim b/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim new file mode 100644 index 0000000..52e80c1 --- /dev/null +++ b/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim @@ -0,0 +1,118 @@ +" Vim Plugin: Edit the file with an existing Vim if possible +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 13 + +" To use add ":packadd! editexisting" in your vimrc file. + +" This plugin serves two purposes: +" 1. On startup, if we were invoked with one file name argument and the file +" is not modified then try to find another Vim instance that is editing +" this file. If there is one then bring it to the foreground and exit. +" 2. When a file is edited and a swap file exists for it, try finding that +" other Vim and bring it to the foreground. Requires Vim 7, because it +" uses the SwapExists autocommand event. + +" Function that finds the Vim instance that is editing "filename" and brings +" it to the foreground. +func s:EditElsewhere(filename) + let fname_esc = substitute(a:filename, "'", "''", "g") + + let servers = serverlist() + while servers != '' + " Get next server name in "servername"; remove it from "servers". + let i = match(servers, "\n") + if i == -1 + let servername = servers + let servers = '' + else + let servername = strpart(servers, 0, i) + let servers = strpart(servers, i + 1) + endif + + " Skip ourselves. + if servername ==? v:servername + continue + endif + + " Check if this server is editing our file. + try + if remote_expr(servername, "bufloaded('" . fname_esc . "')") + " Yes, bring it to the foreground. + if has("win32") + call remote_foreground(servername) + endif + call remote_expr(servername, "foreground()") + + if remote_expr(servername, "exists('*EditExisting')") + " Make sure the file is visible in a window (not hidden). + " If v:swapcommand exists and is set, send it to the server. + if exists("v:swapcommand") + let c = substitute(v:swapcommand, "'", "''", "g") + call remote_expr(servername, "EditExisting('" . fname_esc . "', '" . c . "')") + else + call remote_expr(servername, "EditExisting('" . fname_esc . "', '')") + endif + endif + + if !(has('vim_starting') && has('gui_running') && has('gui_win32')) + " Tell the user what is happening. Not when the GUI is starting + " though, it would result in a message box. + echomsg "File is being edited by " . servername + sleep 2 + endif + return 'q' + endif + catch /^Vim\%((\a\+)\)\=:E241:/ + " Unable to send to this server, ignore it. + endtry + endwhile + return '' +endfunc + +" When the plugin is loaded and there is one file name argument: Find another +" Vim server that is editing this file right now. +if argc() == 1 && !&modified + if s:EditElsewhere(expand("%:p")) == 'q' + quit + endif +endif + +" Setup for handling the situation that an existing swap file is found. +try + au! SwapExists * let v:swapchoice = s:EditElsewhere(expand("<afile>:p")) +catch + " Without SwapExists we don't do anything for ":edit" commands +endtry + +" Function used on the server to make the file visible and possibly execute a +" command. +func! EditExisting(fname, command) + " Get the window number of the file in the current tab page. + let winnr = bufwinnr(a:fname) + if winnr <= 0 + " Not found, look in other tab pages. + let bufnr = bufnr(a:fname) + for i in range(tabpagenr('$')) + if index(tabpagebuflist(i + 1), bufnr) >= 0 + " Make this tab page the current one and find the window number. + exe 'tabnext ' . (i + 1) + let winnr = bufwinnr(a:fname) + break + endif + endfor + endif + + if winnr > 0 + exe winnr . "wincmd w" + elseif exists('*fnameescape') + exe "split " . fnameescape(a:fname) + else + exe "split " . escape(a:fname, " \t\n*?[{`$\\%#'\"|!<") + endif + + if a:command != '' + exe "normal! " . a:command + endif + + redraw +endfunc diff --git a/runtime/pack/dist/opt/editorconfig/.editorconfig b/runtime/pack/dist/opt/editorconfig/.editorconfig new file mode 100644 index 0000000..7eed9e1 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +max_line_length = 80 + +[*.{vim,sh}] +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 + +[*.rb] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 120 + +[*.yml] +indent_style = space +indent_size = 2 + +[*.{bat,vbs,ps1}] +end_of_line = CRLF diff --git a/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS b/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS new file mode 100644 index 0000000..b799668 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS @@ -0,0 +1,6 @@ +Contributors to the EditorConfig Vim Plugin: + +Hong Xu +Trey Hunner +Kent Frazier +Chris White diff --git a/runtime/pack/dist/opt/editorconfig/LICENSE b/runtime/pack/dist/opt/editorconfig/LICENSE new file mode 100644 index 0000000..ed9286e --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/LICENSE @@ -0,0 +1,26 @@ +Unless otherwise stated, all files are distributed under the Simplified BSD +license included below. + +Copyright (c) 2011-2019 EditorConfig Team +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/runtime/pack/dist/opt/editorconfig/LICENSE.PSF b/runtime/pack/dist/opt/editorconfig/LICENSE.PSF new file mode 100755 index 0000000..36eb8e0 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/LICENSE.PSF @@ -0,0 +1,53 @@ +Some code in editorconfig-vim is derived from code licensed under the +PSF license. The following is the text of that license, retrieved 2019-05-05 +from https://docs.python.org/2.6/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python + +PSF LICENSE AGREEMENT FOR PYTHON 2.6.9 + +1. This LICENSE AGREEMENT is between the Python Software Foundation +(``PSF''), and the Individual or Organization (``Licensee'') accessing and +otherwise using Python 2.6.9 software in source or binary form and its +associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 2.6.9 +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., ``Copyright (c) +2001-2010 Python Software Foundation; All Rights Reserved'' are +retained in Python 2.6.9 alone or in any derivative version prepared +by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 2.6.9 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 2.6.9. + +4. PSF is making Python 2.6.9 available to Licensee on an ``AS IS'' +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY +REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY +PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.6.9 WILL NOT INFRINGE +ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +2.6.9 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.6.9, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python 2.6.9, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +# vi: set ft=: diff --git a/runtime/pack/dist/opt/editorconfig/README.md b/runtime/pack/dist/opt/editorconfig/README.md new file mode 100644 index 0000000..961c9ae --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/README.md @@ -0,0 +1,148 @@ +# EditorConfig Vim Plugin + +[![Travis Build Status](https://img.shields.io/travis/cxw42/editorconfig-vim.svg?logo=travis)](https://travis-ci.org/editorconfig/editorconfig-vim) +[![Appveyor Build Status](https://img.shields.io/appveyor/ci/cxw42/editorconfig-vim.svg?logo=appveyor)](https://ci.appveyor.com/project/cxw42/editorconfig-vim) + +This is an [EditorConfig][] plugin for Vim. This plugin can be found on both +[GitHub][] and [Vim online][]. + +## Installation + +To install this plugin, you can use one of the following ways: + +### Install with the archive + +Download the [archive][] and extract it into your Vim runtime directory +(`~/.vim` on UNIX/Linux and `$VIM_INSTALLATION_FOLDER\vimfiles` on windows). +You should have 4 sub-directories in this runtime directory now: "autoload", +"doc", "ftdetect" and "plugin". + +### Install as Vim8 plugin + +Install as a Vim 8 plugin. Note `local` can be any name, but some path +element must be present. On Windows, instead of `~/.vim` use +`$VIM_INSTALLATION_FOLDER\vimfiles`. +```shell +mkdir -p ~/.vim/pack/local/start +cd ~/.vim/pack/local/start +git clone https://github.com/editorconfig/editorconfig-vim.git +``` + +### Install with [pathogen][] + +Use pathogen (the git repository of this plugin is +https://github.com/editorconfig/editorconfig-vim.git) + +### Install with [Vundle][] + +Use Vundle by adding to your `.vimrc` Vundle plugins section: + +```viml +Plugin 'editorconfig/editorconfig-vim' +``` + +Then call `:PluginInstall`. + +### Install with [vim-plug][] + +Use vim-plug by adding to your `.vimrc` in your plugin section: + +```viml +Plug 'editorconfig/editorconfig-vim' +``` + +Source your `.vimrc` by calling `:source $MYVIMRC`. + +Then call `:PlugInstall`. + +### No external editorconfig core library is required + +Previous versions of this plugin also required a Python "core". +The core included the code to parse `.editorconfig` files. +This plugin **includes** the core, so you don't need to download the +core separately. + +## Supported properties + +The EditorConfig Vim plugin supports the following EditorConfig [properties][]: + +* `indent_style` +* `indent_size` +* `tab_width` +* `end_of_line` +* `charset` +* `insert_final_newline` (Feature `+fixendofline`, available on Vim 7.4.785+, + or [PreserveNoEOL][] is required for this property) +* `trim_trailing_whitespace` +* `max_line_length` +* `root` (only used by EditorConfig core) + +## Selected Options + +The supported options are documented in [editorconfig.txt][] +and can be viewed by executing the following: `:help editorconfig`. You may +need to execute `:helptags ALL` so that Vim is aware of editorconfig.txt. + +### Excluded patterns + +To ensure that this plugin works well with [Tim Pope's fugitive][], use the +following patterns array: + +```viml +let g:EditorConfig_exclude_patterns = ['fugitive://.*'] +``` + +If you wanted to avoid loading EditorConfig for any remote files over ssh: + +```viml +let g:EditorConfig_exclude_patterns = ['scp://.*'] +``` + +Of course these two items could be combined into the following: + +```viml +let g:EditorConfig_exclude_patterns = ['fugitive://.*', 'scp://.*'] +``` + +### Disable for a specific filetype + +You can disable this plugin for a specific buffer by setting +`b:EditorConfig_disable`. Therefore, you can disable the +plugin for all buffers of a specific filetype. For example, to disable +EditorConfig for all git commit messages (filetype `gitcommit`): + +```viml +au FileType gitcommit let b:EditorConfig_disable = 1 +``` + +### Disable rules + +In very rare cases, +you might need to override some project-specific EditorConfig rules in global +or local vimrc in some cases, e.g., to resolve conflicts of trailing whitespace +trimming and buffer autosaving. This is not recommended, but you can: + +```viml +let g:EditorConfig_disable_rules = ['trim_trailing_whitespace'] +``` + +You are able to disable any supported EditorConfig properties. + +## Bugs and Feature Requests + +Feel free to submit bugs, feature requests, and other issues to the +[issue tracker][]. Be sure you have read the [contribution guidelines][]! + +[EditorConfig]: http://editorconfig.org +[GitHub]: https://github.com/editorconfig/editorconfig-vim +[PreserveNoEOL]: http://www.vim.org/scripts/script.php?script_id=4550 +[Tim Pope's fugitive]: https://github.com/tpope/vim-fugitive +[Vim online]: http://www.vim.org/scripts/script.php?script_id=3934 +[Vundle]: https://github.com/gmarik/Vundle.vim +[archive]: https://github.com/editorconfig/editorconfig-vim/archive/master.zip +[contribution guidelines]: https://github.com/editorconfig/editorconfig/blob/master/CONTRIBUTING.md#submitting-an-issue +[issue tracker]: https://github.com/editorconfig/editorconfig-vim/issues +[pathogen]: https://github.com/tpope/vim-pathogen +[properties]: http://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties +[editorconfig.txt]: https://github.com/editorconfig/editorconfig-vim/blob/master/doc/editorconfig.txt +[vim-plug]: https://github.com/junegunn/vim-plug diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim new file mode 100644 index 0000000..1f61a33 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim @@ -0,0 +1,60 @@ +" autoload/editorconfig.vim: EditorConfig native Vimscript plugin +" Copyright (c) 2011-2019 EditorConfig Team +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. +" + +if v:version < 700 + finish +endif + +let s:saved_cpo = &cpo +set cpo&vim + +" {{{1 variables +let s:hook_list = [] + +function! editorconfig#AddNewHook(func) " {{{1 + " Add a new hook + + call add(s:hook_list, a:func) +endfunction + +function! editorconfig#ApplyHooks(config) abort " {{{1 + " apply hooks + + for Hook in s:hook_list + let l:hook_ret = Hook(a:config) + + if type(l:hook_ret) != type(0) && l:hook_ret != 0 + " TODO print some debug info here + endif + endfor +endfunction + +" }}} + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vim: fdm=marker fdc=3 diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim new file mode 100644 index 0000000..6885e17 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim @@ -0,0 +1,147 @@ +" autoload/editorconfig_core.vim: top-level functions for +" editorconfig-core-vimscript and editorconfig-vim. + +" Copyright (c) 2018-2020 EditorConfig Team, including Chris White {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" Variables {{{1 + +" Note: we create this variable in every script that accesses it. Normally, I +" would put this in plugin/editorconfig.vim. However, in some of my tests, +" the command-line testing environment did not load plugin/* in the normal +" way. Therefore, I do the check everywhere so I don't have to special-case +" the command line. + +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}1 + +" The latest version of the specification that we support. +" See discussion at https://github.com/editorconfig/editorconfig/issues/395 +function! editorconfig_core#version() + return [0,13,0] +endfunction + +" === CLI =============================================================== {{{1 + +" For use from the command line. Output settings for in_name to +" the buffer named out_name. If an optional argument is provided, it is the +" name of the config file to use (default '.editorconfig'). +" TODO support multiple files +" +" filename (if any) +" @param names {Dictionary} The names of the files to use for this run +" - output [required] Where the editorconfig settings should be written +" - target [required] A string or list of strings to process. Each +" must be a full path. +" - dump [optional] If present, write debug info to this file +" @param job {Dictionary} What to do - same format as the input of +" editorconfig_core#handler#get_configurations(), +" except without the target member. + +function! editorconfig_core#currbuf_cli(names, job) " out_name, in_name, ... + let l:output = [] + + " Preprocess the job + let l:job = deepcopy(a:job) + + if has_key(l:job, 'version') " string to list + let l:ver = split(editorconfig_core#util#strip(l:job.version), '\v\.') + for l:idx in range(len(l:ver)) + let l:ver[l:idx] = str2nr(l:ver[l:idx]) + endfor + + let l:job.version = l:ver + endif + + " TODO provide version output from here instead of the shell script +" if string(a:names) ==? 'version' +" return +" endif +" + if type(a:names) != type({}) || type(a:job) != type({}) + throw 'Need two Dictionary arguments' + endif + + if has_key(a:names, 'dump') + execute 'redir! > ' . fnameescape(a:names.dump) + echom 'Names: ' . string(a:names) + echom 'Job: ' . string(l:job) + let g:editorconfig_core_vimscript_debug = 1 + endif + + if type(a:names['target']) == type([]) + let l:targets = a:names.target + else + let l:targets = [a:names.target] + endif + + for l:target in l:targets + + " Pre-process quoting weirdness so we are more flexible in the face + " of CMake+CTest+BAT+Powershell quoting. + + " Permit wrapping in double-quotes + let l:target = substitute(l:target, '\v^"(.*)"$', '\1', '') + + " Permit empty ('') entries in l:targets + if strlen(l:target)<1 + continue + endif + + if has_key(a:names, 'dump') + echom 'Trying: ' . string(l:target) + endif + + let l:job.target = l:target + let l:options = editorconfig_core#handler#get_configurations(l:job) + + if has_key(a:names, 'dump') + echom 'editorconfig_core#currbuf_cli result: ' . string(l:options) + endif + + if len(l:targets) > 1 + let l:output += [ '[' . l:target . ']' ] + endif + + for [ l:key, l:value ] in items(l:options) + let l:output += [ l:key . '=' . l:value ] + endfor + + endfor "foreach target + + " Write the output file + call writefile(l:output, a:names.output) +endfunction "editorconfig_core#currbuf_cli + +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fo-=ro: diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim new file mode 100644 index 0000000..ef9ced9 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim @@ -0,0 +1,467 @@ +" autoload/editorconfig_core/fnmatch.vim: Globbing for +" editorconfig-vim. Ported from the Python core's fnmatch.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +"Filename matching with shell patterns. +" +"fnmatch(FILENAME, PATH, PATTERN) matches according to the local convention. +"fnmatchcase(FILENAME, PATH, PATTERN) always takes case in account. +" +"The functions operate by translating the pattern into a regular +"expression. They cache the compiled regular expressions for speed. +" +"The function translate(PATTERN) returns a regular expression +"corresponding to PATTERN. (It does not compile it.) + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{1 +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}1 +" === Regexes =========================================================== {{{1 +let s:LEFT_BRACE = '\v[\\]@8<!\{' +" 8 is an arbitrary byte-count limit to the lookbehind (micro-optimization) +"LEFT_BRACE = re.compile( +" r""" +" +" (?<! \\ ) # Not preceded by "\" +" +" \{ # "{" +" +" """, re.VERBOSE +") + +let s:RIGHT_BRACE = '\v[\\]@8<!\}' +" 8 is an arbitrary byte-count limit to the lookbehind (micro-optimization) +"RIGHT_BRACE = re.compile( +" r""" +" +" (?<! \\ ) # Not preceded by "\" +" +" \} # "}" +" +" """, re.VERBOSE +") + +let s:NUMERIC_RANGE = '\v([+-]?\d+)' . '\.\.' . '([+-]?\d+)' +"NUMERIC_RANGE = re.compile( +" r""" +" ( # Capture a number +" [+-] ? # Zero or one "+" or "-" characters +" \d + # One or more digits +" ) +" +" \.\. # ".." +" +" ( # Capture a number +" [+-] ? # Zero or one "+" or "-" characters +" \d + # One or more digits +" ) +" """, re.VERBOSE +") + +" }}}1 +" === Internal functions ================================================ {{{1 + +" Dump the bytes of a:text. For debugging use. +function! s:dump_bytes(text) + let l:idx=0 + while l:idx < strlen(a:text) + let l:byte_val = char2nr(a:text[l:idx]) + echom printf('%10s%-5d%02x %s', '', l:idx, l:byte_val, + \ a:text[l:idx]) + let l:idx+=1 + endwhile +endfunction "s:dump_bytes + +" Dump the characters of a:text and their codepoints. For debugging use. +function! s:dump_chars(text) + let l:chars = split(a:text, '\zs') + let l:idx = 0 + let l:out1 = '' + let l:out2 = '' + while l:idx < len(l:chars) + let l:char = l:chars[l:idx] + let l:out1 .= printf('%5s', l:char) + let l:out2 .= printf('%5x', char2nr(l:char)) + let l:idx+=1 + endwhile + + echom l:out1 + echom l:out2 +endfunction "s:dump_chars + +" }}}1 +" === Translating globs to patterns ===================================== {{{1 + +" Used by s:re_escape: backslash-escape any character below U+0080; +" replace all others with a %U escape. +" See https://vi.stackexchange.com/a/19617/1430 by yours truly +" (https://vi.stackexchange.com/users/1430/cxw). +unlockvar s:replacement_expr +let s:replacement_expr = + \ '\=' . + \ '((char2nr(submatch(1)) >= 128) ? ' . + \ 'printf("%%U%08x", char2nr(submatch(1))) : ' . + \ '("\\" . submatch(1))' . + \ ')' +lockvar s:replacement_expr + +" Escaper for very-magic regexes +function! s:re_escape(text) + return substitute(a:text, '\v([^0-9a-zA-Z_])', s:replacement_expr, 'g') +endfunction + +"def translate(pat, nested=0): +" Translate a shell PATTERN to a regular expression. +" There is no way to quote meta-characters. +function! editorconfig_core#fnmatch#translate(pat, ...) + let l:nested = 0 + if a:0 + let l:nested = a:1 + endif + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#translate: pattern ' . a:pat + echom printf( + \ '- %d chars', strlen(substitute(a:pat, ".", "x", "g"))) + call s:dump_chars(a:pat) + endif + + let l:pat = a:pat " TODO remove if we wind up not needing this + + " Note: the Python sets MULTILINE and DOTALL, but Vim has \_. + " instead of DOTALL, and \_^ / \_$ instead of MULTILINE. + + let l:is_escaped = 0 + + " Find out whether the pattern has balanced braces. + let l:left_braces=[] + let l:right_braces=[] + call substitute(l:pat, s:LEFT_BRACE, '\=add(l:left_braces, 1)', 'g') + call substitute(l:pat, s:RIGHT_BRACE, '\=add(l:right_braces, 1)', 'g') + " Thanks to http://jeromebelleman.gitlab.io/posts/productivity/vimsub/ + let l:matching_braces = (len(l:left_braces) == len(l:right_braces)) + + " Unicode support (#2). Indexing l:pat[l:index] returns bytes, per + " https://github.com/neovim/neovim/issues/68#issue-28114985 . + " Instead, use split() per vimdoc to break the input string into an + " array of *characters*, and process that. + let l:characters = split(l:pat, '\zs') + + let l:index = 0 " character index + let l:length = len(l:characters) + let l:brace_level = 0 + let l:in_brackets = 0 + + let l:result = '' + let l:numeric_groups = [] + while l:index < l:length + let l:current_char = l:characters[l:index] + let l:index += 1 + +" if g:editorconfig_core_vimscript_debug +" echom ' - fnmatch#translate: ' . l:current_char . '@' . +" \ (l:index-1) . '; result ' . l:result +" endif + + if l:current_char ==# '*' + let l:pos = l:index + if l:pos < l:length && l:characters[l:pos] ==# '*' + let l:result .= '\_.*' + let l:index += 1 " skip the second star + else + let l:result .= '[^/]*' + endif + + elseif l:current_char ==# '?' + let l:result .= '\_[^/]' + + elseif l:current_char ==# '[' + if l:in_brackets + let l:result .= '\[' + else + let l:pos = l:index + let l:has_slash = 0 + while l:pos < l:length && l:characters[l:pos] != ']' + if l:characters[l:pos] ==# '/' && l:characters[l:pos-1] !=# '\' + let has_slash = 1 + break + endif + let l:pos += 1 + endwhile + if l:has_slash + " POSIX IEEE 1003.1-2017 sec. 2.13.3: '/' cannot occur + " in a bracket expression, so [/] matches a literal + " three-character string '[' . '/' . ']'. + let l:result .= '\[' + \ . s:re_escape(join(l:characters[l:index : l:pos-1], '')) + \ . '\/' + " escape the slash + let l:index = l:pos + 1 + " resume after the slash + else + if l:index < l:length && l:characters[l:index] =~# '\v%(\^|\!)' + let l:index += 1 + let l:result .= '[^' + else + let l:result .= '[' + endif + let l:in_brackets = 1 + endif + endif + + elseif l:current_char ==# '-' + if l:in_brackets + let l:result .= l:current_char + else + let l:result .= '\' . l:current_char + endif + + elseif l:current_char ==# ']' + if l:in_brackets && !l:is_escaped + let l:result .= ']' + let l:in_brackets = 0 + elseif l:is_escaped + let l:result .= '\]' + let l:is_escaped = 0 + else + let l:result .= '\]' + endif + + elseif l:current_char ==# '{' + let l:pos = l:index + let l:has_comma = 0 + while l:pos < l:length && (l:characters[l:pos] !=# '}' || l:is_escaped) + if l:characters[l:pos] ==# ',' && ! l:is_escaped + let l:has_comma = 1 + break + endif + let l:is_escaped = l:characters[l:pos] ==# '\' && ! l:is_escaped + let l:pos += 1 + endwhile + if ! l:has_comma && l:pos < l:length + let l:num_range = + \ matchlist(join(l:characters[l:index : l:pos-1], ''), + \ s:NUMERIC_RANGE) + if len(l:num_range) > 0 " Remember the ranges + call add(l:numeric_groups, [ 0+l:num_range[1], 0+l:num_range[2] ]) + let l:result .= '([+-]?\d+)' + else + let l:inner_xlat = editorconfig_core#fnmatch#translate( + \ join(l:characters[l:index : l:pos-1], ''), 1) + let l:inner_result = l:inner_xlat[0] + let l:inner_groups = l:inner_xlat[1] + let l:result .= '\{' . l:inner_result . '\}' + let l:numeric_groups += l:inner_groups + endif + let l:index = l:pos + 1 + elseif l:matching_braces + let l:result .= '%(' + let l:brace_level += 1 + else + let l:result .= '\{' + endif + + elseif l:current_char ==# ',' + if l:brace_level > 0 && ! l:is_escaped + let l:result .= '|' + else + let l:result .= '\,' + endif + + elseif l:current_char ==# '}' + if l:brace_level > 0 && ! l:is_escaped + let l:result .= ')' + let l:brace_level -= 1 + else + let l:result .= '\}' + endif + + elseif l:current_char ==# '/' + if join(l:characters[l:index : (l:index + 2)], '') ==# '**/' + let l:result .= '%(/|/\_.*/)' + let l:index += 3 + else + let l:result .= '\/' + endif + + elseif l:current_char != '\' + let l:result .= s:re_escape(l:current_char) + endif + + if l:current_char ==# '\' + if l:is_escaped + let l:result .= s:re_escape(l:current_char) + endif + let l:is_escaped = ! l:is_escaped + else + let l:is_escaped = 0 + endif + + endwhile + + if ! l:nested + let l:result .= '\_$' + endif + + return [l:result, l:numeric_groups] +endfunction " #editorconfig_core#fnmatch#translate + +let s:_cache = {} +function! s:cached_translate(pat) + if ! has_key(s:_cache, a:pat) + "regex = re.compile(res) + let s:_cache[a:pat] = + \ editorconfig_core#fnmatch#translate(a:pat) + " we don't compile the regex + endif + return s:_cache[a:pat] +endfunction " cached_translate + +" }}}1 +" === Matching functions ================================================ {{{1 + +function! editorconfig_core#fnmatch#fnmatch(name, path, pattern) +"def fnmatch(name, pat): +" """Test whether FILENAME matches PATH/PATTERN. +" +" Patterns are Unix shell style: +" +" - ``*`` matches everything except path separator +" - ``**`` matches everything +" - ``?`` matches any single character +" - ``[seq]`` matches any character in seq +" - ``[!seq]`` matches any char not in seq +" - ``{s1,s2,s3}`` matches any of the strings given (separated by commas) +" +" An initial period in FILENAME is not special. +" Both FILENAME and PATTERN are first case-normalized +" if the operating system requires it. +" If you don't want this, use fnmatchcase(FILENAME, PATTERN). +" """ +" + " Note: This throws away the backslash in '\.txt' on Cygwin, but that + " makes sense since it's Windows under the hood. + " We don't care about shellslash since we're going to change backslashes + " to slashes in just a moment anyway. + let l:localname = fnamemodify(a:name, ':p') + + if editorconfig_core#util#is_win() " normalize + let l:localname = substitute(tolower(l:localname), '\v\\', '/', 'g') + let l:path = substitute(tolower(a:path), '\v\\', '/', 'g') + let l:pattern = tolower(a:pattern) + else + let l:localname = l:localname + let l:path = a:path + let l:pattern = a:pattern + endif + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatch testing <' . l:localname . '> against <' . + \ l:pattern . '> wrt <' . l:path . '>' + endif + + return editorconfig_core#fnmatch#fnmatchcase(l:localname, l:path, l:pattern) +endfunction " fnmatch + +function! editorconfig_core#fnmatch#fnmatchcase(name, path, pattern) +"def fnmatchcase(name, pat): +" """Test whether FILENAME matches PATH/PATTERN, including case. +" +" This is a version of fnmatch() which doesn't case-normalize +" its arguments. +" """ +" + let [regex, num_groups] = s:cached_translate(a:pattern) + + let l:escaped_path = s:re_escape(a:path) + let l:regex = '\v' . l:escaped_path . l:regex + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatchcase: regex ' . l:regex + call s:dump_chars(l:regex) + echom '- fnmatch#fnmatchcase: checking ' . a:name + call s:dump_chars(a:name) + endif + + let l:match_groups = matchlist(a:name, l:regex)[1:] " [0] = full match + + if g:editorconfig_core_vimscript_debug + echom printf(' Got %d matches', len(l:match_groups)) + endif + + if len(l:match_groups) == 0 + return 0 + endif + + " Check numeric ranges + let pattern_matched = 1 + for l:idx in range(0,len(l:match_groups)) + let l:num = l:match_groups[l:idx] + if l:num ==# '' + break + endif + + let [min_num, max_num] = num_groups[l:idx] + if (min_num > (0+l:num)) || ((0+l:num) > max_num) + let pattern_matched = 0 + break + endif + + " Reject leading zeros without sign. This is very odd --- + " see editorconfig/editorconfig#371. + if match(l:num, '\v^0') != -1 + let pattern_matched = 0 + break + endif + endfor + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatchcase: ' . (pattern_matched ? 'matched' : 'did not match') + endif + + return pattern_matched +endfunction " fnmatchcase + +" }}}1 +" === Copyright notices ================================================= {{{1 +" Based on code from fnmatch.py file distributed with Python 2.6. +" Portions Copyright (c) 2001-2010 Python Software Foundation; +" All Rights Reserved. Licensed under PSF License (see LICENSE.PSF file). +" +" Changes to original fnmatch: +" +" - translate function supports ``*`` and ``**`` similarly to fnmatch C library +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker: diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim new file mode 100644 index 0000000..c9a66e1 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim @@ -0,0 +1,183 @@ +" autoload/editorconfig_core/handler.vim: Main worker for +" editorconfig-core-vimscript and editorconfig-vim. +" Modified from the Python core's handler.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" Return full filepath for filename in each directory in and above path. {{{1 +" Input path must be an absolute path. +" TODO shellslash/shellescape? +function! s:get_filenames(path, config_filename) + let l:path = a:path + let l:path_list = [] + while 1 + call add(l:path_list, editorconfig_core#util#path_join(l:path, a:config_filename)) + let l:newpath = fnamemodify(l:path, ':h') + if l:path ==? l:newpath || !strlen(l:path) + break + endif + let l:path = l:newpath + endwhile + return l:path_list +endfunction " get_filenames + +" }}}1 +" === Main ============================================================== {{{1 + +" Find EditorConfig files and return all options matching target_filename. +" Throws on failure. +" @param job {Dictionary} required 'target'; optional 'config' and 'version' +function! editorconfig_core#handler#get_configurations(job) + " TODO? support VERSION checks? + +" Special exceptions that may be raised by this function include: +" - ``VersionError``: self.version is invalid EditorConfig version +" - ``PathError``: self.filepath is not a valid absolute filepath +" - ``ParsingError``: improperly formatted EditorConfig file found + + let l:job = deepcopy(a:job) + if has_key(l:job, 'config') + let l:config_filename = l:job.config + else + let l:config_filename = '.editorconfig' + let l:job.config = l:config_filename + endif + + if has_key(l:job, 'version') + let l:version = l:job.version + else + let l:version = editorconfig_core#version() + let l:job.version = l:version + endif + + let l:target_filename = l:job.target + + "echom 'Beginning job ' . string(l:job) + if !s:check_assertions(l:job) + throw "Assertions failed" + endif + + let l:fullpath = fnamemodify(l:target_filename,':p') + let l:path = fnamemodify(l:fullpath, ':h') + let l:conf_files = s:get_filenames(l:path, l:config_filename) + + " echom 'fullpath ' . l:fullpath + " echom 'path ' . l:path + + let l:retval = {} + + " Attempt to find and parse every EditorConfig file in filetree + for l:conf_fn in l:conf_files + "echom 'Trying ' . l:conf_fn + let l:parsed = editorconfig_core#ini#read_ini_file(l:conf_fn, l:target_filename) + if !has_key(l:parsed, 'options') + continue + endif + " echom ' Has options' + + " Merge new EditorConfig file's options into current options + let l:old_options = l:retval + let l:retval = l:parsed.options + " echom 'Old options ' . string(l:old_options) + " echom 'New options ' . string(l:retval) + call extend(l:retval, l:old_options, 'force') + + " Stop parsing if parsed file has a ``root = true`` option + if l:parsed.root + break + endif + endfor + + call s:preprocess_values(l:job, l:retval) + return l:retval +endfunction " get_configurations + +function! s:check_assertions(job) +" TODO +" """Raise error if filepath or version have invalid values""" + +" # Raise ``PathError`` if filepath isn't an absolute path +" if not os.path.isabs(self.filepath): +" raise PathError("Input file must be a full path name.") + + " Throw if version specified is greater than current + let l:v = a:job.version + let l:us = editorconfig_core#version() + " echom 'Comparing requested version ' . string(l:v) . + " \ ' to our version ' . string(l:us) + if l:v[0] > l:us[0] || l:v[1] > l:us[1] || l:v[2] > l:us[2] + throw 'Required version ' . string(l:v) . + \ ' is greater than the current version ' . string(l:us) + endif + + return 1 " All OK if we got here +endfunction " check_assertions + +" }}}1 + +" Preprocess option values for consumption by plugins. {{{1 +" Modifies its argument in place. +function! s:preprocess_values(job, opts) + + " Lowercase option value for certain options + for l:name in ['end_of_line', 'indent_style', 'indent_size', + \ 'insert_final_newline', 'trim_trailing_whitespace', + \ 'charset'] + if has_key(a:opts, l:name) + let a:opts[l:name] = tolower(a:opts[l:name]) + endif + endfor + + " Set indent_size to "tab" if indent_size is unspecified and + " indent_style is set to "tab", provided we are at least v0.10.0. + if get(a:opts, 'indent_style', '') ==? "tab" && + \ !has_key(a:opts, 'indent_size') && + \ ( a:job.version[0]>0 || a:job.version[1] >=10 ) + let a:opts['indent_size'] = 'tab' + endif + + " Set tab_width to indent_size if indent_size is specified and + " tab_width is unspecified + if has_key(a:opts, 'indent_size') && !has_key(a:opts, 'tab_width') && + \ get(a:opts, 'indent_size', '') !=? "tab" + let a:opts['tab_width'] = a:opts['indent_size'] + endif + + " Set indent_size to tab_width if indent_size is "tab" + if has_key(a:opts, 'indent_size') && has_key(a:opts, 'tab_width') && + \ get(a:opts, 'indent_size', '') ==? "tab" + let a:opts['indent_size'] = a:opts['tab_width'] + endif +endfunction " preprocess_values + +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fdl=1: diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim new file mode 100644 index 0000000..7371696 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim @@ -0,0 +1,264 @@ +" autoload/editorconfig_core/ini.vim: Config-file parser for +" editorconfig-core-vimscript and editorconfig-vim. +" Modified from the Python core's ini.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{2 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}2 + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{2 +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}2 +" === Constants, including regexes ====================================== {{{2 +" Regular expressions for parsing section headers and options. +" Allow ``]`` and escaped ``;`` and ``#`` characters in section headers. +" In fact, allow \ to escape any single character - it needs to cover at +" least \ * ? [ ! ] { }. +unlockvar s:SECTCRE s:OPTCRE s:MAX_SECTION_NAME s:MAX_PROPERTY_NAME s:MAX_PROPERTY_VALUE +let s:SECTCRE = '\v^\s*\[(%([^\\#;]|\\.)+)\]' + +" Regular expression for parsing option name/values. +" Allow any amount of whitespaces, followed by separator +" (either ``:`` or ``=``), followed by any amount of whitespace and then +" any characters to eol +let s:OPTCRE = '\v\s*([^:=[:space:]][^:=]*)\s*([:=])\s*(.*)$' + +let s:MAX_SECTION_NAME = 4096 +let s:MAX_PROPERTY_NAME = 1024 +let s:MAX_PROPERTY_VALUE = 4096 + +lockvar s:SECTCRE s:OPTCRE s:MAX_SECTION_NAME s:MAX_PROPERTY_NAME s:MAX_PROPERTY_VALUE + +" }}}2 +" === Main ============================================================== {{{1 + +" Read \p config_filename and return the options applicable to +" \p target_filename. This is the main entry point in this file. +function! editorconfig_core#ini#read_ini_file(config_filename, target_filename) + if !filereadable(a:config_filename) + return {} + endif + + try + let l:lines = readfile(a:config_filename) + if &encoding !=? 'utf-8' + " strip BOM + if len(l:lines) > 0 && l:lines[0][:2] ==# "\xEF\xBB\xBF" + let l:lines[0] = l:lines[0][3:] + endif + " convert from UTF-8 to 'encoding' + call map(l:lines, 'iconv(v:val, "utf-8", &encoding)') + endif + let result = s:parse(a:config_filename, a:target_filename, l:lines) + catch + " rethrow, but with a prefix since throw 'Vim...' fails. + throw 'Could not read editorconfig file at ' . v:throwpoint . ': ' . string(v:exception) + endtry + + return result +endfunction + +function! s:parse(config_filename, target_filename, lines) +" Parse a sectioned setup file. +" The sections in setup file contains a title line at the top, +" indicated by a name in square brackets (`[]'), plus key/value +" options lines, indicated by `name: value' format lines. +" Continuations are represented by an embedded newline then +" leading whitespace. Blank lines, lines beginning with a '#', +" and just about everything else are ignored. + + let l:in_section = 0 + let l:matching_section = 0 + let l:optname = '' + let l:lineno = 0 + let l:e = [] " Errors, if any + + let l:options = {} " Options applicable to this file + let l:is_root = 0 " Whether a:config_filename declares root=true + + while 1 + if l:lineno == len(a:lines) + break + endif + + let l:line = a:lines[l:lineno] + let l:lineno = l:lineno + 1 + + " comment or blank line? + if editorconfig_core#util#strip(l:line) ==# '' + continue + endif + if l:line =~# '\v^[#;]' + continue + endif + + " is it a section header? + if g:editorconfig_core_vimscript_debug + echom "Header? <" . l:line . ">" + endif + + let l:mo = matchlist(l:line, s:SECTCRE) + if len(l:mo) + let l:sectname = l:mo[1] + let l:in_section = 1 + if strlen(l:sectname) > s:MAX_SECTION_NAME + " Section name too long => ignore the section + let l:matching_section = 0 + else + let l:matching_section = s:matches_filename( + \ a:config_filename, a:target_filename, l:sectname) + endif + + if g:editorconfig_core_vimscript_debug + echom 'In section ' . l:sectname . ', which ' . + \ (l:matching_section ? 'matches' : 'does not match') + \ ' file ' . a:target_filename . ' (config ' . + \ a:config_filename . ')' + endif + + " So sections can't start with a continuation line + let l:optname = '' + + " Is it an option line? + else + let l:mo = matchlist(l:line, s:OPTCRE) + if len(l:mo) + let l:optname = mo[1] + let l:optval = mo[3] + + if g:editorconfig_core_vimscript_debug + echom printf('Saw raw opt <%s>=<%s>', l:optname, l:optval) + endif + + let l:optval = editorconfig_core#util#strip(l:optval) + " allow empty values + if l:optval ==? '""' + let l:optval = '' + endif + let l:optname = s:optionxform(l:optname) + if !l:in_section && optname ==? 'root' + let l:is_root = (optval ==? 'true') + endif + if g:editorconfig_core_vimscript_debug + echom printf('Saw opt <%s>=<%s>', l:optname, l:optval) + endif + + if l:matching_section && + \ strlen(l:optname) <= s:MAX_PROPERTY_NAME && + \ strlen(l:optval) <= s:MAX_PROPERTY_VALUE + let l:options[l:optname] = l:optval + endif + else + " a non-fatal parsing error occurred. set up the + " exception but keep going. the exception will be + " raised at the end of the file and will contain a + " list of all bogus lines + call add(e, "Parse error in '" . a:config_filename . "' at line " . + \ l:lineno . ": '" . l:line . "'") + endif + endif + endwhile + + " if any parsing errors occurred, raise an exception + if len(l:e) + throw string(l:e) + endif + + return {'root': l:is_root, 'options': l:options} +endfunction! + +" }}}1 +" === Helpers =========================================================== {{{1 + +" Preprocess option names +function! s:optionxform(optionstr) + let l:result = substitute(a:optionstr, '\v\s+$', '', 'g') " rstrip + return tolower(l:result) +endfunction + +" Return true if \p glob matches \p target_filename +function! s:matches_filename(config_filename, target_filename, glob) +" config_dirname = normpath(dirname(config_filename)).replace(sep, '/') + let l:config_dirname = fnamemodify(a:config_filename, ':p:h') . '/' + + if editorconfig_core#util#is_win() + " Regardless of whether shellslash is set, make everything slashes + let l:config_dirname = + \ tolower(substitute(l:config_dirname, '\v\\', '/', 'g')) + endif + + let l:glob = substitute(a:glob, '\v\\([#;])', '\1', 'g') + + " Take account of the path to the editorconfig file. + " editorconfig-core-c/src/lib/editorconfig.c says: + " "Pattern would be: /dir/of/editorconfig/file[double_star]/[section] if + " section does not contain '/', or /dir/of/editorconfig/file[section] + " if section starts with a '/', or /dir/of/editorconfig/file/[section] if + " section contains '/' but does not start with '/'." + + if stridx(l:glob, '/') != -1 " contains a slash + if l:glob[0] ==# '/' + let l:glob = l:glob[1:] " trim leading slash + endif +" This will be done by fnmatch +" let l:glob = l:config_dirname . l:glob + else " does not contain a slash + let l:config_dirname = l:config_dirname[:-2] + " Trim trailing slash + let l:glob = '**/' . l:glob + endif + + if g:editorconfig_core_vimscript_debug + echom '- ini#matches_filename: checking <' . a:target_filename . + \ '> against <' . l:glob . '> with respect to config file <' . + \ a:config_filename . '>' + echom '- ini#matches_filename: config_dirname is ' . l:config_dirname + endif + + return editorconfig_core#fnmatch#fnmatch(a:target_filename, + \ l:config_dirname, l:glob) +endfunction " matches_filename + +" }}}1 +" === Copyright notices ================================================= {{{2 +" Based on code from ConfigParser.py file distributed with Python 2.6. +" Portions Copyright (c) 2001-2010 Python Software Foundation; +" All Rights Reserved. Licensed under PSF License (see LICENSE.PSF file). +" +" Changes to original ConfigParser: +" +" - Special characters can be used in section names +" - Octothorpe can be used for comments (not just at beginning of line) +" - Only track INI options in sections that match target filename +" - Stop parsing files with when ``root = true`` is found +" }}}2 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fdl=1: diff --git a/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim new file mode 100644 index 0000000..c4df04a --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim @@ -0,0 +1,84 @@ +" util.vim: part of editorconfig-core-vimscript and editorconfig-vim. +" Copyright (c) 2018-2019 EditorConfig Team, including Chris White {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" A verbatim copy of ingo#fs#path#Separator() {{{1 +" from https://github.com/vim-scripts/ingo-library/blob/558132e2221db3af26dc2f2c6756d092d48a459f/autoload/ingo/fs/path.vim +" distributed under the Vim license. +function! editorconfig_core#util#Separator() + return (exists('+shellslash') && ! &shellslash ? '\' : '/') +endfunction " }}}1 + +" path_join(): ('a','b')->'a/b'; ('a/','b')->'a/b'. {{{1 +function! editorconfig_core#util#path_join(a, b) + " TODO shellescape/shellslash? + "echom 'Joining <' . a:a . '> and <' . a:b . '>' + "echom 'Length is ' . strlen(a:a) + "echom 'Last char is ' . char2nr(a:a[-1]) + if a:a !~# '\v%(\/|\\)$' + return a:a . editorconfig_core#util#Separator() . a:b + else + return a:a . a:b + endif +endfunction " }}}1 + +" is_win() by xolox {{{1 +" The following function is modified from +" https://github.com/xolox/vim-misc/blob/master/autoload/xolox/misc/os.vim +" Copyright (c) 2015 Peter Odding <peter@peterodding.com> +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in all +" copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +" SOFTWARE. +function! editorconfig_core#util#is_win() + " Returns 1 (true) when on Microsoft Windows, 0 (false) otherwise. + return has('win16') || has('win32') || has('win64') +endfunction " }}}1 + +" strip() {{{1 +function! editorconfig_core#util#strip(s) + return substitute(a:s, '\v^\s+|\s+$','','g') +endfunction " }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker: diff --git a/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt b/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt new file mode 100644 index 0000000..be234b0 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt @@ -0,0 +1,238 @@ +*editorconfig.txt* EditorConfig plugin for vim. + +File: editorconfig.txt +Version: 1.1.1 +Maintainer: EditorConfig Team <http://editorconfig.org> +Description: EditorConfig vim plugin + +CONTENTS~ + *editorconfig-contents* +---------------------------------------------------------------------------- +1. Overview |editorconfig-overview| +2. Installation |editorconfig-installation| +3. Commands |editorconfig-commands| +4. Settings |editorconfig-settings| +5. Advanced |editorconfig-advanced| +6. License |editorconfig-license| + + +OVERVIEW~ + *editorconfig-overview* +---------------------------------------------------------------------------- +This is the EditorConfig plugin for vim. + + +INSTALLATION~ + *editorconfig-installation* +---------------------------------------------------------------------------- +Follow the instructions in the README.md file to install this plugin. + +COMMANDS~ + *editorconfig-commands* +---------------------------------------------------------------------------- + + *:EditorConfigReload* +Command: + :EditorConfigReload + +Reload the EditorConfig conf files. When `.editorconfig` files are modified, +this command could prevent you to reload the current edited file to load the +new configuration. + +SETTINGS~ + *editorconfig-settings* +---------------------------------------------------------------------------- + *g:EditorConfig_core_mode* +Specify the mode of EditorConfig core. Generally it is OK to leave this option +empty. Currently, the supported modes are "vim_core" (default) and +"external_command". + + vim_core: Use the included VimScript EditorConfig Core. + external_command: Run external EditorConfig Core. + +If "g:EditorConfig_core_mode" is not specified, this plugin will automatically +choose "vim_core". + +If you choose "external_command" mode, you must also set +|g:EditorConfig_exec_path|. + +Changes to "g:EditorConfig_core_mode" will not take effect until Vim +is restarted. + + *b:EditorConfig_disable* +This is a buffer-local variable that disables the EditorConfig plugin for a +single buffer. + +Example: Disable EditorConfig for the current buffer: +> + let b:EditorConfig_disable = 1 +< +Example: Disable EditorConfig for all git commit messages: +> + au FileType gitcommit let b:EditorConfig_disable = 1 +< + + *g:EditorConfig_exclude_patterns* +This is a list contains file path patterns which will be ignored by +EditorConfig plugin. When the path of the opened buffer (i.e. +"expand('%:p')") matches any of the patterns in the list, EditorConfig will +not load for this file. The default is an empty list. + +Example: Avoid loading EditorConfig for any remote files over ssh +> + let g:EditorConfig_exclude_patterns = ['scp://.*'] +< + + *g:EditorConfig_exec_path* +The file path to the EditorConfig core executable. You can set this value in +your |vimrc| like this: +> + let g:EditorConfig_exec_path = 'Path to your EditorConfig Core executable' +< +The default value is empty. + +If "g:EditorConfig_exec_path" is not set, the plugin will use the "vim_core" +mode regardless of the setting of |g:EditorConfig_core_mode|. + +Changes to "g:EditorConfig_exec_path" will not take effect until Vim +is restarted. + + *g:EditorConfig_max_line_indicator* +The way to show the line where the maximal length is reached. Accepted values +are "line", "fill", "exceeding" and "fillexceeding", otherwise there will be +no max line indicator. + + "line": the right column of the max line length column will be + highlighted on all lines, by adding +1 to 'colorcolumn'. + + "fill": all the columns to the right of the max line length + column will be highlighted on all lines, by setting + 'colorcolumn' to a list starting from "max_line_length + + 1" to the number of columns on the screen. + + "exceeding": the right column of the max line length column will be + highlighted on lines that exceed the max line length, by + adding a match for the ColorColumn group. + + "fillexceeding": all the columns to the right of the max line length + column will be highlighted on lines that exceed the max + line length, by adding a match for the ColorColumn group. + + "none": no max line length indicator will be shown. Recommended + when you do not want any indicator to be shown, but any + value other than those listed above also work as "none". + +To set this option, add any of the following lines to your |vimrc| file: +> + let g:EditorConfig_max_line_indicator = "line" + let g:EditorConfig_max_line_indicator = "fill" + let g:EditorConfig_max_line_indicator = "exceeding" + let g:EditorConfig_max_line_indicator = "fillexceeding" + let g:EditorConfig_max_line_indicator = "none" +< +The default value is "line". + + *g:EditorConfig_enable_for_new_buf* +Set this to 1 if you want EditorConfig plugin to set options +for new empty buffers too. +Path to .editorconfig will be determined based on CWD (see |getcwd()|) +> + let g:EditorConfig_enable_for_new_buf = 1 +< +This option defaults to 0. + + *g:EditorConfig_preserve_formatoptions* +Set this to 1 if you don't want your formatoptions modified when +max_line_length is set: +> + let g:EditorConfig_preserve_formatoptions = 1 +< +This option defaults to 0. + + *g:EditorConfig_softtabstop_space* +When spaces are used for indent, Vim's 'softtabstop' feature will make the +backspace key delete one indent level. If you turn off that feature (by +setting the option to 0), only a single space will be deleted. +This option defaults to 1, which enables 'softtabstop' and uses the +'shiftwidth' value for it. You can also set this to -1 to automatically follow +the current 'shiftwidth' value (since Vim 7.3.693). Or set this to [] if +EditorConfig should not touch 'softtabstop' at all. + + *g:EditorConfig_softtabstop_tab* +When tabs are used for indent, Vim's 'softtabstop' feature only applies to +backspacing over existing runs of spaces. +This option defaults to 1, so backspace will delete one indent level worth of +spaces; -1 does the same but automatically follows the current 'shiftwidth' +value. Set this to 0 to have backspace delete just a single space character. +Or set this to [] if EditorConfig should not touch 'softtabstop' at all. + + *g:EditorConfig_verbose* +Set this to 1 if you want debug info printed: +> + let g:EditorConfig_verbose = 1 +< + +ADVANCED~ + *editorconfig-advanced* +---------------------------------------------------------------------------- + *editorconfig-hook* + *EditorConfig#AddNewHook()* +While this plugin offers several builtin supported properties (as mentioned +here: https://github.com/editorconfig/editorconfig-vim#supported-properties), +we are also able to add our own hooks to support additional EditorConfig +properties, including those not in the EditorConfig standard. For example, we +are working on an Objective-C project, and all our "*.m" files should be +Objective-C source files. However, vim sometimes detect "*.m" files as MATLAB +source files, which causes incorrect syntax highlighting, code indentation, +etc. To solve the case, we could write the following code into the |vimrc| +file: +> + function! FiletypeHook(config) + if has_key(a:config, 'vim_filetype') + let &filetype = a:config['vim_filetype'] + endif + + return 0 " Return 0 to show no error happened + endfunction + + call editorconfig#AddNewHook(function('FiletypeHook')) +< +And add the following code to your .editorconfig file: +> + [*.m] + vim_filetype = objc +< +Then try to open an Objective-C file, you will find the |filetype| is set to +"objc". + +License~ + *editorconfig-license* +---------------------------------------------------------------------------- + +License: + Copyright (c) 2011-2019 EditorConfig Team + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + +vim:ft=help:tw=78:cc= diff --git a/runtime/pack/dist/opt/editorconfig/doc/tags b/runtime/pack/dist/opt/editorconfig/doc/tags new file mode 100644 index 0000000..8c82765 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/doc/tags @@ -0,0 +1,21 @@ +:EditorConfigReload editorconfig.txt /*:EditorConfigReload* +EditorConfig#AddNewHook() editorconfig.txt /*EditorConfig#AddNewHook()* +b:EditorConfig_disable editorconfig.txt /*b:EditorConfig_disable* +editorconfig-advanced editorconfig.txt /*editorconfig-advanced* +editorconfig-commands editorconfig.txt /*editorconfig-commands* +editorconfig-contents editorconfig.txt /*editorconfig-contents* +editorconfig-hook editorconfig.txt /*editorconfig-hook* +editorconfig-installation editorconfig.txt /*editorconfig-installation* +editorconfig-license editorconfig.txt /*editorconfig-license* +editorconfig-overview editorconfig.txt /*editorconfig-overview* +editorconfig-settings editorconfig.txt /*editorconfig-settings* +editorconfig.txt editorconfig.txt /*editorconfig.txt* +g:EditorConfig_core_mode editorconfig.txt /*g:EditorConfig_core_mode* +g:EditorConfig_enable_for_new_buf editorconfig.txt /*g:EditorConfig_enable_for_new_buf* +g:EditorConfig_exclude_patterns editorconfig.txt /*g:EditorConfig_exclude_patterns* +g:EditorConfig_exec_path editorconfig.txt /*g:EditorConfig_exec_path* +g:EditorConfig_max_line_indicator editorconfig.txt /*g:EditorConfig_max_line_indicator* +g:EditorConfig_preserve_formatoptions editorconfig.txt /*g:EditorConfig_preserve_formatoptions* +g:EditorConfig_softtabstop_space editorconfig.txt /*g:EditorConfig_softtabstop_space* +g:EditorConfig_softtabstop_tab editorconfig.txt /*g:EditorConfig_softtabstop_tab* +g:EditorConfig_verbose editorconfig.txt /*g:EditorConfig_verbose* diff --git a/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim b/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim new file mode 100644 index 0000000..d1f8e00 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim @@ -0,0 +1 @@ +autocmd BufNewFile,BufRead .editorconfig setfiletype dosini diff --git a/runtime/pack/dist/opt/editorconfig/mkzip.sh b/runtime/pack/dist/opt/editorconfig/mkzip.sh new file mode 100755 index 0000000..fe5b2e3 --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/mkzip.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +zip -r editorconfig-vim-$*.zip autoload/* doc/* ftdetect/* plugin/* diff --git a/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim b/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim new file mode 100644 index 0000000..18addef --- /dev/null +++ b/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim @@ -0,0 +1,614 @@ +" plugin/editorconfig.vim: EditorConfig native Vimscript plugin file +" Copyright (c) 2011-2019 EditorConfig Team +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. +" + +" check for Vim versions and duplicate script loading. +if v:version < 700 || exists("g:loaded_EditorConfig") + finish +endif +let g:loaded_EditorConfig = 1 + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{1 + +" Make sure the globals all exist +if !exists('g:EditorConfig_exec_path') + let g:EditorConfig_exec_path = '' +endif + +if !exists('g:EditorConfig_verbose') + let g:EditorConfig_verbose = 0 +endif + +if !exists('g:EditorConfig_preserve_formatoptions') + let g:EditorConfig_preserve_formatoptions = 0 +endif + +if !exists('g:EditorConfig_max_line_indicator') + let g:EditorConfig_max_line_indicator = 'line' +endif + +if !exists('g:EditorConfig_exclude_patterns') + let g:EditorConfig_exclude_patterns = [] +endif + +if !exists('g:EditorConfig_disable_rules') + let g:EditorConfig_disable_rules = [] +endif + +if !exists('g:EditorConfig_enable_for_new_buf') + let g:EditorConfig_enable_for_new_buf = 0 +endif + +if !exists('g:EditorConfig_softtabstop_space') + let g:EditorConfig_softtabstop_space = 1 +endif + +if !exists('g:EditorConfig_softtabstop_tab') + let g:EditorConfig_softtabstop_tab = 1 +endif + +" Copy some of the globals into script variables --- changes to these +" globals won't affect the plugin until the plugin is reloaded. +if exists('g:EditorConfig_core_mode') && !empty(g:EditorConfig_core_mode) + let s:editorconfig_core_mode = g:EditorConfig_core_mode +else + let s:editorconfig_core_mode = '' +endif + +if exists('g:EditorConfig_exec_path') && !empty(g:EditorConfig_exec_path) + let s:editorconfig_exec_path = g:EditorConfig_exec_path +else + let s:editorconfig_exec_path = '' +endif + +let s:initialized = 0 + +" }}}1 + +" shellslash handling {{{1 +function! s:DisableShellSlash(bufnr) " {{{2 + " disable shellslash for proper escaping of Windows paths + + " In Windows, 'shellslash' also changes the behavior of 'shellescape'. + " It makes 'shellescape' behave like in UNIX environment. So ':setl + " noshellslash' before evaluating 'shellescape' and restore the + " settings afterwards when 'shell' does not contain 'sh' somewhere. + let l:shell = getbufvar(a:bufnr, '&shell') + if has('win32') && empty(matchstr(l:shell, 'sh')) + let s:old_shellslash = getbufvar(a:bufnr, '&shellslash') + setbufvar(a:bufnr, '&shellslash', 0) + endif +endfunction " }}}2 + +function! s:ResetShellSlash(bufnr) " {{{2 + " reset shellslash to the user-set value, if any + if exists('s:old_shellslash') + setbufvar(a:bufnr, '&shellslash', s:old_shellslash) + unlet! s:old_shellslash + endif +endfunction " }}}2 +" }}}1 + +" Mode initialization functions {{{1 + +function! s:InitializeVimCore() +" Initialize vim core. Returns 1 on failure; 0 on success +" At the moment, all we need to do is to check that it is installed. + try + let l:vim_core_ver = editorconfig_core#version() + catch + return 1 + endtry + return 0 +endfunction + +function! s:InitializeExternalCommand() +" Initialize external_command mode + + if empty(s:editorconfig_exec_path) + echo 'Please specify a g:EditorConfig_exec_path' + return 1 + endif + + if g:EditorConfig_verbose + echo 'Checking for external command ' . s:editorconfig_exec_path . ' ...' + endif + + if !executable(s:editorconfig_exec_path) + echo 'File ' . s:editorconfig_exec_path . ' is not executable.' + return 1 + endif + + return 0 +endfunction +" }}}1 + +function! s:Initialize() " Initialize the plugin. {{{1 + " Returns truthy on error, falsy on success. + + if empty(s:editorconfig_core_mode) + let s:editorconfig_core_mode = 'vim_core' " Default core choice + endif + + if s:editorconfig_core_mode ==? 'external_command' + if s:InitializeExternalCommand() + echohl WarningMsg + echo 'EditorConfig: Failed to initialize external_command mode. ' . + \ 'Falling back to vim_core mode.' + echohl None + let s:editorconfig_core_mode = 'vim_core' + endif + endif + + if s:editorconfig_core_mode ==? 'vim_core' + if s:InitializeVimCore() + echohl ErrorMsg + echo 'EditorConfig: Failed to initialize vim_core mode. ' . + \ 'The plugin will not function.' + echohl None + return 1 + endif + + elseif s:editorconfig_core_mode ==? 'external_command' + " Nothing to do here, but this elseif is required to avoid + " external_command falling into the else clause. + + else " neither external_command nor vim_core + echohl ErrorMsg + echo "EditorConfig: I don't know how to use mode " . s:editorconfig_core_mode + echohl None + return 1 + endif + + let s:initialized = 1 + return 0 +endfunction " }}}1 + +function! s:GetFilenames(path, filename) " {{{1 +" Yield full filepath for filename in each directory in and above path + + let l:path_list = [] + let l:path = a:path + while 1 + let l:path_list += [l:path . '/' . a:filename] + let l:newpath = fnamemodify(l:path, ':h') + if l:path == l:newpath + break + endif + let l:path = l:newpath + endwhile + return l:path_list +endfunction " }}}1 + +function! s:UseConfigFiles(from_autocmd) abort " Apply config to the current buffer {{{1 + " from_autocmd is truthy if called from an autocmd, falsy otherwise. + + " Get the properties of the buffer we are working on + if a:from_autocmd + let l:bufnr = str2nr(expand('<abuf>')) + let l:buffer_name = expand('<afile>:p') + let l:buffer_path = expand('<afile>:p:h') + else + let l:bufnr = bufnr('%') + let l:buffer_name = expand('%:p') + let l:buffer_path = expand('%:p:h') + endif + call setbufvar(l:bufnr, 'editorconfig_tried', 1) + + " Only process normal buffers (do not treat help files as '.txt' files) + " When starting Vim with a directory, the buftype might not yet be set: + " Therefore, also check if buffer_name is a directory. + if index(['', 'acwrite'], &buftype) == -1 || isdirectory(l:buffer_name) + return + endif + + if empty(l:buffer_name) + if g:EditorConfig_enable_for_new_buf + let l:buffer_name = getcwd() . "/." + else + if g:EditorConfig_verbose + echo 'Skipping EditorConfig for unnamed buffer' + endif + return + endif + endif + + if getbufvar(l:bufnr, 'EditorConfig_disable', 0) + if g:EditorConfig_verbose + echo 'EditorConfig disabled --- skipping buffer "' . l:buffer_name . '"' + endif + return + endif + + " Ignore specific patterns + for pattern in g:EditorConfig_exclude_patterns + if l:buffer_name =~ pattern + if g:EditorConfig_verbose + echo 'Skipping EditorConfig for buffer "' . l:buffer_name . + \ '" based on pattern "' . pattern . '"' + endif + return + endif + endfor + + " Check if any .editorconfig does exist + let l:conf_files = s:GetFilenames(l:buffer_path, '.editorconfig') + let l:conf_found = 0 + for conf_file in conf_files + if filereadable(conf_file) + let l:conf_found = 1 + break + endif + endfor + if !l:conf_found + return + endif + + if !s:initialized + if s:Initialize() + return + endif + endif + + if g:EditorConfig_verbose + echo 'Applying EditorConfig ' . s:editorconfig_core_mode . + \ ' on file "' . l:buffer_name . '"' + endif + + if s:editorconfig_core_mode ==? 'vim_core' + if s:UseConfigFiles_VimCore(l:bufnr, l:buffer_name) == 0 + call setbufvar(l:bufnr, 'editorconfig_applied', 1) + endif + elseif s:editorconfig_core_mode ==? 'external_command' + call s:UseConfigFiles_ExternalCommand(l:bufnr, l:buffer_name) + call setbufvar(l:bufnr, 'editorconfig_applied', 1) + else + echohl Error | + \ echo "Unknown EditorConfig Core: " . + \ s:editorconfig_core_mode | + \ echohl None + endif +endfunction " }}}1 + +" Custom commands, and autoloading {{{1 + +" Autocommands, and function to enable/disable the plugin {{{2 +function! s:EditorConfigEnable(should_enable) + augroup editorconfig + autocmd! + if a:should_enable + autocmd BufNewFile,BufReadPost,BufFilePost * call s:UseConfigFiles(1) + autocmd VimEnter,BufNew * call s:UseConfigFiles(1) + endif + augroup END +endfunction + +" }}}2 + +" Commands {{{2 +command! EditorConfigEnable call s:EditorConfigEnable(1) +command! EditorConfigDisable call s:EditorConfigEnable(0) + +command! EditorConfigReload call s:UseConfigFiles(0) " Reload EditorConfig files +" }}}2 + +" On startup, enable the autocommands +call s:EditorConfigEnable(1) + +" }}}1 + +" UseConfigFiles function for different modes {{{1 + +function! s:UseConfigFiles_VimCore(bufnr, target) +" Use the vimscript EditorConfig core + try + let l:config = editorconfig_core#handler#get_configurations( + \ { 'target': a:target } ) + call s:ApplyConfig(a:bufnr, l:config) + return 0 " success + catch + return 1 " failure + endtry +endfunction + +function! s:UseConfigFiles_ExternalCommand(bufnr, target) +" Use external EditorConfig core (e.g., the C core) + + call s:DisableShellSlash(a:bufnr) + let l:exec_path = shellescape(s:editorconfig_exec_path) + call s:ResetShellSlash(a:bufnr) + + call s:SpawnExternalParser(a:bufnr, l:exec_path, a:target) +endfunction + +function! s:SpawnExternalParser(bufnr, cmd, target) " {{{2 +" Spawn external EditorConfig. Used by s:UseConfigFiles_ExternalCommand() + + let l:cmd = a:cmd + + if empty(l:cmd) + throw 'No cmd provided' + endif + + let l:config = {} + + call s:DisableShellSlash(a:bufnr) + let l:cmd = l:cmd . ' ' . shellescape(a:target) + call s:ResetShellSlash(a:bufnr) + + let l:parsing_result = split(system(l:cmd), '\v[\r\n]+') + + " if editorconfig core's exit code is not zero, give out an error + " message + if v:shell_error != 0 + echohl ErrorMsg + echo 'Failed to execute "' . l:cmd . '". Exit code: ' . + \ v:shell_error + echo '' + echo 'Message:' + echo l:parsing_result + echohl None + return + endif + + if g:EditorConfig_verbose + echo 'Output from EditorConfig core executable:' + echo l:parsing_result + endif + + for one_line in l:parsing_result + let l:eq_pos = stridx(one_line, '=') + + if l:eq_pos == -1 " = is not found. Skip this line + continue + endif + + let l:eq_left = strpart(one_line, 0, l:eq_pos) + if l:eq_pos + 1 < strlen(one_line) + let l:eq_right = strpart(one_line, l:eq_pos + 1) + else + let l:eq_right = '' + endif + + let l:config[l:eq_left] = l:eq_right + endfor + + call s:ApplyConfig(a:bufnr, l:config) +endfunction " }}}2 + +" }}}1 + +" Set the buffer options {{{1 +function! s:SetCharset(bufnr, charset) abort " apply config['charset'] + + " Remember the buffer's state so we can set `nomodifed` at the end + " if appropriate. + let l:orig_fenc = getbufvar(a:bufnr, "&fileencoding") + let l:orig_enc = getbufvar(a:bufnr, "&encoding") + let l:orig_modified = getbufvar(a:bufnr, "&modified") + + if a:charset == "utf-8" + call setbufvar(a:bufnr, '&fileencoding', 'utf-8') + call setbufvar(a:bufnr, '&bomb', 0) + elseif a:charset == "utf-8-bom" + call setbufvar(a:bufnr, '&fileencoding', 'utf-8') + call setbufvar(a:bufnr, '&bomb', 1) + elseif a:charset == "latin1" + call setbufvar(a:bufnr, '&fileencoding', 'latin1') + call setbufvar(a:bufnr, '&bomb', 0) + elseif a:charset == "utf-16be" + call setbufvar(a:bufnr, '&fileencoding', 'utf-16be') + call setbufvar(a:bufnr, '&bomb', 1) + elseif a:charset == "utf-16le" + call setbufvar(a:bufnr, '&fileencoding', 'utf-16le') + call setbufvar(a:bufnr, '&bomb', 1) + endif + + let l:new_fenc = getbufvar(a:bufnr, "&fileencoding") + + " If all we did was change the fileencoding from the default to a copy + " of the default, we didn't actually modify the file. + if !l:orig_modified && (l:orig_fenc ==# '') && (l:new_fenc ==# l:orig_enc) + if g:EditorConfig_verbose + echo 'Setting nomodified on buffer ' . a:bufnr + endif + call setbufvar(a:bufnr, '&modified', 0) + endif +endfunction + +function! s:ApplyConfig(bufnr, config) abort + if g:EditorConfig_verbose + echo 'Options: ' . string(a:config) + endif + + if s:IsRuleActive('indent_style', a:config) + if a:config["indent_style"] == "tab" + call setbufvar(a:bufnr, '&expandtab', 0) + elseif a:config["indent_style"] == "space" + call setbufvar(a:bufnr, '&expandtab', 1) + endif + endif + + if s:IsRuleActive('tab_width', a:config) + let l:tabstop = str2nr(a:config["tab_width"]) + call setbufvar(a:bufnr, '&tabstop', l:tabstop) + else + " Grab the current ts so we can use it below + let l:tabstop = getbufvar(a:bufnr, '&tabstop') + endif + + if s:IsRuleActive('indent_size', a:config) + " if indent_size is 'tab', set shiftwidth to tabstop; + " if indent_size is a positive integer, set shiftwidth to the integer + " value + if a:config["indent_size"] == "tab" + call setbufvar(a:bufnr, '&shiftwidth', l:tabstop) + if type(g:EditorConfig_softtabstop_tab) != type([]) + call setbufvar(a:bufnr, '&softtabstop', + \ g:EditorConfig_softtabstop_tab > 0 ? + \ l:tabstop : g:EditorConfig_softtabstop_tab) + endif + else + let l:indent_size = str2nr(a:config["indent_size"]) + if l:indent_size > 0 + call setbufvar(a:bufnr, '&shiftwidth', l:indent_size) + if type(g:EditorConfig_softtabstop_space) != type([]) + call setbufvar(a:bufnr, '&softtabstop', + \ g:EditorConfig_softtabstop_space > 0 ? + \ l:indent_size : g:EditorConfig_softtabstop_space) + endif + endif + endif + + endif + + if s:IsRuleActive('end_of_line', a:config) && + \ getbufvar(a:bufnr, '&modifiable') + if a:config["end_of_line"] == "lf" + call setbufvar(a:bufnr, '&fileformat', 'unix') + elseif a:config["end_of_line"] == "crlf" + call setbufvar(a:bufnr, '&fileformat', 'dos') + elseif a:config["end_of_line"] == "cr" + call setbufvar(a:bufnr, '&fileformat', 'mac') + endif + endif + + if s:IsRuleActive('charset', a:config) && + \ getbufvar(a:bufnr, '&modifiable') + call s:SetCharset(a:bufnr, a:config["charset"]) + endif + + augroup editorconfig_trim_trailing_whitespace + autocmd! BufWritePre <buffer> + if s:IsRuleActive('trim_trailing_whitespace', a:config) && + \ get(a:config, 'trim_trailing_whitespace', 'false') ==# 'true' + execute 'autocmd BufWritePre <buffer=' . a:bufnr . '> call s:TrimTrailingWhitespace()' + endif + augroup END + + if s:IsRuleActive('insert_final_newline', a:config) + if exists('+fixendofline') + if a:config["insert_final_newline"] == "false" + call setbufvar(a:bufnr, '&fixendofline', 0) + else + call setbufvar(a:bufnr, '&fixendofline', 1) + endif + elseif exists(':SetNoEOL') == 2 + if a:config["insert_final_newline"] == "false" + silent! SetNoEOL " Use the PreserveNoEOL plugin to accomplish it + endif + endif + endif + + " highlight the columns following max_line_length + if s:IsRuleActive('max_line_length', a:config) && + \ a:config['max_line_length'] != 'off' + let l:max_line_length = str2nr(a:config['max_line_length']) + + if l:max_line_length >= 0 + call setbufvar(a:bufnr, '&textwidth', l:max_line_length) + if g:EditorConfig_preserve_formatoptions == 0 + " setlocal formatoptions+=tc + let l:fo = getbufvar(a:bufnr, '&formatoptions') + if l:fo !~# 't' + let l:fo .= 't' + endif + if l:fo !~# 'c' + let l:fo .= 'c' + endif + call setbufvar(a:bufnr, '&formatoptions', l:fo) + endif + endif + + if exists('+colorcolumn') + if l:max_line_length > 0 + if g:EditorConfig_max_line_indicator == 'line' + " setlocal colorcolumn+=+1 + let l:cocol = getbufvar(a:bufnr, '&colorcolumn') + if !empty(l:cocol) + let l:cocol .= ',' + endif + let l:cocol .= '+1' + call setbufvar(a:bufnr, '&colorcolumn', l:cocol) + elseif g:EditorConfig_max_line_indicator == 'fill' && + \ l:max_line_length < getbufvar(a:bufnr, '&columns') + " Fill only if the columns of screen is large enough + call setbufvar(a:bufnr, '&colorcolumn', + \ join(range(l:max_line_length+1, + \ getbufvar(a:bufnr, '&columns')), + \ ',')) + elseif g:EditorConfig_max_line_indicator == 'exceeding' + call setbufvar(a:bufnr, '&colorcolumn', '') + for l:match in getmatches() + if get(l:match, 'group', '') == 'ColorColumn' + call matchdelete(get(l:match, 'id')) + endif + endfor + call matchadd('ColorColumn', + \ '\%' . (l:max_line_length + 1) . 'v.', 100) + elseif g:EditorConfig_max_line_indicator == 'fillexceeding' + let &l:colorcolumn = '' + for l:match in getmatches() + if get(l:match, 'group', '') == 'ColorColumn' + call matchdelete(get(l:match, 'id')) + endif + endfor + call matchadd('ColorColumn', + \ '\%'. (l:max_line_length + 1) . 'v.\+', -1) + endif + endif + endif + endif + + call editorconfig#ApplyHooks(a:config) +endfunction + +" }}}1 + +function! s:TrimTrailingWhitespace() " {{{1 + " Called from within a buffer-specific autocmd, so we can use '%' + if getbufvar('%', '&modifiable') + " don't lose user position when trimming trailing whitespace + let s:view = winsaveview() + try + silent! keeppatterns keepjumps %s/\s\+$//e + finally + call winrestview(s:view) + endtry + endif +endfunction " }}}1 + +function! s:IsRuleActive(name, config) " {{{1 + return index(g:EditorConfig_disable_rules, a:name) < 0 && + \ has_key(a:config, a:name) +endfunction "}}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vim: fdm=marker fdc=3 diff --git a/runtime/pack/dist/opt/justify/plugin/justify.vim b/runtime/pack/dist/opt/justify/plugin/justify.vim new file mode 100644 index 0000000..4ef3bf9 --- /dev/null +++ b/runtime/pack/dist/opt/justify/plugin/justify.vim @@ -0,0 +1,316 @@ +" Function to left and right align text. +" +" Written by: Preben "Peppe" Guldberg <c928400@student.dtu.dk> +" Created: 980806 14:13 (or around that time anyway) +" Revised: 001103 00:36 (See "Revisions" below) + + +" function Justify( [ textwidth [, maxspaces [, indent] ] ] ) +" +" Justify() will left and right align a line by filling in an +" appropriate amount of spaces. Extra spaces are added to existing +" spaces starting from the right side of the line. As an example, the +" following documentation has been justified. +" +" The function takes the following arguments: + +" textwidth argument +" ------------------ +" If not specified, the value of the 'textwidth' option is used. If +" 'textwidth' is zero a value of 80 is used. +" +" Additionally the arguments 'tw' and '' are accepted. The value of +" 'textwidth' will be used. These are handy, if you just want to specify +" the maxspaces argument. + +" maxspaces argument +" ------------------ +" If specified, alignment will only be done, if the longest space run +" after alignment is no longer than maxspaces. +" +" An argument of '' is accepted, should the user like to specify all +" arguments. +" +" To aid user defined commands, negative values are accepted aswell. +" Using a negative value specifies the default behaviour: any length of +" space runs will be used to justify the text. + +" indent argument +" --------------- +" This argument specifies how a line should be indented. The default is +" to keep the current indentation. +" +" Negative values: Keep current amount of leading whitespace. +" Positive values: Indent all lines with leading whitespace using this +" amount of whitespace. +" +" Note that the value 0, needs to be quoted as a string. This value +" leads to a left flushed text. +" +" Additionally units of 'shiftwidth'/'sw' and 'tabstop'/'ts' may be +" added. In this case, if the value of indent is positive, the amount of +" whitespace to be added will be multiplied by the value of the +" 'shiftwidth' and 'tabstop' settings. If these units are used, the +" argument must be given as a string, eg. Justify('','','2sw'). +" +" If the values of 'sw' or 'tw' are negative, they are treated as if +" they were 0, which means that the text is flushed left. There is no +" check if a negative number prefix is used to change the sign of a +" negative 'sw' or 'ts' value. +" +" As with the other arguments, '' may be used to get the default +" behaviour. + + +" Notes: +" +" If the line, adjusted for space runs and leading/trailing whitespace, +" is wider than the used textwidth, the line will be left untouched (no +" whitespace removed). This should be equivalent to the behaviour of +" :left, :right and :center. +" +" If the resulting line is shorter than the used textwidth it is left +" untouched. +" +" All space runs in the line are truncated before the alignment is +" carried out. +" +" If you have set 'noexpandtab', :retab! is used to replace space runs +" with whitespace using the value of 'tabstop'. This should be +" conformant with :left, :right and :center. +" +" If joinspaces is set, an extra space is added after '.', '?' and '!'. +" If 'cpooptions' include 'j', extra space is only added after '.'. +" (This may on occasion conflict with maxspaces.) + + +" Related mappings: +" +" Mappings that will align text using the current text width, using at +" most four spaces in a space run and keeping current indentation. +nmap _j :%call Justify('tw',4)<CR> +vmap _j :call Justify('tw',4)<CR> +" +" Mappings that will remove space runs and format lines (might be useful +" prior to aligning the text). +nmap ,gq :%s/\s\+/ /g<CR>gq1G +vmap ,gq :s/\s\+/ /g<CR>gvgq + + +" User defined command: +" +" The following is an ex command that works as a shortcut to the Justify +" function. Arguments to Justify() can be added after the command. +com! -range -nargs=* Justify <line1>,<line2>call Justify(<f-args>) +" +" The following commands are all equivalent: +" +" 1. Simplest use of Justify(): +" :call Justify() +" :Justify +" +" 2. The _j mapping above via the ex command: +" :%Justify tw 4 +" +" 3. Justify visualised text at 72nd column while indenting all +" previously indented text two shiftwidths +" :'<,'>call Justify(72,'','2sw') +" :'<,'>Justify 72 -1 2sw +" +" This documentation has been justified using the following command: +":se et|kz|1;/^" function Justify(/+,'z-g/^" /s/^" //|call Justify(70,3)|s/^/" / + +" Revisions: +" 001103: If 'joinspaces' was set, calculations could be wrong. +" Tabs at start of line could also lead to errors. +" Use setline() instead of "exec 's/foo/bar/' - safer. +" Cleaned up the code a bit. +" +" Todo: Convert maps to the new script specific form + +" Error function +function! Justify_error(message) + echohl Error + echo "Justify([tw, [maxspaces [, indent]]]): " . a:message + echohl None +endfunction + + +" Now for the real thing +function! Justify(...) range + + if a:0 > 3 + call Justify_error("Too many arguments (max 3)") + return 1 + endif + + " Set textwidth (accept 'tw' and '' as arguments) + if a:0 >= 1 + if a:1 =~ '^\(tw\)\=$' + let tw = &tw + elseif a:1 =~ '^\d\+$' + let tw = a:1 + else + call Justify_error("tw must be a number (>0), '' or 'tw'") + return 2 + endif + else + let tw = &tw + endif + if tw == 0 + let tw = 80 + endif + + " Set maximum number of spaces between WORDs + if a:0 >= 2 + if a:2 == '' + let maxspaces = tw + elseif a:2 =~ '^-\d\+$' + let maxspaces = tw + elseif a:2 =~ '^\d\+$' + let maxspaces = a:2 + else + call Justify_error("maxspaces must be a number or ''") + return 3 + endif + else + let maxspaces = tw + endif + if maxspaces <= 1 + call Justify_error("maxspaces should be larger than 1") + return 4 + endif + + " Set the indentation style (accept sw and ts units) + let indent_fix = '' + if a:0 >= 3 + if (a:3 == '') || a:3 =~ '^-[1-9]\d*\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = -1 + elseif a:3 =~ '^-\=0\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = 0 + elseif a:3 =~ '^\d\+\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = substitute(a:3, '\D', '', 'g') + elseif a:3 =~ '^\(shiftwidth\|sw\|tabstop\|ts\)$' + let indent = 1 + else + call Justify_error("indent: a number with 'sw'/'ts' unit") + return 5 + endif + if indent >= 0 + while indent > 0 + let indent_fix = indent_fix . ' ' + let indent = indent - 1 + endwhile + let indent_sw = 0 + if a:3 =~ '\(shiftwidth\|sw\)' + let indent_sw = &sw + elseif a:3 =~ '\(tabstop\|ts\)' + let indent_sw = &ts + endif + let indent_fix2 = '' + while indent_sw > 0 + let indent_fix2 = indent_fix2 . indent_fix + let indent_sw = indent_sw - 1 + endwhile + let indent_fix = indent_fix2 + endif + else + let indent = -1 + endif + + " Avoid substitution reports + let save_report = &report + set report=1000000 + + " Check 'joinspaces' and 'cpo' + if &js == 1 + if &cpo =~ 'j' + let join_str = '\(\. \)' + else + let join_str = '\([.!?!] \)' + endif + endif + + let cur = a:firstline + while cur <= a:lastline + + let str_orig = getline(cur) + let save_et = &et + set et + exec cur . "retab" + let &et = save_et + let str = getline(cur) + + let indent_str = indent_fix + let indent_n = strlen(indent_str) + " Shall we remember the current indentation + if indent < 0 + let indent_orig = matchstr(str_orig, '^\s*') + if strlen(indent_orig) > 0 + let indent_str = indent_orig + let indent_n = strlen(matchstr(str, '^\s*')) + endif + endif + + " Trim trailing, leading and running whitespace + let str = substitute(str, '\s\+$', '', '') + let str = substitute(str, '^\s\+', '', '') + let str = substitute(str, '\s\+', ' ', 'g') + let str_n = strdisplaywidth(str) + + " Possible addition of space after punctuation + if exists("join_str") + let str = substitute(str, join_str, '\1 ', 'g') + endif + let join_n = strdisplaywidth(str) - str_n + + " Can extraspaces be added? + " Note that str_n may be less than strlen(str) [joinspaces above] + if strdisplaywidth(str) <= tw - indent_n && str_n > 0 + " How many spaces should be added + let s_add = tw - str_n - indent_n - join_n + let s_nr = strlen(substitute(str, '\S', '', 'g') ) - join_n + let s_dup = s_add / s_nr + let s_mod = s_add % s_nr + + " Test if the changed line fits with tw + if 0 <= (str_n + (maxspaces - 1)*s_nr + indent_n) - tw + + " Duplicate spaces + while s_dup > 0 + let str = substitute(str, '\( \+\)', ' \1', 'g') + let s_dup = s_dup - 1 + endwhile + + " Add extra spaces from the end + while s_mod > 0 + let str = substitute(str, '\(\(\s\+\S\+\)\{' . s_mod . '}\)$', ' \1', '') + let s_mod = s_mod - 1 + endwhile + + " Indent the line + if indent_n > 0 + let str = substitute(str, '^', indent_str, '' ) + endif + + " Replace the line + call setline(cur, str) + + " Convert to whitespace + if &et == 0 + exec cur . 'retab!' + endif + + endif " Change of line + endif " Possible change + + let cur = cur + 1 + endwhile + + norm ^ + + let &report = save_report + +endfunction + +" EOF vim: tw=78 ts=8 sw=4 sts=4 noet ai diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim new file mode 100644 index 0000000..dc2aba6 --- /dev/null +++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim @@ -0,0 +1,778 @@ +" matchit.vim: (global plugin) Extended "%" matching +" autload script of matchit plugin, see ../plugin/matchit.vim +" Last Change: Jan 24, 2022 + +" Neovim does not support scriptversion +if has("vimscript-4") + scriptversion 4 +endif + +let s:last_mps = "" +let s:last_words = ":" +let s:patBR = "" + +let s:save_cpo = &cpo +set cpo&vim + +" 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 = '<C-]>' +" let g:match_autoCR = '<C-CR>' +" if exists("g:match_auto") +" execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls' +" endif +" if exists("g:match_autoCR") +" execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>' +" endif +" if exists("g:match_gthhoh") +" execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>' +" endif " gthhoh = "Get the heck out of here!" + +let s:notslash = '\\\@1<!\%(\\\\\)*' + +function s:RestoreOptions() + " In s:CleanUp(), :execute "set" restore_options . + let restore_options = "" + if get(b:, 'match_ignorecase', &ic) != &ic + let restore_options ..= (&ic ? " " : " no") .. "ignorecase" + let &ignorecase = b:match_ignorecase + endif + if &ve != '' + let restore_options = " ve=" .. &ve .. restore_options + set ve= + endif + if &smartcase + let restore_options = " smartcase " .. restore_options + set nosmartcase + endif + return restore_options +endfunction + +function matchit#Match_wrapper(word, forward, mode) range + let restore_options = s:RestoreOptions() + " In s:CleanUp(), we may need to check whether the cursor moved forward. + let startpos = [line("."), col(".")] + " if a count has been applied, use the default [count]% mode (see :h N%) + if v:count + exe "normal! " .. v:count .. "%" + return s:CleanUp(restore_options, a:mode, startpos) + end + if a:mode =~# "v" && mode(1) =~# 'ni' + exe "norm! gv" + elseif a:mode == "o" && mode(1) !~# '[vV]' + exe "norm! v" + " If this function was called from Visual mode, make sure that the cursor + " is at the correct end of the Visual range: + elseif a:mode == "v" + execute "normal! gv\<Esc>" + let startpos = [line("."), col(".")] + endif + + " 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 = "" + elseif b:match_words =~ ":" + let match_words = b:match_words + else + " Allow b:match_words = "GetVimMatchWords()" . + 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_mps = &mps + " quote the special chars in 'matchpairs', replace [,:] with \| and then + " append the builtin pairs (/*, */, #if, #ifdef, #ifndef, #else, #elif, + " #endif) + let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") .. + \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + " s:all = pattern with all the keywords + let match_words = match_words .. (strlen(match_words) ? "," : "") .. default + let s:last_words = match_words + 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') + " Just in case there are too many '\(...)' groups inside the pattern, make + " sure to use \%(...) groups, so that error E872 can be avoided + let s:all = substitute(s:all, '\\(', '\\%(', 'g') + let s:all = '\%(' .. s:all .. '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + " Reconstruct the version with unresolved backrefs. + let s:patBR = substitute(match_words .. ',', + \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') + let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g') + 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(startpos[0]) + 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, startpos) + 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, startpos[1]-1) + let curcol = match(matchline, regexp) + " If there is no match, give up. + if curcol == -1 + return s:CleanUp(restore_options, a:mode, startpos) + 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 + " + " 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, s: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 view = winsaveview() + call cursor(0, curcol + 1) + 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) + if &selection isnot# 'inclusive' && a:mode == 'v' + " move cursor one pos to the right, because selection is not inclusive + " add virtualedit=onemore, to make it work even when the match ends the + " line + if !(col('.') < col('$')-1) + let eolmark=1 " flag to set a mark on eol (since we cannot move there) + endif + norm! l + endif + let final_position = "call cursor(" .. line(".") .. "," .. col(".") .. ")" + " Restore cursor position and original screen. + call winrestview(view) + normal! m' + if sp_return > 0 + execute final_position + endif + if exists('eolmark') && eolmark + call setpos("''", [0, line('.'), col('$'), 0]) " set mark on the eol + endif + return s:CleanUp(restore_options, a:mode, startpos, 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, startpos, ...) + if strlen(a:options) + execute "set" a:options + endif + " 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:startpos[0] < line(".")) || + \ (a:startpos[0] == line(".") && a:startpos[1] < 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\+\)>:</\1>' +" a:prefix = '^.\{3}\(' +" a:group = '<\(\k\+\)>:</\(\k\+\)>' +" a:suffix = '\).\{2}$' +" a:matchline = "123<tag>12" or "123</tag>12" +" then extract "tag" from a:matchline and return "<tag>:</tag>" . +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 d = 0 + while d < 10 + if tailBR =~ s:notslash .. '\\' .. 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 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 = "<pat1>,<pat2>,..." +" a:1 = "<alt1>,<alt2>,..." +" If <patn> is the first pattern that matches a:string then return <patn> +" if no optional arguments are given; return <patn>,<altn> 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 + +fun! matchit#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<CR> + " match = bit of text that is recognized as a match + amenu &Matchit.&match :echo b:match_match<CR> + " curcol = cursor column of the start of the matching text + amenu &Matchit.&curcol :echo b:match_col<CR> + " wholeBR = matching group, original version + amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR> + " iniBR = 'if' piece, original version + amenu &Matchit.ini&BR :echo b:match_iniBR<CR> + " ini = 'if' piece, with all backrefs resolved from match + amenu &Matchit.&ini :echo b:match_ini<CR> + " tail = 'else\|endif' piece, with all backrefs resolved from match + amenu &Matchit.&tail :echo b:match_tail<CR> + " fin = 'endif' piece, with all backrefs resolved from match + amenu &Matchit.&word :echo b:match_word<CR> + " '\'.d in ini refers to the same thing as '\'.table[d] in word. + amenu &Matchit.t&able :echo '0:' .. b:match_table .. ':9'<CR> +endfun + +" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW" +" or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W". +" Return a "mark" for the original position, so that +" let m = MultiMatch("bW", "n") ... call winrestview(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! matchit#MultiMatch(spflag, mode) + let restore_options = s:RestoreOptions() + let startpos = [line("."), col(".")] + " save v:count1 variable, might be reset from the restore_cursor command + let level = v:count1 + if a:mode == "o" && mode(1) !~# '[vV]' + exe "norm! v" + endif + + " 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 matchit#Match_wrapper(). + 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 + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") .. + \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + let s:last_mps = &mps + let match_words = match_words .. (strlen(match_words) ? "," : "") .. default + let s:last_words = match_words + 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, '[,:]\+', '\\|', 'g') .. '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + " Reconstruct the version with unresolved backrefs. + let s:patBR = substitute(match_words .. ',', + \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') + let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g') + 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 matchit#Match_wrapper(). + " - maybe even more functionality should be split off + " - into separate functions! + let openlist = split(s:pat .. ',', s:notslash .. '\zs:.\{-}' .. s:notslash .. ',') + let midclolist = split(',' .. s:pat, s:notslash .. '\zs,.\{-}' .. s:notslash .. ':') + call map(midclolist, {-> split(v:val, s:notslash .. ':')}) + let closelist = [] + let middlelist = [] + call map(midclolist, {i,v -> [extend(closelist, v[-1 : -1]), + \ extend(middlelist, v[0 : -2])]}) + call map(openlist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + call map(middlelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + call map(closelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + let open = join(openlist, ',') + let middle = join(middlelist, ',') + let close = join(closelist, ',') + 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 view = winsaveview() + + " Third step: call searchpair(). + " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. + let openpat = substitute(open, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let openpat = substitute(openpat, ',', '\\|', 'g') + let closepat = substitute(close, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let closepat = substitute(closepat, ',', '\\|', 'g') + let middlepat = substitute(middle, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let middlepat = substitute(middlepat, ',', '\\|', 'g') + + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = '0' + else + try + execute "if " .. skip .. "| let skip = '0' | endif" + catch /^Vim\%((\a\+)\)\=:E363/ + " We won't find anything, so skip searching, should keep Vim responsive. + return {} + endtry + endif + mark ' + while level + if searchpair(openpat, middlepat, closepat, a:spflag, skip) < 1 + call s:CleanUp(restore_options, a:mode, startpos) + return {} + endif + let level = level - 1 + endwhile + + " Restore options and return a string to restore the original position. + call s:CleanUp(restore_options, a:mode, startpos) + return view +endfun + +" Search backwards for "if" or "while" or "<tag>" or ... +" and return "endif" or "endwhile" or "</tag>" or ... . +" For now, this uses b:match_words and the same script variables +" as matchit#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 = matchit#MultiMatch("bW") +" +" if startpos == "" +" return "" +" endif +" " - TODO: figure out whether 'if' or '<tag>' 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("<cword>") +" 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:et: diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt new file mode 100644 index 0000000..d072d59 --- /dev/null +++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -0,0 +1,412 @@ +*matchit.txt* Extended "%" matching + +For instructions on installing this file, type + `:help matchit-install` +inside Vim. + +For Vim version 9.0. Last change: 2023 June 28 + + + VIM REFERENCE MANUAL by Benji Fisher et al + +*matchit* *matchit.vim* + +1. Extended matching with "%" |matchit-intro| +2. Activation |matchit-activate| +3. Configuration |matchit-configure| +4. Supporting a New Language |matchit-newlang| +5. Known Bugs and Limitations |matchit-bugs| + +The functionality mentioned here is a plugin, see |add-plugin|. +This plugin is only available if 'compatible' is not set. + +============================================================================== +1. Extended matching with "%" *matchit-intro* + + *matchit-%* +% Cycle forward through matching groups, such as "if", "else", "endif", + as specified by |b:match_words|. + + *g%* *v_g%* *o_g%* +g% Cycle backwards through matching groups, as specified by + |b:match_words|. For example, go from "if" to "endif" to "else". + + *[%* *v_[%* *o_[%* +[% Go to [count] previous unmatched group, as specified by + |b:match_words|. Similar to |[{|. + + *]%* *v_]%* *o_]%* +]% Go to [count] next unmatched group, as specified by + |b:match_words|. Similar to |]}|. + + *v_a%* +a% In Visual mode, select the matching group, as specified by + |b:match_words|, containing the cursor. Similar to |v_a[|. + A [count] is ignored, and only the first character of the closing + pattern is selected. + +In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, +bracket, or paren to its match. This can be configured with the 'matchpairs' +option. The matchit plugin extends this in several ways: + + You can match whole words, such as "if" and "endif", not just + single characters. You can also specify a |regular-expression|. + You can define groups with more than two words, such as "if", + "else", "endif". Banging on the "%" key will cycle from the "if" to + the first "else", the next "else", ..., the closing "endif", and back + to the opening "if". Nested structures are skipped. Using |g%| goes + in the reverse direction. + By default, words inside comments and strings are ignored, unless + the cursor is inside a comment or string when you type "%". If the + only thing you want to do is modify the behavior of "%" so that it + behaves this way, you do not have to define |b:match_words|, since the + script uses the 'matchpairs' option as well as this variable. + +See |matchit-details| for details on what the script does, and |b:match_words| +for how to specify matching patterns. + +MODES: *matchit-modes* *matchit-v_%* *matchit-o_%* + +Mostly, % and related motions (|g%| and |[%| and |]%|) should just work like built-in +|motion| commands in |Operator-pending| and |Visual| modes (as of 8.1.648) + +LANGUAGES: *matchit-languages* + +Currently, the following languages are supported: Ada, ASP with VBS, Csh, +DTD, Entity, Essbase, Fortran, HTML, JSP (same as HTML), LaTeX, Lua, Pascal, +SGML, Shell, Tcsh, Vim, XML. Other languages may already have support via +the default |filetype-plugin|s in the standard vim distribution. + +To support a new language, see |matchit-newlang| below. + +DETAILS: *matchit-details* *matchit-parse* + +Here is an outline of what matchit.vim does each time you hit the "%" key. If +there are |backref|s in |b:match_words| then the first step is to produce a +version in which these back references have been eliminated; if there are no +|backref|s then this step is skipped. This step is called parsing. For +example, "\(foo\|bar\):end\1" is parsed to yield +"\(foo\|bar\):end\(foo\|bar\)". This can get tricky, especially if there are +nested groups. If debugging is turned on, the parsed version is saved as +|b:match_pat|. + + *matchit-choose* +Next, the script looks for a word on the current line that matches the pattern +just constructed. It includes the patterns from the 'matchpairs' option. +The goal is to do what you expect, which turns out to be a little complicated. +The script follows these rules: + + Insist on a match that ends on or after the cursor. + Prefer a match that includes the cursor position (that is, one that + starts on or before the cursor). + Prefer a match that starts as close to the cursor as possible. + If more than one pattern in |b:match_words| matches, choose the one + that is listed first. + +Examples: + + Suppose you > + :let b:match_words = '<:>,<tag>:</tag>' +< and hit "%" with the cursor on or before the "<" in "a <tag> is born". + The pattern '<' comes first, so it is preferred over '<tag>', which + also matches. If the cursor is on the "t", however, then '<tag>' is + preferred, because this matches a bit of text containing the cursor. + If the two groups of patterns were reversed then '<' would never be + preferred. + + Suppose you > + :let b:match_words = 'if:end if' +< (Note the space!) and hit "%" with the cursor at the end of "end if". + Then "if" matches, which is probably not what you want, but if the + cursor starts on the "end " then "end if" is chosen. (You can avoid + this problem by using a more complicated pattern.) + +If there is no match, the cursor does not move. (Before version 1.13 of the +script, it would fall back on the usual behavior of |%|). If debugging is +turned on, the matched bit of text is saved as |b:match_match| and the cursor +column of the start of the match is saved as |b:match_col|. + +Next, the script looks through |b:match_words| (original and parsed versions) +for the group and pattern that match. If debugging is turned on, the group is +saved as |b:match_ini| (the first pattern) and |b:match_tail| (the rest). If +there are |backref|s then, in addition, the matching pattern is saved as +|b:match_word| and a table of translations is saved as |b:match_table|. If +there are |backref|s, these are determined from the matching pattern and +|b:match_match| and substituted into each pattern in the matching group. + +The script decides whether to search forwards or backwards and chooses +arguments for the |searchpair()| function. Then, the cursor is moved to the +start of the match, and |searchpair()| is called. By default, matching +structures inside strings and comments are ignored. This can be changed by +setting |b:match_skip|. + +============================================================================== +2. Activation *matchit-activate* + +To use the matchit plugin add this line to your |vimrc|: > + packadd! matchit + +The script should start working the next time you start Vim. + +To use the matchit plugin after Vim has started, execute this command: > + packadd matchit + +(Earlier versions of the script did nothing unless a |buffer-variable| named +|b:match_words| was defined. Even earlier versions contained autocommands +that set this variable for various file types. Now, |b:match_words| is +defined in many of the default |filetype-plugin|s instead.) + +For a new language, you can add autocommands to the script or to your vimrc +file, but the recommended method is to add a line such as > + let b:match_words = '\<foo\>:\<bar\>' +to the |filetype-plugin| for your language. See |b:match_words| below for how +this variable is interpreted. + +TROUBLESHOOTING *matchit-troubleshoot* + +The script should work in most installations of Vim. It may not work if Vim +was compiled with a minimal feature set, for example if the |+syntax| option +was not enabled. If your Vim has support for syntax compiled in, but you do +not have |syntax| highlighting turned on, matchit.vim should work, but it may +fail to skip matching groups in comments and strings. If the |filetype| +mechanism is turned off, the |b:match_words| variable will probably not be +defined automatically. + +2.1 Temporarily disable the matchit plugin *matchit-disable* *:MatchDisable* + +To temporarily reset the plugins, that are setup you can run the following +command: > + :MatchDisable + +This will delete all the defined key mappings to the Vim default. +Now the "%" command will work like before loading the plugin |%| + +2.2 Re-enable the matchit plugin *:MatchEnable* + +To re-enable the plugin, after it was disabled, use the following command: > + :MatchEnable + +This will resetup the key mappings. + +============================================================================== +3. Configuration *matchit-configure* + +There are several variables that govern the behavior of matchit.vim. Note +that these are variables local to the buffer, not options, so use |:let| to +define them, not |:set|. Some of these variables have values that matter; for +others, it only matters whether the variable has been defined. All of these +can be defined in the |filetype-plugin| or autocommand that defines +|b:match_words| or "on the fly." + +The main variable is |b:match_words|. It is described in the section below on +supporting a new language. + + *MatchError* *matchit-hl* *matchit-highlight* +MatchError is the highlight group for error messages from the script. By +default, it is linked to WarningMsg. If you do not want to be bothered by +error messages, you can define this to be something invisible. For example, +if you use the GUI version of Vim and your command line is normally white, you +can do > + :hi MatchError guifg=white guibg=white +< + *b:match_ignorecase* +If you > + :let b:match_ignorecase = 1 +then matchit.vim acts as if 'ignorecase' is set: for example, "end" and "END" +are equivalent. If you > + :let b:match_ignorecase = 0 +then matchit.vim treats "end" and "END" differently. (There will be no +b:match_infercase option unless someone requests it.) + + *b:match_debug* +Define b:match_debug if you want debugging information to be saved. See +|matchit-debug|, below. + + *b:match_skip* +If b:match_skip is defined, it is passed as the skip argument to +|searchpair()|. This controls when matching structures are skipped, or +ignored. By default, they are ignored inside comments and strings, as +determined by the |syntax| mechanism. (If syntax highlighting is turned off, +nothing is skipped.) You can set b:match_skip to a string, which evaluates to +a non-zero, numerical value if the match is to be skipped or zero if the match +should not be skipped. In addition, the following special values are +supported by matchit.vim: + 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 +(The "s" is meant to suggest "syntax", and the "r" is meant to suggest +"regular expression".) + +Examples: + + You can get the default behavior with > + :let b:match_skip = 's:comment\|string' +< + If you want to skip matching structures unless they are at the start + of the line (ignoring whitespace) then you can > + :let b:match_skip = 'R:^\s*' +< Do not do this if strings or comments can span several lines, since + the normal syntax checking will not be done if you set b:match_skip. + + In LaTeX, since "%" is used as the comment character, you can > + :let b:match_skip = 'r:%' +< Unfortunately, this will skip anything after "\%", an escaped "%". To + allow for this, and also "\\%" (an escaped backslash followed by the + comment character) you can > + :let b:match_skip = 'r:\(^\|[^\\]\)\(\\\\\)*%' +< + See the $VIMRUNTIME/ftplugin/vim.vim for an example that uses both + syntax and a regular expression. + +============================================================================== +4. Supporting a New Language *matchit-newlang* + *b:match_words* +In order for matchit.vim to support a new language, you must define a suitable +pattern for |b:match_words|. You may also want to set some of the +|matchit-configure| variables, as described above. If your language has a +complicated syntax, or many keywords, you will need to know something about +Vim's |regular-expression|s. + +The format for |b:match_words| is similar to that of the 'matchpairs' option: +it is a comma (,)-separated list of groups; each group is a colon(:)-separated +list of patterns (regular expressions). Commas and backslashes that are part +of a pattern should be escaped with backslashes ('\:' and '\,'). It is OK to +have only one group; the effect is undefined if a group has only one pattern. +A simple example is > + :let b:match_words = '\<if\>:\<endif\>,' + \ . '\<while\>:\<continue\>:\<break\>:\<endwhile\>' +(In Vim regular expressions, |\<| and |\>| denote word boundaries. Thus "if" +matches the end of "endif" but "\<if\>" does not.) Then banging on the "%" +key will bounce the cursor between "if" and the matching "endif"; and from +"while" to any matching "continue" or "break", then to the matching "endwhile" +and back to the "while". It is almost always easier to use |literal-string|s +(single quotes) as above: '\<if\>' rather than "\\<if\\>" and so on. + +Exception: If the ":" character does not appear in b:match_words, then it is +treated as an expression to be evaluated. For example, > + :let b:match_words = 'GetMatchWords()' +allows you to define a function. This can return a different string depending +on the current syntax, for example. + +Once you have defined the appropriate value of |b:match_words|, you will +probably want to have this set automatically each time you edit the +appropriate file type. The recommended way to do this is by adding the +definition to a |filetype-plugin| file. + +Tips: Be careful that your initial pattern does not match your final pattern. +See the example above for the use of word-boundary expressions. It is usually +better to use ".\{-}" (as many as necessary) instead of ".*" (as many as +possible). See |\{-|. For example, in the string "<tag>label</tag>", "<.*>" +matches the whole string whereas "<.\{-}>" and "<[^>]*>" match "<tag>" and +"</tag>". + + *matchit-spaces* *matchit-s:notend* +If "if" is to be paired with "end if" (Note the space!) then word boundaries +are not enough. Instead, define a regular expression s:notend that will match +anything but "end" and use it as follows: > + :let s:notend = '\%(\<end\s\+\)\@<!' + :let b:match_words = s:notend . '\<if\>:\<end\s\+if\>' +< *matchit-s:sol* +This is a simplified version of what is done for Ada. The s:notend is a +|script-variable|. Similarly, you may want to define a start-of-line regular +expression > + :let s:sol = '\%(^\|;\)\s*' +if keywords are only recognized after the start of a line or after a +semicolon (;), with optional white space. + + *matchit-backref* *matchit-\1* +In any group, the expressions |\1|, |\2|, ..., |\9| refer to parts of the +INITIAL pattern enclosed in |\(|escaped parentheses|\)|. These are referred +to as back references, or backrefs. For example, > + :let b:match_words = '\<b\(o\+\)\>:\(h\)\1\>' +means that "bo" pairs with "ho" and "boo" pairs with "hoo" and so on. Note +that "\1" does not refer to the "\(h\)" in this example. If you have +"\(nested \(parentheses\)\) then "\d" refers to the d-th "\(" and everything +up to and including the matching "\)": in "\(nested\(parentheses\)\)", "\1" +refers to everything and "\2" refers to "\(parentheses\)". If you use a +variable such as |s:notend| or |s:sol| in the previous paragraph then remember +to count any "\(" patterns in this variable. You do not have to count groups +defined by |\%(\)|. + +It should be possible to resolve back references from any pattern in the +group. For example, > + :let b:match_words = '\(foo\)\(bar\):more\1:and\2:end\1\2' +would not work because "\2" cannot be determined from "morefoo" and "\1" +cannot be determined from "andbar". On the other hand, > + :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +should work (and have the same effect as "foobar:barfoo:endfoobar"), although +this has not been thoroughly tested. + +You can use |zero-width| patterns such as |\@<=| and |\zs|. (The latter has +not been thoroughly tested in matchit.vim.) For example, if the keyword "if" +must occur at the start of the line, with optional white space, you might use +the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of +at the start of the line. For another example, if HTML had only one tag then +one could > + :let b:match_words = '<:>,<\@<=tag>:<\@<=/tag>' +so that "%" can bounce between matching "<" and ">" pairs or (starting on +"tag" or "/tag") between matching tags. Without the |\@<=|, the script would +bounce from "tag" to the "<" in "</tag>", and another "%" would not take you +back to where you started. + +DEBUGGING *matchit-debug* *:MatchDebug* + +If you are having trouble figuring out the appropriate definition of +|b:match_words| then you can take advantage of the same information I use when +debugging the script. This is especially true if you are not sure whether +your patterns or my script are at fault! To make this more convenient, I have +made the command :MatchDebug, which defines the variable |b:match_debug| and +creates a Matchit menu. This menu makes it convenient to check the values of +the variables described below. You will probably also want to read +|matchit-details| above. + +Defining the variable |b:match_debug| causes the script to set the following +variables, each time you hit the "%" key. Several of these are only defined +if |b:match_words| includes |backref|s. + + *b:match_pat* +The b:match_pat variable is set to |b:match_words| with |backref|s parsed. + *b:match_match* +The b:match_match variable is set to the bit of text that is recognized as a +match. + *b:match_col* +The b:match_col variable is set to the cursor column of the start of the +matching text. + *b:match_wholeBR* +The b:match_wholeBR variable is set to the comma-separated group of patterns +that matches, with |backref|s unparsed. + *b:match_iniBR* +The b:match_iniBR variable is set to the first pattern in |b:match_wholeBR|. + *b:match_ini* +The b:match_ini variable is set to the first pattern in |b:match_wholeBR|, +with |backref|s resolved from |b:match_match|. + *b:match_tail* +The b:match_tail variable is set to the remaining patterns in +|b:match_wholeBR|, with |backref|s resolved from |b:match_match|. + *b:match_word* +The b:match_word variable is set to the pattern from |b:match_wholeBR| that +matches |b:match_match|. + *b:match_table* +The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in +|b:match_word|. + +============================================================================== +5. Known Bugs and Limitations *matchit-bugs* + +Repository: https://github.com/chrisbra/matchit/ +Bugs can be reported at the repository and the latest development snapshot can +also be downloaded there. + +Just because I know about a bug does not mean that it is on my todo list. I +try to respond to reports of bugs that cause real problems. If it does not +cause serious problems, or if there is a work-around, a bug may sit there for +a while. Moral: if a bug (known or not) bothers you, let me know. + +It would be nice if "\0" were recognized as the entire pattern. That is, it +would be nice if "foo:\end\0" had the same effect as "\(foo\):\end\1". I may +try to implement this in a future version. (This is not so easy to arrange as +you might think!) + +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/runtime/pack/dist/opt/matchit/doc/tags b/runtime/pack/dist/opt/matchit/doc/tags new file mode 100644 index 0000000..4ccdc87 --- /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 0000000..d6c735d --- /dev/null +++ b/runtime/pack/dist/opt/matchit/plugin/matchit.vim @@ -0,0 +1,127 @@ +" matchit.vim: (global plugin) Extended "%" matching +" Maintainer: Christian Brabandt +" Version: 1.19 +" Last Change: 2023, June 28th +" Repository: https://github.com/chrisbra/matchit +" Previous URL:http://www.vim.org/script.php?script_id=39 +" Previous Maintainer: Benji Fisher PhD <benji@member.AMS.org> + +" Documentation: +" The documentation is in a separate file: ../doc/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("g:loaded_matchit") || &cp + finish +endif +let g:loaded_matchit = 1 + +let s:save_cpo = &cpo +set cpo&vim + +fun MatchEnable() + nnoremap <silent> <Plug>(MatchitNormalForward) :<C-U>call matchit#Match_wrapper('',1,'n')<CR> + nnoremap <silent> <Plug>(MatchitNormalBackward) :<C-U>call matchit#Match_wrapper('',0,'n')<CR> + xnoremap <silent> <Plug>(MatchitVisualForward) :<C-U>call matchit#Match_wrapper('',1,'v')<CR> + \:if col("''") != col("$") \| exe ":normal! m'" \| endif<cr>gv`` + xnoremap <silent> <Plug>(MatchitVisualBackward) :<C-U>call matchit#Match_wrapper('',0,'v')<CR>m'gv`` + onoremap <silent> <Plug>(MatchitOperationForward) :<C-U>call matchit#Match_wrapper('',1,'o')<CR> + onoremap <silent> <Plug>(MatchitOperationBackward) :<C-U>call matchit#Match_wrapper('',0,'o')<CR> + + " Analogues of [{ and ]} using matching patterns: + nnoremap <silent> <Plug>(MatchitNormalMultiBackward) :<C-U>call matchit#MultiMatch("bW", "n")<CR> + nnoremap <silent> <Plug>(MatchitNormalMultiForward) :<C-U>call matchit#MultiMatch("W", "n")<CR> + xnoremap <silent> <Plug>(MatchitVisualMultiBackward) :<C-U>call matchit#MultiMatch("bW", "n")<CR>m'gv`` + xnoremap <silent> <Plug>(MatchitVisualMultiForward) :<C-U>call matchit#MultiMatch("W", "n")<CR>m'gv`` + onoremap <silent> <Plug>(MatchitOperationMultiBackward) :<C-U>call matchit#MultiMatch("bW", "o")<CR> + onoremap <silent> <Plug>(MatchitOperationMultiForward) :<C-U>call matchit#MultiMatch("W", "o")<CR> + + " text object: + xmap <silent> <Plug>(MatchitVisualTextObject) <Plug>(MatchitVisualMultiBackward)o<Plug>(MatchitVisualMultiForward) + + if !exists("g:no_plugin_maps") + nmap <silent> % <Plug>(MatchitNormalForward) + nmap <silent> g% <Plug>(MatchitNormalBackward) + xmap <silent> % <Plug>(MatchitVisualForward) + xmap <silent> g% <Plug>(MatchitVisualBackward) + omap <silent> % <Plug>(MatchitOperationForward) + omap <silent> g% <Plug>(MatchitOperationBackward) + + " Analogues of [{ and ]} using matching patterns: + nmap <silent> [% <Plug>(MatchitNormalMultiBackward) + nmap <silent> ]% <Plug>(MatchitNormalMultiForward) + xmap <silent> [% <Plug>(MatchitVisualMultiBackward) + xmap <silent> ]% <Plug>(MatchitVisualMultiForward) + omap <silent> [% <Plug>(MatchitOperationMultiBackward) + omap <silent> ]% <Plug>(MatchitOperationMultiForward) + + " Text object + xmap a% <Plug>(MatchitVisualTextObject) + endif +endfun + +fun MatchDisable() + " remove all the setup keymappings + nunmap % + nunmap g% + xunmap % + xunmap g% + ounmap % + ounmap g% + + nunmap [% + nunmap ]% + xunmap [% + xunmap ]% + ounmap [% + ounmap ]% + + xunmap a% +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 matchit#Match_debug() +endif +if !exists(":MatchDisable") + command! -nargs=0 MatchDisable :call MatchDisable() +endif +if !exists(":MatchEnable") + command! -nargs=0 MatchEnable :call MatchEnable() +endif + +call MatchEnable() + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:sts=2:sw=2:et: diff --git a/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim b/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim new file mode 100644 index 0000000..04b48b9 --- /dev/null +++ b/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim @@ -0,0 +1,104 @@ +" When you're writing shell scripts and you are in doubt which test to use, +" which shell environment variables are defined, what the syntax of the case +" statement is, and you need to invoke 'man sh'? +" +" Your problems are over now! +" +" Attached is a Vim script file for turning gvim into a shell script editor. +" It may also be used as an example how to use menus in Vim. +" +" Maintainer: Ada (Haowen) Yu <me@yuhaowen.com> +" Original author: Lennart Schultz <les@dmi.min.dk> (mail unreachable) + +" Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise +" <CR> would not be recognized. See ":help 'cpoptions'". +let s:cpo_save = &cpo +set cpo&vim + +imenu ShellMenu.Statements.for for in <CR>do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.case case in<CR>) ;;<CR>esac<esc>bki <esc>k0elli +imenu ShellMenu.Statements.if if <CR>then<CR><CR>fi<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.if-else if <CR>then<CR><CR>else<CR><CR>fi<esc>ki <esc>kki <esc>kk0elli +imenu ShellMenu.Statements.elif elif <CR>then<CR><CR><esc>ki <esc>kk0elli +imenu ShellMenu.Statements.while while do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.break break +imenu ShellMenu.Statements.continue continue +imenu ShellMenu.Statements.function () {<CR><CR>}<esc>ki <esc>k0i +imenu ShellMenu.Statements.return return +imenu ShellMenu.Statements.return-true return 0 +imenu ShellMenu.Statements.return-false return 1 +imenu ShellMenu.Statements.exit exit +imenu ShellMenu.Statements.shift shift +imenu ShellMenu.Statements.trap trap +imenu ShellMenu.Test.Existence [ -e ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file [ -f ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file\ (not\ empty) [ -s ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ directory [ -d ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ executable [ -x ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ readable [ -r ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ writable [ -w ]<esc>hi +imenu ShellMenu.Test.String\ is\ empty [ x = "x$" ]<esc>hhi +imenu ShellMenu.Test.String\ is\ not\ empty [ x != "x$" ]<esc>hhi +imenu ShellMenu.Test.Strings\ are\ equal [ "" = "" ]<esc>hhhhhhhi +imenu ShellMenu.Test.Strings\ are\ not\ equal [ "" != "" ]<esc>hhhhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ than [ -gt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ equal [ -ge ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ equal [ -eq ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ not\ equal [ -ne ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ than [ -lt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ equal [ -le ]<esc>hhhhhhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ not\ set ${:-}<esc>hhi +imenu ShellMenu.ParmSub.Set\ parm\ to\ word\ if\ not\ set ${:=}<esc>hhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ set\ else\ nothing ${:+}<esc>hhi +imenu ShellMenu.ParmSub.If\ parm\ not\ set\ print\ word\ and\ exit ${:?}<esc>hhi +imenu ShellMenu.SpShVars.Number\ of\ positional\ parameters ${#} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (quoted\ spaces) ${*} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (unquoted\ spaces) ${@} +imenu ShellMenu.SpShVars.Flags\ set ${-} +imenu ShellMenu.SpShVars.Return\ code\ of\ last\ command ${?} +imenu ShellMenu.SpShVars.Process\ number\ of\ this\ shell ${$} +imenu ShellMenu.SpShVars.Process\ number\ of\ last\ background\ command ${!} +imenu ShellMenu.Environ.HOME ${HOME} +imenu ShellMenu.Environ.PATH ${PATH} +imenu ShellMenu.Environ.CDPATH ${CDPATH} +imenu ShellMenu.Environ.MAIL ${MAIL} +imenu ShellMenu.Environ.MAILCHECK ${MAILCHECK} +imenu ShellMenu.Environ.PS1 ${PS1} +imenu ShellMenu.Environ.PS2 ${PS2} +imenu ShellMenu.Environ.IFS ${IFS} +imenu ShellMenu.Environ.SHACCT ${SHACCT} +imenu ShellMenu.Environ.SHELL ${SHELL} +imenu ShellMenu.Environ.LC_CTYPE ${LC_CTYPE} +imenu ShellMenu.Environ.LC_MESSAGES ${LC_MESSAGES} +imenu ShellMenu.Builtins.cd cd +imenu ShellMenu.Builtins.echo echo +imenu ShellMenu.Builtins.eval eval +imenu ShellMenu.Builtins.exec exec +imenu ShellMenu.Builtins.export export +imenu ShellMenu.Builtins.getopts getopts +imenu ShellMenu.Builtins.hash hash +imenu ShellMenu.Builtins.newgrp newgrp +imenu ShellMenu.Builtins.pwd pwd +imenu ShellMenu.Builtins.read read +imenu ShellMenu.Builtins.readonly readonly +imenu ShellMenu.Builtins.return return +imenu ShellMenu.Builtins.times times +imenu ShellMenu.Builtins.type type +imenu ShellMenu.Builtins.umask umask +imenu ShellMenu.Builtins.wait wait +imenu ShellMenu.Set.set set +imenu ShellMenu.Set.unset unset +imenu ShellMenu.Set.Mark\ created\ or\ modified\ variables\ for\ export set -a +imenu ShellMenu.Set.Exit\ when\ command\ returns\ non-zero\ status set -e +imenu ShellMenu.Set.Disable\ file\ name\ expansion set -f +imenu ShellMenu.Set.Locate\ and\ remember\ commands\ when\ being\ looked\ up set -h +imenu ShellMenu.Set.All\ assignment\ statements\ are\ placed\ in\ the\ environment\ for\ a\ command set -k +imenu ShellMenu.Set.Read\ commands\ but\ do\ not\ execute\ them set -n +imenu ShellMenu.Set.Exit\ after\ reading\ and\ executing\ one\ command set -t +imenu ShellMenu.Set.Treat\ unset\ variables\ as\ an\ error\ when\ substituting set -u +imenu ShellMenu.Set.Print\ shell\ input\ lines\ as\ they\ are\ read set -v +imenu ShellMenu.Set.Print\ commands\ and\ their\ arguments\ as\ they\ are\ executed set -x + +" Restore the previous value of 'cpoptions'. +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim b/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim new file mode 100644 index 0000000..8b85be0 --- /dev/null +++ b/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim @@ -0,0 +1,22 @@ +" These macros swap the left and right mouse buttons (for left handed) +" Don't forget to do ":set mouse=a" or the mouse won't work at all +noremap <LeftMouse> <RightMouse> +noremap <2-LeftMouse> <2-RightMouse> +noremap <3-LeftMouse> <3-RightMouse> +noremap <4-LeftMouse> <4-RightMouse> +noremap <LeftDrag> <RightDrag> +noremap <LeftRelease> <RightRelease> +noremap <RightMouse> <LeftMouse> +noremap <2-RightMouse> <2-LeftMouse> +noremap <3-RightMouse> <3-LeftMouse> +noremap <4-RightMouse> <4-LeftMouse> +noremap <RightDrag> <LeftDrag> +noremap <RightRelease> <LeftRelease> +noremap g<LeftMouse> <C-RightMouse> +noremap g<RightMouse> <C-LeftMouse> +noremap! <LeftMouse> <RightMouse> +noremap! <LeftDrag> <RightDrag> +noremap! <LeftRelease> <RightRelease> +noremap! <RightMouse> <LeftMouse> +noremap! <RightDrag> <LeftDrag> +noremap! <RightRelease> <LeftRelease> diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim new file mode 100644 index 0000000..1dce91b --- /dev/null +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -0,0 +1,1810 @@ +" Debugger plugin using gdb. +" +" Author: Bram Moolenaar +" Copyright: Vim license applies, see ":help license" +" Last Change: 2023 Nov 02 +" +" WORK IN PROGRESS - The basics works stable, more to come +" Note: In general you need at least GDB 7.12 because this provides the +" frame= response in MI thread-selected events we need to sync stack to file. +" The one included with "old" MingW is too old (7.6.1), you may upgrade it or +" use a newer version from http://www.equation.com/servlet/equation.cmd?fa=gdb +" +" There are two ways to run gdb: +" - In a terminal window; used if possible, does not work on MS-Windows +" Not used when g:termdebug_use_prompt is set to 1. +" - Using a "prompt" buffer; may use a terminal window for the program +" +" For both the current window is used to view source code and shows the +" current statement from gdb. +" +" USING A TERMINAL WINDOW +" +" Opens two visible terminal windows: +" 1. runs a pty for the debugged program, as with ":term NONE" +" 2. runs gdb, passing the pty of the debugged program +" A third terminal window is hidden, it is used for communication with gdb. +" +" USING A PROMPT BUFFER +" +" Opens a window with a prompt buffer to communicate with gdb. +" Gdb is run as a job with callbacks for I/O. +" On Unix another terminal window is opened to run the debugged program +" On MS-Windows a separate console is opened to run the debugged program +" +" The communication with gdb uses GDB/MI. See: +" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html + +" In case this gets sourced twice. +if exists(':Termdebug') + finish +endif + +" Need either the +terminal feature or +channel and the prompt buffer. +" The terminal feature does not work with gdb on win32. +if has('terminal') && !has('win32') + let s:way = 'terminal' +elseif has('channel') && exists('*prompt_setprompt') + let s:way = 'prompt' +else + if has('terminal') + let s:err = 'Cannot debug, missing prompt buffer support' + else + let s:err = 'Cannot debug, +channel feature is not supported' + endif + command -nargs=* -complete=file -bang Termdebug echoerr s:err + command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err + finish +endif + +let s:keepcpo = &cpo +set cpo&vim + +" The command that starts debugging, e.g. ":Termdebug vim". +" To end type "quit" in the gdb window. +command -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) +command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) + +let s:pc_id = 12 +let s:asm_id = 13 +let s:break_id = 14 " breakpoint number is added to this +let s:stopped = 1 +let s:running = 0 + +let s:parsing_disasm_msg = 0 +let s:asm_lines = [] +let s:asm_addr = '' + +" Take a breakpoint number as used by GDB and turn it into an integer. +" The breakpoint may contain a dot: 123.4 -> 123004 +" The main breakpoint has a zero subid. +func s:Breakpoint2SignNumber(id, subid) + return s:break_id + a:id * 1000 + a:subid +endfunction + +" Define or adjust the default highlighting, using background "new". +" When the 'background' option is set then "old" has the old value. +func s:Highlight(init, old, new) + let default = a:init ? 'default ' : '' + if a:new ==# 'light' && a:old !=# 'light' + exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" + elseif a:new ==# 'dark' && a:old !=# 'dark' + exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" + endif +endfunc + +" Define the default highlighting, using the current 'background' value. +func s:InitHighlight() + call s:Highlight(1, '', &background) + hi default debugBreakpoint term=reverse ctermbg=red guibg=red + hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray +endfunc + +" Setup an autocommand to redefine the default highlight when the colorscheme +" is changed. +func s:InitAutocmd() + augroup TermDebug + autocmd! + autocmd ColorScheme * call s:InitHighlight() + augroup END +endfunc + +" Get the command to execute the debugger as a list, defaults to ["gdb"]. +func s:GetCommand() + if exists('g:termdebug_config') + let cmd = get(g:termdebug_config, 'command', 'gdb') + elseif exists('g:termdebugger') + let cmd = g:termdebugger + else + let cmd = 'gdb' + endif + + return type(cmd) == v:t_list ? copy(cmd) : [cmd] +endfunc + +func s:Echoerr(msg) + echohl ErrorMsg | echom '[termdebug] ' .. a:msg | echohl None +endfunc + +func s:StartDebug(bang, ...) + " First argument is the command to debug, second core file or process ID. + call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) +endfunc + +func s:StartDebugCommand(bang, ...) + " First argument is the command to debug, rest are run arguments. + call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) +endfunc + +func s:StartDebug_internal(dict) + if exists('s:gdbwin') + call s:Echoerr('Terminal debugger already running, cannot run two') + return + endif + let gdbcmd = s:GetCommand() + if !executable(gdbcmd[0]) + call s:Echoerr('Cannot execute debugger program "' .. gdbcmd[0] .. '"') + return + endif + + let s:ptywin = 0 + let s:pid = 0 + let s:asmwin = 0 + let s:asmbuf = 0 + let s:varwin = 0 + let s:varbuf = 0 + + if exists('#User#TermdebugStartPre') + doauto <nomodeline> User TermdebugStartPre + endif + + " Uncomment this line to write logging in "debuglog". + " call ch_logfile('debuglog', 'w') + + let s:sourcewin = win_getid() + + " Remember the old value of 'signcolumn' for each buffer that it's set in, so + " that we can restore the value for all buffers. + let b:save_signcolumn = &signcolumn + let s:signcolumn_buflist = [bufnr()] + + let s:save_columns = 0 + let s:allleft = 0 + let wide = 0 + if exists('g:termdebug_config') + let wide = get(g:termdebug_config, 'wide', 0) + elseif exists('g:termdebug_wide') + let wide = g:termdebug_wide + endif + if wide > 0 + if &columns < wide + let s:save_columns = &columns + let &columns = wide + " If we make the Vim window wider, use the whole left half for the debug + " windows. + let s:allleft = 1 + endif + let s:vertical = 1 + else + let s:vertical = 0 + endif + + " Override using a terminal window by setting g:termdebug_use_prompt to 1. + let use_prompt = 0 + if exists('g:termdebug_config') + let use_prompt = get(g:termdebug_config, 'use_prompt', 0) + elseif exists('g:termdebug_use_prompt') + let use_prompt = g:termdebug_use_prompt + endif + if has('terminal') && !has('win32') && !use_prompt + let s:way = 'terminal' + else + let s:way = 'prompt' + endif + + if s:way == 'prompt' + call s:StartDebug_prompt(a:dict) + else + call s:StartDebug_term(a:dict) + endif + + if s:GetDisasmWindow() + let curwinid = win_getid() + call s:GotoAsmwinOrCreateIt() + call win_gotoid(curwinid) + endif + + if s:GetVariablesWindow() + let curwinid = win_getid() + call s:GotoVariableswinOrCreateIt() + call win_gotoid(curwinid) + endif + + if exists('#User#TermdebugStartPost') + doauto <nomodeline> User TermdebugStartPost + endif +endfunc + +" Use when debugger didn't start or ended. +func s:CloseBuffers() + exe 'bwipe! ' . s:ptybuf + exe 'bwipe! ' . s:commbuf + if s:asmbuf > 0 && bufexists(s:asmbuf) + exe 'bwipe! ' . s:asmbuf + endif + if s:varbuf > 0 && bufexists(s:varbuf) + exe 'bwipe! ' . s:varbuf + endif + let s:running = 0 + unlet! s:gdbwin +endfunc + +func s:CheckGdbRunning() + let gdbproc = term_getjob(s:gdbbuf) + if gdbproc == v:null || job_status(gdbproc) !=# 'run' + call s:Echoerr(string(s:GetCommand()[0]) . ' exited unexpectedly') + call s:CloseBuffers() + return '' + endif + return 'ok' +endfunc + +" Open a terminal window without a job, to run the debugged program in. +func s:StartDebug_term(dict) + let s:ptybuf = term_start('NONE', { + \ 'term_name': 'debugged program', + \ 'vertical': s:vertical, + \ }) + if s:ptybuf == 0 + call s:Echoerr('Failed to open the program terminal window') + return + endif + let pty = job_info(term_getjob(s:ptybuf))['tty_out'] + let s:ptywin = win_getid() + if s:vertical + " Assuming the source code window will get a signcolumn, use two more + " columns for that, thus one less for the terminal window. + exe (&columns / 2 - 1) . "wincmd |" + if s:allleft + " use the whole left column + wincmd H + endif + endif + + " Create a hidden terminal window to communicate with gdb + let s:commbuf = term_start('NONE', { + \ 'term_name': 'gdb communication', + \ 'out_cb': function('s:CommOutput'), + \ 'hidden': 1, + \ }) + if s:commbuf == 0 + call s:Echoerr('Failed to open the communication terminal window') + exe 'bwipe! ' . s:ptybuf + return + endif + let commpty = job_info(term_getjob(s:commbuf))['tty_out'] + + let gdb_args = get(a:dict, 'gdb_args', []) + let proc_args = get(a:dict, 'proc_args', []) + + let gdb_cmd = s:GetCommand() + + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args') + let gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty) + else + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_cmd += ['-quiet'] + " Disable pagination, it causes everything to stop at the gdb + let gdb_cmd += ['-iex', 'set pagination off'] + " Interpret commands while the target is running. This should usually only + " be exec-interrupt, since many commands don't work properly while the + " target is running (so execute during startup). + let gdb_cmd += ['-iex', 'set mi-async on'] + " Open a terminal window to run the debugger. + let gdb_cmd += ['-tty', pty] + " Command executed _after_ startup is done, provides us with the necessary + " feedback + let gdb_cmd += ['-ex', 'echo startupdone\n'] + endif + + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter') + let gdb_cmd = g:termdebug_config.command_filter(gdb_cmd) + endif + + " Adding arguments requested by the user + let gdb_cmd += gdb_args + + call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdbbuf = term_start(gdb_cmd, { + \ 'term_finish': 'close', + \ }) + if s:gdbbuf == 0 + call s:Echoerr('Failed to open the gdb terminal window') + call s:CloseBuffers() + return + endif + let s:gdbwin = win_getid() + + " Wait for the "startupdone" message before sending any commands. + let try_count = 0 + while 1 + if s:CheckGdbRunning() != 'ok' + return + endif + + for lnum in range(1, 200) + if term_getline(s:gdbbuf, lnum) =~ 'startupdone' + let try_count = 9999 + break + endif + endfor + let try_count += 1 + if try_count > 300 + " done or give up after five seconds + break + endif + sleep 10m + endwhile + + " Set arguments to be run. + if len(proc_args) + call term_sendkeys(s:gdbbuf, 'server set args ' . join(proc_args) . "\r") + endif + + " Connect gdb to the communication pty, using the GDB/MI interface. + " Prefix "server" to avoid adding this to the history. + call term_sendkeys(s:gdbbuf, 'server new-ui mi ' . commpty . "\r") + + " Wait for the response to show up, users may not notice the error and wonder + " why the debugger doesn't work. + let try_count = 0 + while 1 + if s:CheckGdbRunning() != 'ok' + return + endif + + let response = '' + for lnum in range(1, 200) + let line1 = term_getline(s:gdbbuf, lnum) + let line2 = term_getline(s:gdbbuf, lnum + 1) + if line1 =~ 'new-ui mi ' + " response can be in the same line or the next line + let response = line1 . line2 + if response =~ 'Undefined command' + call s:Echoerr('Sorry, your gdb is too old, gdb 7.12 is required') + " CHECKME: possibly send a "server show version" here + call s:CloseBuffers() + return + endif + if response =~ 'New UI allocated' + " Success! + break + endif + elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' + " Reading symbols might take a while, try more times + let try_count -= 1 + endif + endfor + if response =~ 'New UI allocated' + break + endif + let try_count += 1 + if try_count > 100 + call s:Echoerr('Cannot check if your gdb works, continuing anyway') + break + endif + sleep 10m + endwhile + + call job_setoptions(term_getjob(s:gdbbuf), {'exit_cb': function('s:EndTermDebug')}) + + " Set the filetype, this can be used to add mappings. + set filetype=termdebug + + call s:StartDebugCommon(a:dict) +endfunc + +" Open a window with a prompt buffer to run gdb in. +func s:StartDebug_prompt(dict) + if s:vertical + vertical new + else + new + endif + let s:gdbwin = win_getid() + let s:promptbuf = bufnr('') + call prompt_setprompt(s:promptbuf, 'gdb> ') + set buftype=prompt + file gdb + call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) + call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) + + if s:vertical + " Assuming the source code window will get a signcolumn, use two more + " columns for that, thus one less for the terminal window. + exe (&columns / 2 - 1) . "wincmd |" + endif + + let gdb_args = get(a:dict, 'gdb_args', []) + let proc_args = get(a:dict, 'proc_args', []) + + let gdb_cmd = s:GetCommand() + " Add -quiet to avoid the intro message causing a hit-enter prompt. + let gdb_cmd += ['-quiet'] + " Disable pagination, it causes everything to stop at the gdb, needs to be run early + let gdb_cmd += ['-iex', 'set pagination off'] + " Interpret commands while the target is running. This should usually only + " be exec-interrupt, since many commands don't work properly while the + " target is running (so execute during startup). + let gdb_cmd += ['-iex', 'set mi-async on'] + " directly communicate via mi2 + let gdb_cmd += ['--interpreter=mi2'] + + " Adding arguments requested by the user + let gdb_cmd += gdb_args + + call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdbjob = job_start(gdb_cmd, { + \ 'exit_cb': function('s:EndPromptDebug'), + \ 'out_cb': function('s:GdbOutCallback'), + \ }) + if job_status(s:gdbjob) != "run" + call s:Echoerr('Failed to start gdb') + exe 'bwipe! ' . s:promptbuf + return + endif + exe $'au BufUnload <buffer={s:promptbuf}> ++once ' .. + \ 'call job_stop(s:gdbjob, ''kill'')' + " Mark the buffer modified so that it's not easy to close. + set modified + let s:gdb_channel = job_getchannel(s:gdbjob) + + let s:ptybuf = 0 + if has('win32') + " MS-Windows: run in a new console window for maximum compatibility + call s:SendCommand('set new-console on') + elseif has('terminal') + " Unix: Run the debugged program in a terminal window. Open it below the + " gdb window. + belowright let s:ptybuf = term_start('NONE', { + \ 'term_name': 'debugged program', + \ }) + if s:ptybuf == 0 + call s:Echoerr('Failed to open the program terminal window') + call job_stop(s:gdbjob) + return + endif + let s:ptywin = win_getid() + let pty = job_info(term_getjob(s:ptybuf))['tty_out'] + call s:SendCommand('tty ' . pty) + + " Since GDB runs in a prompt window, the environment has not been set to + " match a terminal window, need to do that now. + call s:SendCommand('set env TERM = xterm-color') + call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) + call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) + call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) + call s:SendCommand('set env COLORS = ' . &t_Co) + call s:SendCommand('set env VIM_TERMINAL = ' . v:version) + else + " TODO: open a new terminal, get the tty name, pass on to gdb + call s:SendCommand('show inferior-tty') + endif + call s:SendCommand('set print pretty on') + call s:SendCommand('set breakpoint pending on') + + " Set arguments to be run + if len(proc_args) + call s:SendCommand('set args ' . join(proc_args)) + endif + + call s:StartDebugCommon(a:dict) + startinsert +endfunc + +func s:StartDebugCommon(dict) + " Sign used to highlight the line where the program has stopped. + " There can be only one. + call sign_define('debugPC', #{linehl: 'debugPC'}) + + " Install debugger commands in the text window. + call win_gotoid(s:sourcewin) + call s:InstallCommands() + call win_gotoid(s:gdbwin) + + " Enable showing a balloon with eval info + if has("balloon_eval") || has("balloon_eval_term") + set balloonexpr=TermDebugBalloonExpr() + if has("balloon_eval") + set ballooneval + endif + if has("balloon_eval_term") + set balloonevalterm + endif + endif + + " Contains breakpoints that have been placed, key is a string with the GDB + " breakpoint number. + " Each entry is a dict, containing the sub-breakpoints. Key is the subid. + " For a breakpoint that is just a number the subid is zero. + " For a breakpoint "123.4" the id is "123" and subid is "4". + " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: + " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} + let s:breakpoints = {} + + " Contains breakpoints by file/lnum. The key is "fname:lnum". + " Each entry is a list of breakpoint IDs at that position. + let s:breakpoint_locations = {} + + augroup TermDebug + au BufRead * call s:BufRead() + au BufUnload * call s:BufUnloaded() + au OptionSet background call s:Highlight(0, v:option_old, v:option_new) + augroup END + + " Run the command if the bang attribute was given and got to the debug + " window. + if get(a:dict, 'bang', 0) + call s:SendResumingCommand('-exec-run') + call win_gotoid(s:ptywin) + endif +endfunc + +" Send a command to gdb. "cmd" is the string without line terminator. +func s:SendCommand(cmd) + call ch_log('sending to gdb: ' . a:cmd) + if s:way == 'prompt' + call ch_sendraw(s:gdb_channel, a:cmd . "\n") + else + call term_sendkeys(s:commbuf, a:cmd . "\r") + endif +endfunc + +" This is global so that a user can create their mappings with this. +func TermDebugSendCommand(cmd) + if s:way == 'prompt' + call ch_sendraw(s:gdb_channel, a:cmd . "\n") + else + let do_continue = 0 + if !s:stopped + let do_continue = 1 + Stop + sleep 10m + endif + " TODO: should we prepend CTRL-U to clear the command? + call term_sendkeys(s:gdbbuf, a:cmd . "\r") + if do_continue + Continue + endif + endif +endfunc + +" Send a command that resumes the program. If the program isn't stopped the +" command is not sent (to avoid a repeated command to cause trouble). +" If the command is sent then reset s:stopped. +func s:SendResumingCommand(cmd) + if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + call ch_log('assume that program is running after this command') + call s:SendCommand(a:cmd) + else + call ch_log('dropping command, program is running: ' . a:cmd) + endif +endfunc + +" Function called when entering a line in the prompt buffer. +func s:PromptCallback(text) + call s:SendCommand(a:text) +endfunc + +" Function called when pressing CTRL-C in the prompt buffer and when placing a +" breakpoint. +func s:PromptInterrupt() + call ch_log('Interrupting gdb') + if has('win32') + " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to + " the debugger program so that gdb responds again. + if s:pid == 0 + call s:Echoerr('Cannot interrupt gdb, did not find a process ID') + else + call debugbreak(s:pid) + endif + else + call job_stop(s:gdbjob, 'int') + endif +endfunc + +" Function called when gdb outputs text. +func s:GdbOutCallback(channel, text) + call ch_log('received from gdb: ' . a:text) + + " Disassembly messages need to be forwarded as-is. + if s:parsing_disasm_msg + call s:CommOutput(a:channel, a:text) + return + end + + " Drop the gdb prompt, we have our own. + " Drop status and echo'd commands. + if a:text == '(gdb) ' || a:text == '^done' || + \ (a:text[0] == '&' && a:text !~ '^&"disassemble') + return + endif + if a:text =~ '^\^error,msg=' + let text = s:DecodeMessage(a:text[11:], v:false) + if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' + " Silently drop evaluation errors. + unlet s:evalexpr + return + endif + elseif a:text[0] == '~' + let text = s:DecodeMessage(a:text[1:], v:false) + else + call s:CommOutput(a:channel, a:text) + return + endif + + let curwinid = win_getid() + call win_gotoid(s:gdbwin) + + " Add the output above the current prompt. + call append(line('$') - 1, text) + set modified + + call win_gotoid(curwinid) +endfunc + +" Decode a message from gdb. "quotedText" starts with a ", return the text up +" to the next unescaped ", unescaping characters: +" - remove line breaks (unless "literal" is v:true) +" - change \" to " +" - change \\t to \t (unless "literal" is v:true) +" - change \0xhh to \xhh (disabled for now) +" - change \ooo to octal +" - change \\ to \ +func s:DecodeMessage(quotedText, literal) + if a:quotedText[0] != '"' + call s:Echoerr('DecodeMessage(): missing quote in ' . a:quotedText) + return + endif + let msg = a:quotedText + \ ->substitute('^"\|[^\\]\zs".*', '', 'g') + \ ->substitute('\\"', '"', 'g') + "\ multi-byte characters arrive in octal form + "\ NULL-values must be kept encoded as those break the string otherwise + \ ->substitute('\\000', s:NullRepl, 'g') + \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') + "\ Note: GDB docs also mention hex encodings - the translations below work + "\ but we keep them out for performance-reasons until we actually see + "\ those in mi-returns + "\ \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + "\ \ ->substitute('\\0x00', s:NullRepl, 'g') + \ ->substitute('\\\\', '\', 'g') + \ ->substitute(s:NullRepl, '\\000', 'g') + if !a:literal + return msg + \ ->substitute('\\t', "\t", 'g') + \ ->substitute('\\n', '', 'g') + else + return msg + endif +endfunc +const s:NullRepl = 'XXXNULLXXX' + +" Extract the "name" value from a gdb message with fullname="name". +func s:GetFullname(msg) + if a:msg !~ 'fullname' + return '' + endif + let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''), v:true) + if has('win32') && name =~ ':\\\\' + " sometimes the name arrives double-escaped + let name = substitute(name, '\\\\', '\\', 'g') + endif + return name +endfunc + +" Extract the "addr" value from a gdb message with addr="0x0001234". +func s:GetAsmAddr(msg) + if a:msg !~ 'addr=' + return '' + endif + let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''), v:false) + return addr +endfunc + +func s:EndTermDebug(job, status) + if exists('#User#TermdebugStopPre') + doauto <nomodeline> User TermdebugStopPre + endif + + exe 'bwipe! ' . s:commbuf + unlet s:gdbwin + call s:EndDebugCommon() +endfunc + +func s:EndDebugCommon() + let curwinid = win_getid() + + if exists('s:ptybuf') && s:ptybuf + exe 'bwipe! ' . s:ptybuf + endif + if s:asmbuf > 0 && bufexists(s:asmbuf) + exe 'bwipe! ' . s:asmbuf + endif + if s:varbuf > 0 && bufexists(s:varbuf) + exe 'bwipe! ' . s:varbuf + endif + let s:running = 0 + + " Restore 'signcolumn' in all buffers for which it was set. + call win_gotoid(s:sourcewin) + let was_buf = bufnr() + for bufnr in s:signcolumn_buflist + if bufexists(bufnr) + exe bufnr .. "buf" + if exists('b:save_signcolumn') + let &signcolumn = b:save_signcolumn + unlet b:save_signcolumn + endif + endif + endfor + if bufexists(was_buf) + exe was_buf .. "buf" + endif + + call s:DeleteCommands() + + call win_gotoid(curwinid) + + if s:save_columns > 0 + let &columns = s:save_columns + endif + + if has("balloon_eval") || has("balloon_eval_term") + set balloonexpr= + if has("balloon_eval") + set noballooneval + endif + if has("balloon_eval_term") + set noballoonevalterm + endif + endif + + if exists('#User#TermdebugStopPost') + doauto <nomodeline> User TermdebugStopPost + endif + + au! TermDebug +endfunc + +func s:EndPromptDebug(job, status) + if exists('#User#TermdebugStopPre') + doauto <nomodeline> User TermdebugStopPre + endif + + if bufexists(s:promptbuf) + exe 'bwipe! ' . s:promptbuf + endif + + call s:EndDebugCommon() + unlet s:gdbwin + call ch_log("Returning from EndPromptDebug()") +endfunc + +" Disassembly window - added by Michael Sartain +" +" - CommOutput: &"disassemble $pc\n" +" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" +" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" +" ... +" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" +" - CommOutput: ~" 0x0000555556467cd1:\tret \n" +" - CommOutput: ~"End of assembler dump.\n" +" - CommOutput: ^done + +" - CommOutput: &"disassemble $pc\n" +" - CommOutput: &"No function contains specified address.\n" +" - CommOutput: ^error,msg="No function contains specified address." +func s:HandleDisasmMsg(msg) + if a:msg =~ '^\^done' + let curwinid = win_getid() + if win_gotoid(s:asmwin) + silent! %delete _ + call setline(1, s:asm_lines) + set nomodified + set filetype=asm + + let lnum = search('^' . s:asm_addr) + if lnum != 0 + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + + call win_gotoid(curwinid) + endif + + let s:parsing_disasm_msg = 0 + let s:asm_lines = [] + elseif a:msg =~ '^\^error,msg=' + if s:parsing_disasm_msg == 1 + " Disassemble call ran into an error. This can happen when gdb can't + " find the function frame address, so let's try to disassemble starting + " at current PC + call s:SendCommand('disassemble $pc,+100') + endif + let s:parsing_disasm_msg = 0 + elseif a:msg =~ '^&"disassemble \$pc' + if a:msg =~ '+100' + " This is our second disasm attempt + let s:parsing_disasm_msg = 2 + endif + elseif a:msg !~ '^&"disassemble' + let value = substitute(a:msg, '^\~\"[ ]*', '', '') + let value = substitute(value, '^=>[ ]*', '', '') + let value = substitute(value, '\\n\"\r$', '', '') + let value = substitute(value, '\\n\"$', '', '') + let value = substitute(value, '\r', '', '') + let value = substitute(value, '\\t', ' ', 'g') + + if value != '' || !empty(s:asm_lines) + call add(s:asm_lines, value) + endif + endif +endfunc + +func s:ParseVarinfo(varinfo) + let dict = {} + let nameIdx = matchstrpos(a:varinfo, '{name="\([^"]*\)"') + let dict['name'] = a:varinfo[nameIdx[1] + 7 : nameIdx[2] - 2] + let typeIdx = matchstrpos(a:varinfo, ',type="\([^"]*\)"') + " 'type' maybe is a url-like string, + " try to shorten it and show only the /tail + let dict['type'] = (a:varinfo[typeIdx[1] + 7 : typeIdx[2] - 2])->fnamemodify(':t') + let valueIdx = matchstrpos(a:varinfo, ',value="\(.*\)"}') + if valueIdx[1] == -1 + let dict['value'] = 'Complex value' + else + let dict['value'] = a:varinfo[valueIdx[1] + 8 : valueIdx[2] - 3] + endif + return dict +endfunc + +func s:HandleVariablesMsg(msg) + let curwinid = win_getid() + if win_gotoid(s:varwin) + + silent! %delete _ + let spaceBuffer = 20 + call setline(1, 'Type' . + \ repeat(' ', 16) . + \ 'Name' . + \ repeat(' ', 16) . + \ 'Value') + let cnt = 1 + let capture = '{name=".\{-}",\%(arg=".\{-}",\)\{0,1\}type=".\{-}"\%(,value=".\{-}"\)\{0,1\}}' + let varinfo = matchstr(a:msg, capture, 0, cnt) + while varinfo != '' + let vardict = s:ParseVarinfo(varinfo) + call setline(cnt + 1, vardict['type'] . + \ repeat(' ', max([20 - len(vardict['type']), 1])) . + \ vardict['name'] . + \ repeat(' ', max([20 - len(vardict['name']), 1])) . + \ vardict['value']) + let cnt += 1 + let varinfo = matchstr(a:msg, capture, 0, cnt) + endwhile + endif + call win_gotoid(curwinid) +endfunc + +" Handle a message received from gdb on the GDB/MI interface. +func s:CommOutput(chan, msg) + let msgs = split(a:msg, "\r") + + for msg in msgs + " remove prefixed NL + if msg[0] == "\n" + let msg = msg[1:] + endif + + if s:parsing_disasm_msg + call s:HandleDisasmMsg(msg) + elseif msg != '' + if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' + call s:HandleCursor(msg) + elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' + call s:HandleNewBreakpoint(msg, 0) + elseif msg =~ '^=breakpoint-modified,' + call s:HandleNewBreakpoint(msg, 1) + elseif msg =~ '^=breakpoint-deleted,' + call s:HandleBreakpointDelete(msg) + elseif msg =~ '^=thread-group-started' + call s:HandleProgramRun(msg) + elseif msg =~ '^\^done,value=' + call s:HandleEvaluate(msg) + elseif msg =~ '^\^error,msg=' + call s:HandleError(msg) + elseif msg =~ '^&"disassemble' + let s:parsing_disasm_msg = 1 + let s:asm_lines = [] + call s:HandleDisasmMsg(msg) + elseif msg =~ '^\^done,variables=' + call s:HandleVariablesMsg(msg) + endif + endif + endfor +endfunc + +func s:GotoProgram() + if has('win32') + if executable('powershell') + call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) + endif + else + call win_gotoid(s:ptywin) + endif +endfunc + +" Install commands in the current window to control the debugger. +func s:InstallCommands() + let save_cpo = &cpo + set cpo&vim + + command -nargs=? Break call s:SetBreakpoint(<q-args>) + command -nargs=? Tbreak call s:SetBreakpoint(<q-args>, v:true) + command Clear call s:ClearBreakpoint() + command Step call s:SendResumingCommand('-exec-step') + command Over call s:SendResumingCommand('-exec-next') + command -nargs=? Until call s:Until(<q-args>) + command Finish call s:SendResumingCommand('-exec-finish') + command -nargs=* Run call s:Run(<q-args>) + command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . <q-args>) + + if s:way == 'prompt' + command Stop call s:PromptInterrupt() + command Continue call s:SendCommand('continue') + else + command Stop call s:SendCommand('-exec-interrupt') + " using -exec-continue results in CTRL-C in the gdb window not working, + " communicating via commbuf (= use of SendCommand) has the same result + "command Continue call s:SendCommand('-exec-continue') + command Continue call term_sendkeys(s:gdbbuf, "continue\r") + endif + + command -nargs=* Frame call s:Frame(<q-args>) + command -count=1 Up call s:Up(<count>) + command -count=1 Down call s:Down(<count>) + + command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) + command Gdb call win_gotoid(s:gdbwin) + command Program call s:GotoProgram() + command Source call s:GotoSourcewinOrCreateIt() + command Asm call s:GotoAsmwinOrCreateIt() + command Var call s:GotoVariableswinOrCreateIt() + command Winbar call s:InstallWinbar(1) + + let map = 1 + if exists('g:termdebug_config') + let map = get(g:termdebug_config, 'map_K', 1) + elseif exists('g:termdebug_map_K') + let map = g:termdebug_map_K + endif + if map + let s:k_map_saved = maparg('K', 'n', 0, 1) + if !empty(s:k_map_saved) && !s:k_map_saved.buffer || empty(s:k_map_saved) + nnoremap K :Evaluate<CR> + endif + endif + + let map = 1 + if exists('g:termdebug_config') + let map = get(g:termdebug_config, 'map_plus', 1) + endif + if map + let s:plus_map_saved = maparg('+', 'n', 0, 1) + if !empty(s:plus_map_saved) && !s:plus_map_saved.buffer || empty(s:plus_map_saved) + nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>' + endif + endif + + let map = 1 + if exists('g:termdebug_config') + let map = get(g:termdebug_config, 'map_minus', 1) + endif + if map + let s:minus_map_saved = maparg('-', 'n', 0, 1) + if !empty(s:minus_map_saved) && !s:minus_map_saved.buffer || empty(s:minus_map_saved) + nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>' + endif + endif + + + if has('menu') && &mouse != '' + call s:InstallWinbar(0) + + let popup = 1 + if exists('g:termdebug_config') + let popup = get(g:termdebug_config, 'popup', 1) + elseif exists('g:termdebug_popup') + let popup = g:termdebug_popup + endif + if popup + let s:saved_mousemodel = &mousemodel + let &mousemodel = 'popup_setpos' + an 1.200 PopUp.-SEP3- <Nop> + an 1.210 PopUp.Set\ breakpoint :Break<CR> + an 1.220 PopUp.Clear\ breakpoint :Clear<CR> + an 1.230 PopUp.Run\ until :Until<CR> + an 1.240 PopUp.Evaluate :Evaluate<CR> + endif + endif + + let &cpo = save_cpo +endfunc + +let s:winbar_winids = [] + +" Install the window toolbar in the current window. +func s:InstallWinbar(force) + " install the window toolbar by default, can be disabled in the config + let winbar = 1 + if exists('g:termdebug_config') + let winbar = get(g:termdebug_config, 'winbar', 1) + endif + + if has('menu') && &mouse != '' && (winbar || a:force) + nnoremenu WinBar.Step :Step<CR> + nnoremenu WinBar.Next :Over<CR> + nnoremenu WinBar.Finish :Finish<CR> + nnoremenu WinBar.Cont :Continue<CR> + nnoremenu WinBar.Stop :Stop<CR> + nnoremenu WinBar.Eval :Evaluate<CR> + call add(s:winbar_winids, win_getid()) + endif +endfunc + +" Delete installed debugger commands in the current window. +func s:DeleteCommands() + delcommand Break + delcommand Tbreak + delcommand Clear + delcommand Step + delcommand Over + delcommand Until + delcommand Finish + delcommand Run + delcommand Arguments + delcommand Stop + delcommand Continue + delcommand Frame + delcommand Up + delcommand Down + delcommand Evaluate + delcommand Gdb + delcommand Program + delcommand Source + delcommand Asm + delcommand Var + delcommand Winbar + + if exists('s:k_map_saved') + if !empty(s:k_map_saved) && !s:k_map_saved.buffer + nunmap K + call mapset(s:k_map_saved) + elseif empty(s:k_map_saved) + nunmap K + endif + unlet s:k_map_saved + endif + if exists('s:plus_map_saved') + if !empty(s:plus_map_saved) && !s:plus_map_saved.buffer + nunmap + + call mapset(s:plus_map_saved) + elseif empty(s:plus_map_saved) + nunmap + + endif + unlet s:plus_map_saved + endif + if exists('s:minus_map_saved') + if !empty(s:minus_map_saved) && !s:minus_map_saved.buffer + nunmap - + call mapset(s:minus_map_saved) + elseif empty(s:minus_map_saved) + nunmap - + endif + unlet s:minus_map_saved + endif + + if has('menu') + " Remove the WinBar entries from all windows where it was added. + let curwinid = win_getid() + for winid in s:winbar_winids + if win_gotoid(winid) + aunmenu WinBar.Step + aunmenu WinBar.Next + aunmenu WinBar.Finish + aunmenu WinBar.Cont + aunmenu WinBar.Stop + aunmenu WinBar.Eval + endif + endfor + call win_gotoid(curwinid) + let s:winbar_winids = [] + + if exists('s:saved_mousemodel') + let &mousemodel = s:saved_mousemodel + unlet s:saved_mousemodel + aunmenu PopUp.-SEP3- + aunmenu PopUp.Set\ breakpoint + aunmenu PopUp.Clear\ breakpoint + aunmenu PopUp.Run\ until + aunmenu PopUp.Evaluate + endif + endif + + call sign_unplace('TermDebug') + unlet s:breakpoints + unlet s:breakpoint_locations + + call sign_undefine('debugPC') + call sign_undefine(s:BreakpointSigns->map("'debugBreakpoint' .. v:val")) + let s:BreakpointSigns = [] +endfunc + +" :Until - Execute until past a specified position or current line +func s:Until(at) + if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + call ch_log('assume that program is running after this command') + " Use the fname:lnum format + let at = empty(a:at) ? + \ fnameescape(expand('%:p')) . ':' . line('.') : a:at + call s:SendCommand('-exec-until ' . at) + else + call ch_log('dropping command, program is running: exec-until') + endif +endfunc + +" :Break - Set a breakpoint at the cursor position. +func s:SetBreakpoint(at, tbreak=v:false) + " Setting a breakpoint may not work while the program is running. + " Interrupt to make it work. + let do_continue = 0 + if !s:stopped + let do_continue = 1 + Stop + sleep 10m + endif + + " Use the fname:lnum format, older gdb can't handle --source. + let at = empty(a:at) ? + \ fnameescape(expand('%:p')) . ':' . line('.') : a:at + if a:tbreak + let cmd = '-break-insert -t ' . at + else + let cmd = '-break-insert ' . at + endif + call s:SendCommand(cmd) + if do_continue + Continue + endif +endfunc + +" :Clear - Delete a breakpoint at the cursor position. +func s:ClearBreakpoint() + let fname = fnameescape(expand('%:p')) + let lnum = line('.') + let bploc = printf('%s:%d', fname, lnum) + if has_key(s:breakpoint_locations, bploc) + let idx = 0 + let nr = 0 + for id in s:breakpoint_locations[bploc] + if has_key(s:breakpoints, id) + " Assume this always works, the reply is simply "^done". + call s:SendCommand('-break-delete ' . id) + for subid in keys(s:breakpoints[id]) + call sign_unplace('TermDebug', + \ #{id: s:Breakpoint2SignNumber(id, subid)}) + endfor + unlet s:breakpoints[id] + unlet s:breakpoint_locations[bploc][idx] + let nr = id + break + else + let idx += 1 + endif + endfor + if nr != 0 + if empty(s:breakpoint_locations[bploc]) + unlet s:breakpoint_locations[bploc] + endif + echomsg 'Breakpoint ' . id . ' cleared from line ' . lnum . '.' + else + call s:Echoerr('Internal error trying to remove breakpoint at line ' . lnum . '!') + endif + else + echomsg 'No breakpoint to remove at line ' . lnum . '.' + endif +endfunc + +func s:Run(args) + if a:args != '' + call s:SendResumingCommand('-exec-arguments ' . a:args) + endif + call s:SendResumingCommand('-exec-run') +endfunc + +" :Frame - go to a specific frame in the stack +func s:Frame(arg) + " Note: we explicit do not use mi's command + " call s:SendCommand('-stack-select-frame "' . a:arg .'"') + " as we only get a "done" mi response and would have to open the file + " 'manually' - using cli command "frame" provides us with the mi response + " already parsed and allows for more formats + if a:arg =~ '^\d\+$' || a:arg == '' + " specify frame by number + call s:SendCommand('-interpreter-exec mi "frame ' . a:arg .'"') + elseif a:arg =~ '^0x[0-9a-fA-F]\+$' + " specify frame by stack address + call s:SendCommand('-interpreter-exec mi "frame address ' . a:arg .'"') + else + " specify frame by function name + call s:SendCommand('-interpreter-exec mi "frame function ' . a:arg .'"') + endif +endfunc + +" :Up - go a:count frames in the stack "higher" +func s:Up(count) + " the 'correct' one would be -stack-select-frame N, but we don't know N + call s:SendCommand($'-interpreter-exec console "up {a:count}"') +endfunc + +" :Down - go a:count frames in the stack "below" +func s:Down(count) + " the 'correct' one would be -stack-select-frame N, but we don't know N + call s:SendCommand($'-interpreter-exec console "down {a:count}"') +endfunc + +func s:SendEval(expr) + " check for "likely" boolean expressions, in which case we take it as lhs + if a:expr =~ "[=!<>]=" + let exprLHS = a:expr + else + " remove text that is likely an assignment + let exprLHS = substitute(a:expr, ' *=.*', '', '') + endif + + " encoding expression to prevent bad errors + let expr = a:expr + let expr = substitute(expr, '\\', '\\\\', 'g') + let expr = substitute(expr, '"', '\\"', 'g') + call s:SendCommand('-data-evaluate-expression "' . expr . '"') + let s:evalexpr = exprLHS +endfunc + +" :Evaluate - evaluate what is specified / under the cursor +func s:Evaluate(range, arg) + let expr = s:GetEvaluationExpression(a:range, a:arg) + let s:ignoreEvalError = 0 + call s:SendEval(expr) +endfunc + +" get what is specified / under the cursor +func s:GetEvaluationExpression(range, arg) + if a:arg != '' + " user supplied evaluation + let expr = s:CleanupExpr(a:arg) + " DSW: replace "likely copy + paste" assignment + let expr = substitute(expr, '"\([^"]*\)": *', '\1=', 'g') + elseif a:range == 2 + " no evaluation but provided but range set + let pos = getcurpos() + let reg = getreg('v', 1, 1) + let regt = getregtype('v') + normal! gv"vy + let expr = s:CleanupExpr(@v) + call setpos('.', pos) + call setreg('v', reg, regt) + else + " no evaluation provided: get from C-expression under cursor + " TODO: allow filetype specific lookup #9057 + let expr = expand('<cexpr>') + endif + return expr +endfunc + +" clean up expression that may get in because of range +" (newlines and surrounding whitespace) +" As it can also be specified via ex-command for assignments this function +" may not change the "content" parts (like replacing contained spaces) +func s:CleanupExpr(expr) + " replace all embedded newlines/tabs/... + let expr = substitute(a:expr, '\_s', ' ', 'g') + + if &filetype ==# 'cobol' + " extra cleanup for COBOL: + " - a semicolon nmay be used instead of a space + " - a trailing comma or period is ignored as it commonly separates/ends + " multiple expr + let expr = substitute(expr, ';', ' ', 'g') + let expr = substitute(expr, '[,.]\+ *$', '', '') + endif + + " get rid of leading and trailing spaces + let expr = substitute(expr, '^ *', '', '') + let expr = substitute(expr, ' *$', '', '') + return expr +endfunc + +let s:ignoreEvalError = 0 +let s:evalFromBalloonExpr = 0 + +" Handle the result of data-evaluate-expression +func s:HandleEvaluate(msg) + let value = a:msg + \ ->substitute('.*value="\(.*\)"', '\1', '') + \ ->substitute('\\"', '"', 'g') + \ ->substitute('\\\\', '\\', 'g') + "\ multi-byte characters arrive in octal form, replace everything but NULL values + \ ->substitute('\\000', s:NullRepl, 'g') + \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') + "\ Note: GDB docs also mention hex encodings - the translations below work + "\ but we keep them out for performance-reasons until we actually see + "\ those in mi-returns + "\ ->substitute('\\0x00', s:NullRep, 'g') + "\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + \ ->substitute(s:NullRepl, '\\000', 'g') + if s:evalFromBalloonExpr + if s:evalFromBalloonExprResult == '' + let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value + else + let s:evalFromBalloonExprResult .= ' = ' . value + endif + call balloon_show(s:evalFromBalloonExprResult) + else + echomsg '"' . s:evalexpr . '": ' . value + endif + + if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' + " Looks like a pointer, also display what it points to. + let s:ignoreEvalError = 1 + call s:SendEval('*' . s:evalexpr) + else + let s:evalFromBalloonExpr = 0 + endif +endfunc + +" Show a balloon with information of the variable under the mouse pointer, +" if there is any. +func TermDebugBalloonExpr() + if v:beval_winid != s:sourcewin + return '' + endif + if !s:stopped + " Only evaluate when stopped, otherwise setting a breakpoint using the + " mouse triggers a balloon. + return '' + endif + let s:evalFromBalloonExpr = 1 + let s:evalFromBalloonExprResult = '' + let s:ignoreEvalError = 1 + let expr = s:CleanupExpr(v:beval_text) + call s:SendEval(expr) + return '' +endfunc + +" Handle an error. +func s:HandleError(msg) + if s:ignoreEvalError + " Result of s:SendEval() failed, ignore. + let s:ignoreEvalError = 0 + let s:evalFromBalloonExpr = 0 + return + endif + let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '') + call s:Echoerr(substitute(msgVal, '\\"', '"', 'g')) +endfunc + +func s:GotoSourcewinOrCreateIt() + if !win_gotoid(s:sourcewin) + new + let s:sourcewin = win_getid() + call s:InstallWinbar(0) + endif +endfunc + +func s:GetDisasmWindow() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'disasm_window', 0) + endif + if exists('g:termdebug_disasm_window') + return g:termdebug_disasm_window + endif + return 0 +endfunc + +func s:GetDisasmWindowHeight() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'disasm_window_height', 0) + endif + if exists('g:termdebug_disasm_window') && g:termdebug_disasm_window > 1 + return g:termdebug_disasm_window + endif + return 0 +endfunc + +func s:GotoAsmwinOrCreateIt() + if !win_gotoid(s:asmwin) + let mdf = '' + if win_gotoid(s:sourcewin) + " 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + let mdf = 'vert' + exe mdf .. ' ' .. 60 .. 'new' + else + exe 'rightbelow new' + endif + else + exe 'new' + endif + + let s:asmwin = win_getid() + + setlocal nowrap + setlocal number + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal signcolumn=no + setlocal modifiable + + if s:asmbuf > 0 && bufexists(s:asmbuf) + exe 'buffer' . s:asmbuf + else + silent file Termdebug-asm-listing + let s:asmbuf = bufnr('Termdebug-asm-listing') + endif + + if mdf != 'vert' && s:GetDisasmWindowHeight() > 0 + exe 'resize ' .. s:GetDisasmWindowHeight() + endif + endif + + if s:asm_addr != '' + let lnum = search('^' . s:asm_addr) + if lnum == 0 + if s:stopped + call s:SendCommand('disassemble $pc') + endif + else + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + endif +endfunc + +func s:GetVariablesWindow() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'variables_window', 0) + endif + if exists('g:termdebug_disasm_window') + return g:termdebug_variables_window + endif + return 0 +endfunc + +func s:GetVariablesWindowHeight() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'variables_window_height', 0) + endif + if exists('g:termdebug_variables_window') && g:termdebug_variables_window > 1 + return g:termdebug_variables_window + endif + return 0 +endfunc + +func s:GotoVariableswinOrCreateIt() + if !win_gotoid(s:varwin) + let mdf = '' + if win_gotoid(s:sourcewin) + " 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + let mdf = 'vert' + exe mdf .. ' ' .. 60 .. 'new' + else + exe 'rightbelow new' + endif + else + exe 'new' + endif + + let s:varwin = win_getid() + + setlocal nowrap + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal signcolumn=no + setlocal modifiable + + if s:varbuf > 0 && bufexists(s:varbuf) + exe 'buffer' . s:varbuf + else + silent file Termdebug-variables-listing + let s:varbuf = bufnr('Termdebug-variables-listing') + endif + + if mdf != 'vert' && s:GetVariablesWindowHeight() > 0 + exe 'resize ' .. s:GetVariablesWindowHeight() + endif + endif + + if s:running + call s:SendCommand('-stack-list-variables 2') + endif +endfunc + +" Handle stopping and running message from gdb. +" Will update the sign that shows the current position. +func s:HandleCursor(msg) + let wid = win_getid() + + if a:msg =~ '^\*stopped' + call ch_log('program stopped') + let s:stopped = 1 + if a:msg =~ '^\*stopped,reason="exited-normally"' + let s:running = 0 + endif + elseif a:msg =~ '^\*running' + call ch_log('program running') + let s:stopped = 0 + let s:running = 1 + endif + + if a:msg =~ 'fullname=' + let fname = s:GetFullname(a:msg) + else + let fname = '' + endif + + if a:msg =~ 'addr=' + let asm_addr = s:GetAsmAddr(a:msg) + if asm_addr != '' + let s:asm_addr = asm_addr + + let curwinid = win_getid() + if win_gotoid(s:asmwin) + let lnum = search('^' . s:asm_addr) + if lnum == 0 + call s:SendCommand('disassemble $pc') + else + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + + call win_gotoid(curwinid) + endif + endif + endif + + if s:running && s:stopped && bufwinnr('Termdebug-variables-listing') != -1 + call s:SendCommand('-stack-list-variables 2') + endif + + if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) + let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') + if lnum =~ '^[0-9]*$' + call s:GotoSourcewinOrCreateIt() + if expand('%:p') != fnamemodify(fname, ':p') + echomsg 'different fname: "' .. expand('%:p') .. '" vs "' .. fnamemodify(fname, ':p') .. '"' + augroup Termdebug + " Always open a file read-only instead of showing the ATTENTION + " prompt, since it is unlikely we want to edit the file. + " The file may be changed but not saved, warn for that. + au SwapExists * echohl WarningMsg + \ | echo 'Warning: file is being edited elsewhere' + \ | echohl None + \ | let v:swapchoice = 'o' + augroup END + if &modified + " TODO: find existing window + exe 'split ' . fnameescape(fname) + let s:sourcewin = win_getid() + call s:InstallWinbar(0) + else + exe 'edit ' . fnameescape(fname) + endif + augroup Termdebug + au! SwapExists + augroup END + endif + exe lnum + normal! zv + call sign_unplace('TermDebug', #{id: s:pc_id}) + call sign_place(s:pc_id, 'TermDebug', 'debugPC', fname, + \ #{lnum: lnum, priority: 110}) + if !exists('b:save_signcolumn') + let b:save_signcolumn = &signcolumn + call add(s:signcolumn_buflist, bufnr()) + endif + setlocal signcolumn=yes + endif + elseif !s:stopped || fname != '' + call sign_unplace('TermDebug', #{id: s:pc_id}) + endif + + call win_gotoid(wid) +endfunc + +let s:BreakpointSigns = [] + +func s:CreateBreakpoint(id, subid, enabled) + let nr = printf('%d.%d', a:id, a:subid) + if index(s:BreakpointSigns, nr) == -1 + call add(s:BreakpointSigns, nr) + if a:enabled == "n" + let hiName = "debugBreakpointDisabled" + else + let hiName = "debugBreakpoint" + endif + let label = '' + if exists('g:termdebug_config') + let label = get(g:termdebug_config, 'sign', '') + endif + if label == '' + let label = printf('%02X', a:id) + if a:id > 255 + let label = 'F+' + endif + endif + call sign_define('debugBreakpoint' .. nr, + \ #{text: strpart(label, 0, 2), + \ texthl: hiName}) + endif +endfunc + +func! s:SplitMsg(s) + return split(a:s, '{.\{-}}\zs') +endfunction + +" Handle setting a breakpoint +" Will update the sign that shows the breakpoint +func s:HandleNewBreakpoint(msg, modifiedFlag) + if a:msg !~ 'fullname=' + " a watch or a pending breakpoint does not have a file name + if a:msg =~ 'pending=' + let nr = substitute(a:msg, '.*number=\"\([0-9.]*\)\".*', '\1', '') + let target = substitute(a:msg, '.*pending=\"\([^"]*\)\".*', '\1', '') + echomsg 'Breakpoint ' . nr . ' (' . target . ') pending.' + endif + return + endif + for msg in s:SplitMsg(a:msg) + let fname = s:GetFullname(msg) + if empty(fname) + continue + endif + let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') + if empty(nr) + return + endif + + " If "nr" is 123 it becomes "123.0" and subid is "0". + " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. + let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') + let enabled = substitute(msg, '.*enabled="\([yn]\)".*', '\1', '') + call s:CreateBreakpoint(id, subid, enabled) + + if has_key(s:breakpoints, id) + let entries = s:breakpoints[id] + else + let entries = {} + let s:breakpoints[id] = entries + endif + if has_key(entries, subid) + let entry = entries[subid] + else + let entry = {} + let entries[subid] = entry + endif + + let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') + let entry['fname'] = fname + let entry['lnum'] = lnum + + let bploc = printf('%s:%d', fname, lnum) + if !has_key(s:breakpoint_locations, bploc) + let s:breakpoint_locations[bploc] = [] + endif + let s:breakpoint_locations[bploc] += [id] + + if bufloaded(fname) + call s:PlaceSign(id, subid, entry) + let posMsg = ' at line ' . lnum . '.' + else + let posMsg = ' in ' . fname . ' at line ' . lnum . '.' + endif + if !a:modifiedFlag + let actionTaken = 'created' + elseif enabled == 'n' + let actionTaken = 'disabled' + else + let actionTaken = 'enabled' + endif + echomsg 'Breakpoint ' . nr . ' ' . actionTaken . posMsg + endfor +endfunc + +func s:PlaceSign(id, subid, entry) + let nr = printf('%d.%d', a:id, a:subid) + call sign_place(s:Breakpoint2SignNumber(a:id, a:subid), 'TermDebug', + \ 'debugBreakpoint' .. nr, a:entry['fname'], + \ #{lnum: a:entry['lnum'], priority: 110}) + let a:entry['placed'] = 1 +endfunc + +" Handle deleting a breakpoint +" Will remove the sign that shows the breakpoint +func s:HandleBreakpointDelete(msg) + let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 + if empty(id) + return + endif + if has_key(s:breakpoints, id) + for [subid, entry] in items(s:breakpoints[id]) + if has_key(entry, 'placed') + call sign_unplace('TermDebug', + \ #{id: s:Breakpoint2SignNumber(id, subid)}) + unlet entry['placed'] + endif + endfor + unlet s:breakpoints[id] + echomsg 'Breakpoint ' . id . ' cleared.' + endif +endfunc + +" Handle the debugged program starting to run. +" Will store the process ID in s:pid +func s:HandleProgramRun(msg) + let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 + if nr == 0 + return + endif + let s:pid = nr + call ch_log('Detected process ID: ' . s:pid) +endfunc + +" Handle a BufRead autocommand event: place any signs. +func s:BufRead() + let fname = expand('<afile>:p') + for [id, entries] in items(s:breakpoints) + for [subid, entry] in items(entries) + if entry['fname'] == fname + call s:PlaceSign(id, subid, entry) + endif + endfor + endfor +endfunc + +" Handle a BufUnloaded autocommand event: unplace any signs. +func s:BufUnloaded() + let fname = expand('<afile>:p') + for [id, entries] in items(s:breakpoints) + for [subid, entry] in items(entries) + if entry['fname'] == fname + let entry['placed'] = 0 + endif + endfor + endfor +endfunc + +call s:InitHighlight() +call s:InitAutocmd() + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim: sw=2 sts=2 et |