diff options
121 files changed, 4118 insertions, 3449 deletions
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 25f8414008..d281bb1404 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -12,7 +12,7 @@ jobs: name: Backport Pull Request if: > github.repository_owner == 'neovim' && ( - github.event_name == 'pull_request' && + github.event_name == 'pull_request_target' && github.event.pull_request.merged ) || ( github.event_name == 'issue_comment' && diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 559d8eae83..6c74ec99d3 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -9,7 +9,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v2.3.1 + - uses: actions/checkout@v2 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} diff --git a/.gitignore b/.gitignore index e07ce4906e..0988a51cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Tools /venv/ compile_commands.json +/.luarc.json # IDEs /.vs/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f44937b5ae..4b62ca8556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,12 +136,12 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # If not in a git repo (e.g., a tarball) these tokens define the complete # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) -set(NVIM_VERSION_MINOR 6) +set(NVIM_VERSION_MINOR 7) set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 8) # Bump this after any API change. +set(NVIM_API_LEVEL 9) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. set(NVIM_API_PRERELEASE true) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9c1173007..faf9181a2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -244,6 +244,10 @@ You can lint a single file (but this will _not_ exclude legacy errors): ("Exuberant ctags", the typical `ctags` binary provided by your distro, is unmaintained and won't recognize many function signatures in Neovim source.) - Explore the source code [on the web](https://sourcegraph.com/github.com/neovim/neovim). +- If using [lua-language-server][], symlink `contrib/luarc.json` into the + project root: + + $ ln -s contrib/luarc.json .luarc.json Reviewing @@ -252,10 +256,10 @@ Reviewing To help review pull requests, start with [this checklist][review-checklist]. Reviewing can be done on GitHub, but you may find it easier to do locally. -Using [`hub`][hub], you can create a new branch with the contents of a pull +Using [GitHub CLI][gh], you can create a new branch with the contents of a pull request, e.g. [#1820][1820]: - hub checkout https://github.com/neovim/neovim/pull/1820 + gh pr checkout https://github.com/neovim/neovim/pull/1820 Use [`git log -p master..FETCH_HEAD`][git-history-filtering] to list all commits in the feature branch which aren't in the `master` branch; `-p` @@ -270,7 +274,7 @@ as context, use the `-W` argument as well. [git-rebasing]: http://git-scm.com/book/en/v2/Git-Branching-Rebasing [github-issues]: https://github.com/neovim/neovim/issues [1820]: https://github.com/neovim/neovim/pull/1820 -[hub]: https://hub.github.com/ +[gh]: https://cli.github.com/ [conventional_commits]: https://www.conventionalcommits.org [style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style [ASan]: http://clang.llvm.org/docs/AddressSanitizer.html @@ -287,4 +291,5 @@ as context, use the `-W` argument as well. [wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp [pr-draft]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request [pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request -[uncrustify]: https://formulae.brew.sh/formula/uncrustify +[uncrustify]: http://uncrustify.sourceforge.net/ +[lua-language-server]: https://github.com/sumneko/lua-language-server/ diff --git a/ci/snap/deploy.sh b/ci/snap/deploy.sh index 579c48e933..8429059e22 100755 --- a/ci/snap/deploy.sh +++ b/ci/snap/deploy.sh @@ -24,7 +24,7 @@ snap_realease_needed() { trigger_snapcraft_webhook() { [[ -n "${PAYLOAD_SIG}" ]] || exit - echo "Triggering new snap relase via webhook..." + echo "Triggering new snap release via webhook..." curl -X POST \ -H "Content-Type: application/json" \ -H "X-Hub-Signature: sha1=${PAYLOAD_SIG}" \ diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 581f25857b..613475b00d 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -92,7 +92,7 @@ if (NOT "${HAVE_BE64TOH}") # any case and ORDER_BIG_ENDIAN will not be examined. # - CMAKE_CROSSCOMPILING *and* HAVE_BE64TOH are both false. In this case # be64toh function which uses cycle and arithmetic operations is used which - # will work regardless of endianess. Function is sub-optimal though. + # will work regardless of endianness. Function is sub-optimal though. check_c_source_runs(" ${SI} ${MS} diff --git a/contrib/local.mk.example b/contrib/local.mk.example index 778e848d60..20396e86ae 100644 --- a/contrib/local.mk.example +++ b/contrib/local.mk.example @@ -27,7 +27,7 @@ # With non-Debug builds interprocedural optimization (IPO) (which includes # link-time optimization (LTO)) is enabled by default, which causes the link -# step to take a significant amout of time, which is relevant when building +# step to take a significant amount of time, which is relevant when building # often. You can disable it explicitly: # CMAKE_EXTRA_FLAGS += -DENABLE_LTO=OFF diff --git a/contrib/luarc.json b/contrib/luarc.json new file mode 100644 index 0000000000..770b023ac6 --- /dev/null +++ b/contrib/luarc.json @@ -0,0 +1,23 @@ +{ + "runtime.version": "LuaJIT", + "diagnostics": { + "enable": true, + "globals": [ + "vim", + "describe", + "it", + "before_each", + "after_each", + "setup", + "teardown" + ] + }, + "workspace": { + "library": { + "runtime/lua": true + }, + "maxPreload": 2000, + "preloadFileSize": 1000 + }, + "telemetry.enable": false +} diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 344889249b..a3db4ae87a 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -1,7 +1,7 @@ " Vim functions for file type detection " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2020 Aug 17 +" Last Change: 2021 Nov 27 " These functions are moved here from runtime/filetype.vim to make startup " faster. @@ -219,6 +219,23 @@ func dist#ft#FTe() endif endfunc +" Distinguish between Forth and F#. +" Provided by Doug Kearns. +func dist#ft#FTfs() + if exists("g:filetype_fs") + exe "setf " . g:filetype_fs + else + let line = getline(nextnonblank(1)) + " comments and colon definitions + if line =~ '^\s*\.\=( ' || line =~ '^\s*\\G\= ' || line =~ '^\\$' + \ || line =~ '^\s*: \S' + setf forth + else + setf fsharp + endif + endif +endfunc + " Distinguish between HTML, XHTML and Django func dist#ft#FThtml() let n = 1 diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 166fef028d..482d8c198d 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -1851,7 +1851,7 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* switched If a window inside the current tabpage (including a float) already shows the buffer One of these windows will be set as current window temporarily. Otherwise a temporary - scratch window (calleed the "autocmd window" for historical + scratch window (called the "autocmd window" for historical reasons) will be used. This is useful e.g. to call vimL functions that only work with @@ -2175,7 +2175,7 @@ nvim_buf_set_text({buffer}, {start_row}, {start_col}, {end_row}, {end_col}, Parameters: ~ {buffer} Buffer handle, or 0 for current buffer {start_row} First line index - {start_column} Last column + {start_column} First column {end_row} Last line index {end_column} Last column {replacement} Array of lines to use as replacement @@ -2500,7 +2500,7 @@ nvim_set_decoration_provider({ns_id}, {opts}) specific window. ["win", winid, bufnr, topline, botline_guess] • on_line: called for each buffer line being - redrawn. (The interation with fold lines is + redrawn. (The interaction with fold lines is subject to change) ["win", winid, bufnr, row] • on_end: called at the end of a redraw cycle ["end", tick] diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 7ca6b3cd8e..879a34bcaa 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -718,7 +718,27 @@ MenuPopup Just before showing the popup menu (under the o Operator-pending i Insert c Command line - *OptionSet* + *ModeChanged* +ModeChanged After changing the mode. The pattern is + matched against `'old_mode:new_mode'`, for + example match against `*:c` to simulate + |CmdlineEnter|. + The following values of |v:event| are set: + old_mode The mode before it changed. + new_mode The new mode as also returned + by |mode()| called with a + non-zero argument. + When ModeChanged is triggered, old_mode will + have the value of new_mode when the event was + last triggered. + This will be triggered on every minor mode + change. + Usage example to use relative line numbers + when entering visual mode: > + :au ModeChanged [vV\x16]*:* let &l:rnu = mode() =~# '^[vV\x16]' + :au ModeChanged *:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]' + :au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]' +< *OptionSet* OptionSet After setting an option (except during |startup|). The |autocmd-pattern| is matched against the long option name. |<amatch>| @@ -793,7 +813,7 @@ QuitPre When using `:quit`, `:wq` or `:qall`, before before QuitPre is triggered. Can be used to close any non-essential window if the current window is the last ordinary window. - See also |ExitPre|, ||WinClosed|. + See also |ExitPre|, |WinClosed|. *RemoteReply* RemoteReply When a reply from a Vim that functions as server was received |server2client()|. The diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 6b46ac9cf2..7716af25bd 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -863,9 +863,11 @@ Note: these are typed literally, they are not special keys! *:<amatch>* *<amatch>* <amatch> When executing autocommands, is replaced with the match for which this autocommand was executed. *E497* - It differs from <afile> only when the file name isn't used - to match with (for FileType, Syntax and SpellFileMissing + It differs from <afile> when the file name isn't used to + match with (for FileType, Syntax and SpellFileMissing events). + When the match is with a file name, it is expanded to the + full path. *:<sfile>* *<sfile>* <sfile> When executing a ":source" command, is replaced with the file name of the sourced file. *E498* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index d41cee3cdd..9a65737dae 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -239,7 +239,7 @@ DiagnosticUnderlineHint *hl-DiagnosticFloatingError* DiagnosticFloatingError Used to color "Error" diagnostic messages in diagnostics float. - See |vim.diagnostic.show_line_diagnostics()| + See |vim.diagnostic.open_float()| *hl-DiagnosticFloatingWarn* DiagnosticFloatingWarn @@ -373,39 +373,8 @@ config({opts}, {namespace}) *vim.diagnostic.config()* Otherwise, all signs use the same priority. - • float: Options for floating windows: - • severity: See |diagnostic-severity|. - • header: (string or table) String to use - as the header for the floating window. If - a table, it is interpreted as a [text, - hl_group] tuple. Defaults to - "Diagnostics:". - • source: (string) Include the diagnostic - source in the message. One of "always" or - "if_many". - • format: (function) A function that takes - a diagnostic as input and returns a - string. The return value is the text used - to display the diagnostic. - • prefix: (function, string, or table) - Prefix each diagnostic in the floating - window. If a function, it must have the - signature (diagnostic, i, total) -> - (string, string), where {i} is the index - of the diagnostic being evaluated and - {total} is the total number of - diagnostics displayed in the window. The - function should return a string which is - prepended to each diagnostic in the - window as well as an (optional) highlight - group which will be used to highlight the - prefix. If {prefix} is a table, it is - interpreted as a [text, hl_group] tuple - as in |nvim_echo()|; otherwise, if - {prefix} is a string, it is prepended to - each diagnostic in the window with no - highlight. - + • float: Options for floating windows. See + |vim.diagnostic.open_float()|. • update_in_insert: (default false) Update diagnostics in Insert mode (if false, diagnostics are updated on InsertLeave) @@ -470,7 +439,7 @@ get_namespace({namespace}) *vim.diagnostic.get_namespace()* Get namespace metadata. Parameters: ~ - {ns} number Diagnostic namespace + {namespace} number Diagnostic namespace Return: ~ table Namespace metadata @@ -539,6 +508,9 @@ goto_next({opts}) *vim.diagnostic.goto_next()* "true", call |vim.diagnostic.open_float()| after moving. If a table, pass the table as the {opts} parameter to |vim.diagnostic.open_float()|. + Unless overridden, the float will show + diagnostics at the new cursor position (as if + "cursor" were passed to the "scope" option). • win_id: (number, default 0) Window ID goto_prev({opts}) *vim.diagnostic.goto_prev()* @@ -613,7 +585,7 @@ open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* addition to the following: • namespace: (number) Limit diagnostics to the given namespace - • scope: (string, default "buffer") Show + • scope: (string, default "line") Show diagnostics from the whole buffer ("buffer"), the current cursor line ("line"), or the current cursor position ("cursor"). @@ -641,8 +613,21 @@ open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* diagnostic. Overrides the setting from |vim.diagnostic.config()|. • prefix: (function, string, or table) Prefix - each diagnostic in the floating window. - Overrides the setting from + each diagnostic in the floating window. If a + function, it must have the signature + (diagnostic, i, total) -> (string, string), + where {i} is the index of the diagnostic being + evaluated and {total} is the total number of + diagnostics displayed in the window. The + function should return a string which is + prepended to each diagnostic in the window as + well as an (optional) highlight group which + will be used to highlight the prefix. If + {prefix} is a table, it is interpreted as a + [text, hl_group] tuple as in |nvim_echo()|; + otherwise, if {prefix} is a string, it is + prepended to each diagnostic in the window with + no highlight. Overrides the setting from |vim.diagnostic.config()|. Return: ~ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d1af4c4d72..75b782fbff 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2042,10 +2042,29 @@ v:option_new New value of the option. Valid while executing an |OptionSet| autocommand. *v:option_old* v:option_old Old value of the option. Valid while executing an |OptionSet| - autocommand. + autocommand. Depending on the command used for setting and the + kind of option this is either the local old value or the + global old value. + *v:option_oldlocal* +v:option_oldlocal + Old local value of the option. Valid while executing an + |OptionSet| autocommand. + *v:option_oldglobal* +v:option_oldglobal + Old global value of the option. Valid while executing an + |OptionSet| autocommand. *v:option_type* v:option_type Scope of the set command. Valid while executing an |OptionSet| autocommand. Can be either "global" or "local" + *v:option_command* +v:option_command + Command used to set the option. Valid while executing an + |OptionSet| autocommand. + value option was set via ~ + "setlocal" |:setlocal| or ":let l:xxx" + "setglobal" |:setglobal| or ":let g:xxx" + "set" |:set| or |:let| + "modeline" |modeline| *v:operator* *operator-variable* v:operator The last operator given in Normal mode. This is a single character except for commands starting with <g> or <z>, @@ -3085,7 +3104,7 @@ bufname([{buf}]) *bufname()* bufname(3) name of buffer 3 bufname("%") name of current buffer bufname("file2") name of buffer where "file2" matches. - +< *bufnr()* bufnr([{buf} [, {create}]]) The result is the number of a buffer, as it is displayed by @@ -3591,7 +3610,7 @@ count({comp}, {expr} [, {ic} [, {start}]]) *count()* Can also be used as a |method|: > mylist->count(val) - +< *cscope_connection()* cscope_connection([{num} , {dbpath} [, {prepend}]]) Checks for the existence of a |cscope| connection. If no @@ -8598,6 +8617,7 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* For the use of {buf}, see |bufname()| above. {lnum} is used like with |setline()|. + Use "$" to refer to the last line in buffer {buf}. When {lnum} is just below the last line the {text} will be added below the last line. On success 0 is returned, on failure 1 is returned. @@ -9078,7 +9098,7 @@ shellescape({string} [, {special}]) *shellescape()* Otherwise encloses {string} in single-quotes and replaces all "'" with "'\''". - If {special} is a ||non-zero-arg|: + If {special} is a |non-zero-arg|: - Special items such as "!", "%", "#" and "<cword>" will be preceded by a backslash. The backslash will be removed again by the |:!| command. @@ -9088,7 +9108,7 @@ shellescape({string} [, {special}]) *shellescape()* - The "!" character will be escaped. This is because csh and tcsh use "!" for history replacement even in single-quotes. - The <NL> character is escaped (twice if {special} is - a ||non-zero-arg|). + a |non-zero-arg|). If 'shell' contains "fish" in the tail, the "\" character will be escaped because in fish it is used as an escape character diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 42a9993c8c..bbbe71ec3a 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -128,18 +128,19 @@ can be used to overrule the filetype used for certain extensions: file name variable ~ *.asa g:filetype_asa |ft-aspvbs-syntax| |ft-aspperl-syntax| - *.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| *.asm g:asmsyntax |ft-asm-syntax| - *.prg g:filetype_prg - *.pl g:filetype_pl - *.inc g:filetype_inc - *.w g:filetype_w |ft-cweb-syntax| + *.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| + *.fs g:filetype_fs |ft-forth-syntax| *.i g:filetype_i |ft-progress-syntax| + *.inc g:filetype_inc *.m g:filetype_m |ft-mathematica-syntax| *.p g:filetype_p |ft-pascal-syntax| + *.pl g:filetype_pl *.pp g:filetype_pp |ft-pascal-syntax| + *.prg g:filetype_prg *.sh g:bash_is_sh |ft-sh-syntax| *.tex g:tex_flavor |ft-tex-plugin| + *.w g:filetype_w |ft-cweb-syntax| *filetype-ignore* To avoid that certain files are being inspected, the g:ft_ignore_pat variable @@ -494,7 +495,6 @@ Options: For further discussion of fortran_have_tabs and the method used for the detection of source format see |ft-fortran-syntax|. - GIT COMMIT *ft-gitcommit-plugin* One command, :DiffGitCached, is provided to show a diff of the current commit diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 1a54ca227f..9bd304cbb4 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -521,7 +521,9 @@ buf_request({bufnr}, {method}, {params}, {handler}) {method} (string) LSP method name {params} (optional, table) Parameters to send to the server - {handler} (optional, function) See |lsp-handler| + {handler} (optional, function) See |lsp-handler| If nil, + follows resolution strategy defined in + |lsp-handler-configuration| Return: ~ 2-tuple: @@ -573,9 +575,6 @@ buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) error, returns `(nil, err)` where `err` is a string describing the failure reason. -check_clients_closed() *vim.lsp.check_clients_closed()* - TODO: Documentation - client() *vim.lsp.client* LSP client object. You can get an active client object via |vim.lsp.get_client_by_id()| or @@ -648,12 +647,21 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* Return: ~ true if client is stopped, false otherwise. -flush({client}) *vim.lsp.flush()* - TODO: Documentation - *vim.lsp.for_each_buffer_client()* for_each_buffer_client({bufnr}, {fn}) - TODO: Documentation + Invokes a function for each LSP client attached to a buffer. + + Parameters: ~ + {bufnr} number Buffer number + {fn} function Function to run on each client attached + to buffer {bufnr}. The function takes the client, + client ID, and buffer number as arguments. + Example: > + + vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) + print(vim.inspect(client)) + end) +< formatexpr({opts}) *vim.lsp.formatexpr()* Provides an interface between the built-in client and a @@ -676,6 +684,8 @@ get_active_clients() *vim.lsp.get_active_clients()* *vim.lsp.get_buffers_by_client_id()* get_buffers_by_client_id({client_id}) + Returns list of buffers attached to client_id. + Parameters: ~ {client_id} number client id @@ -717,16 +727,6 @@ omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* |complete-items| |CompleteDone| - *vim.lsp.prepare()* -prepare({bufnr}, {firstline}, {lastline}, {new_lastline}, {changedtick}) - TODO: Documentation - -reset({client_id}) *vim.lsp.reset()* - TODO: Documentation - -reset_buf({client}, {bufnr}) *vim.lsp.reset_buf()* - TODO: Documentation - set_log_level({level}) *vim.lsp.set_log_level()* Sets the global log level for LSP logging. @@ -796,10 +796,11 @@ start_client({config}) *vim.lsp.start_client()* functions. Commands passed to start_client take precedence over the global command registry. Each key - must be a unique comand name, and the - value is a function which is called - if any LSP action (code action, code - lenses, ...) triggers the command. + must be a unique command name, and + the value is a function which is + called if any LSP action (code + action, code lenses, ...) triggers + the command. {init_options} Values to pass in the initialization request as `initializationOptions` . See `initialize` in the LSP spec. @@ -873,6 +874,11 @@ start_client({config}) *vim.lsp.start_client()* debounce occurs if nil • exit_timeout (number, default 500): Milliseconds to wait for server to + exit cleanly after sending the + 'shutdown' request before sending + kill -15. If set to false, nvim + exits immediately after sending the + 'shutdown' request to the server. {root_dir} string Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on @@ -985,9 +991,9 @@ document_highlight() *vim.lsp.buf.document_highlight()* triggered by a key mapping or by events such as `CursorHold` , eg: > - vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] - vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] - vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] + autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() + autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight() + autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references() < Note: Usage of |vim.lsp.buf.document_highlight()| requires the @@ -1055,7 +1061,7 @@ formatting_sync({options}, {timeout_ms}) |vim.lsp.buf_request_sync()|. Example: > - vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] + autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync() < Parameters: ~ @@ -1087,9 +1093,6 @@ outgoing_calls() *vim.lsp.buf.outgoing_calls()* cursor in the |quickfix| window. If the symbol can resolve to multiple items, the user can pick one in the |inputlist|. -prepare_rename({err}, {result}) *vim.lsp.buf.prepare_rename()* - TODO: Documentation - *vim.lsp.buf.range_code_action()* range_code_action({context}, {start_pos}, {end_pos}) Performs |vim.lsp.buf.code_action()| for a given range. @@ -1344,25 +1347,22 @@ buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* Removes document highlights from a buffer. Parameters: ~ - {bufnr} buffer id + {bufnr} number Buffer id *vim.lsp.util.buf_highlight_references()* buf_highlight_references({bufnr}, {references}, {offset_encoding}) Shows a list of document highlights for a certain buffer. Parameters: ~ - {bufnr} buffer id - {references} List of `DocumentHighlight` objects to - highlight - {offset_encoding} string utf-8|utf-16|utf-32|nil defaults - to utf-16 + {bufnr} number Buffer id + {references} table List of `DocumentHighlight` + objects to highlight + {offset_encoding} string One of "utf-8", "utf-16", + "utf-32", or nil. Defaults to utf-16 See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight -buf_lines({bufnr}) *vim.lsp.util.buf_lines()* - TODO: Documentation - *vim.lsp.util.character_offset()* character_offset({bufnr}, {row}, {col}) Returns the UTF-32 and UTF-16 offsets for a position in a @@ -1429,12 +1429,6 @@ convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp -create_file({change}) *vim.lsp.util.create_file()* - TODO: Documentation - -delete_file({change}) *vim.lsp.util.delete_file()* - TODO: Documentation - *vim.lsp.util.extract_completion_items()* extract_completion_items({result}) Can be used to extract the completion items from a `textDocument/completion` request, which may return one of `CompletionItem[]` , `CompletionList` or null. @@ -1462,9 +1456,6 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* See also: ~ |softtabstop| -get_progress_messages() *vim.lsp.util.get_progress_messages()* - TODO: Documentation - jump_to_location({location}) *vim.lsp.util.jump_to_location()* Jumps to a location. @@ -1599,25 +1590,32 @@ open_floating_preview({contents}, {syntax}, {opts}) Parameters: ~ {contents} table of lines to show in window {syntax} string of syntax to set for opened buffer - {opts} dictionary with optional fields - • height of floating window - • width of floating window - • wrap boolean enable wrapping of long lines - (defaults to true) - • wrap_at character to wrap at for computing - height when wrap is enabled - • max_width maximal width of floating window - • max_height maximal height of floating window - • pad_top number of lines to pad contents at - top - • pad_bottom number of lines to pad contents - at bottom - • focus_id if a popup with this id is opened, - then focus it - • close_events list of events that closes the + {opts} table with optional fields (additional keys + are passed on to |vim.api.nvim_open_win()|) + • height: (number) height of floating window + • width: (number) width of floating window + • wrap: (boolean, default true) wrap long + lines + • wrap_at: (string) character to wrap at for + computing height when wrap is enabled + • max_width: (number) maximal width of + floating window + • max_height: (number) maximal height of floating window - • focusable (boolean, default true): Make + • pad_top: (number) number of lines to pad + contents at top + • pad_bottom: (number) number of lines to pad + contents at bottom + • focus_id: (string) if a popup with this id + is opened, then focus it + • close_events: (table) list of events that + closes the floating window + • focusable: (boolean, default true) Make float focusable + • focus: (boolean, default true) If `true` , + and if {focusable} is also `true` , focus an + existing floating window with the same + {focus_id} Return: ~ bufnr,winnr buffer and window number of the newly created @@ -1758,7 +1756,10 @@ get_filename() *vim.lsp.log.get_filename()* (string) log filename get_level() *vim.lsp.log.get_level()* - TODO: Documentation + Gets the current log level. + + Return: ~ + string current log level set_format_func({handle}) *vim.lsp.log.set_format_func()* Sets formatting function used to format logs @@ -1837,7 +1838,8 @@ rpc_response_error({code}, {message}, {data}) *vim.lsp.rpc.start()* start({cmd}, {cmd_args}, {dispatchers}, {extra_spawn_params}) Starts an LSP server process and create an LSP RPC client - object to interact with it. + object to interact with it. Communication with the server is + currently limited to stdio. Parameters: ~ {cmd} (string) Command to start the LSP @@ -1896,10 +1898,6 @@ compute_diff({prev_lines}, {curr_lines}, {firstline}, {lastline}, Return: ~ table TextDocumentContentChangeEvent see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocumentContentChangeEvent - *vim.lsp.sync.compute_line_length()* -compute_line_length({line}, {offset_encoding}) - TODO: Documentation - ============================================================================== Lua module: vim.lsp.protocol *lsp-protocol* diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 2db1974966..630df16e79 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -362,8 +362,8 @@ cases there is the following agreement: converted to a dictionary `{'a': 42}`: non-string keys are ignored. Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3. are errors. - - `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well - as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not + - `{[vim.type_idx]=vim.types.array}` is converted to an empty list. As well + as `{[vim.type_idx]=vim.types.array, [42]=1}`: integral keys that do not form a 1-step sequence from 1 to N are ignored, as well as all non-integral keys. @@ -1190,9 +1190,6 @@ defer_fn({fn}, {timeout}) *vim.defer_fn()* Return: ~ timer luv timer object -insert_keys({obj}) *vim.insert_keys()* - TODO: Documentation - inspect({object}, {options}) *vim.inspect()* Return a human-readable representation of the given object. @@ -1235,7 +1232,7 @@ on_key({fn}, {ns_id}) *vim.on_key()* it removes the callback for the associated {ns_id} {ns_id} number? Namespace ID. If nil or 0, generates and - returns a new |nvim_create_namesapce()| id. + returns a new |nvim_create_namespace()| id. Return: ~ number Namespace id associated with {fn}. Or count of all @@ -1324,7 +1321,7 @@ deepcopy({orig}) *vim.deepcopy()* and will throw an error. Parameters: ~ - {orig} Table to copy + {orig} table Table to copy Return: ~ New table of copied keys and (nested) values. @@ -1365,9 +1362,6 @@ is_callable({f}) *vim.is_callable()* Return: ~ true if `f` is callable, else false -is_valid({opt}) *vim.is_valid()* - TODO: Documentation - list_extend({dst}, {src}, {start}, {finish}) *vim.list_extend()* Extends a list-like table with the values of another list-like table. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 038808b760..47633c750c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -684,9 +684,12 @@ A jump table for the options with a short description can be found at |Q_op|. 'autowrite' 'aw' boolean (default off) global Write the contents of the file, if it has been modified, on each - :next, :rewind, :last, :first, :previous, :stop, :suspend, :tag, :!, - :make, CTRL-] and CTRL-^ command; and when a :buffer, CTRL-O, CTRL-I, - '{A-Z0-9}, or `{A-Z0-9} command takes one to another file. + `:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`, + `:suspend`, `:tag`, `:!`, `:make`, CTRL-] and CTRL-^ command; and when + a :buffer, CTRL-O, CTRL-I, '{A-Z0-9}, or `{A-Z0-9} command takes one + to another file. + A buffer is not written if it becomes hidden, e.g. when 'bufhidden' is + set to "hide" and `:next` is used Note that for some commands the 'autowrite' option is not used, see 'autowriteall' for that. Some buffers will not be written, specifically when 'buftype' is diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt index 951b897f55..1dbd268038 100644 --- a/runtime/doc/pi_msgpack.txt +++ b/runtime/doc/pi_msgpack.txt @@ -68,7 +68,7 @@ msgpack#strftime({format}, {msgpack-integer}) *msgpack#strftime()* *msgpack#strptime* msgpack#strptime({format}, {time}) *msgpack#strptime()* - Reverse of |msgpack#strptime()|: for any time and format + Reverse of |msgpack#strftime()|: for any time and format |msgpack#equal|( |msgpack#strptime|(format, |msgpack#strftime|(format, time)), time) be true. Requires |+python| or |+python3|, without it only supports non-|msgpack-special-dict| nonnegative times and format diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 9c1f584415..ba1da209f7 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -837,9 +837,9 @@ lists. They set one of the existing error lists as the current one. *:chistory* *:chi* :[count]chi[story] Show the list of error lists. The current list is marked with ">". The output looks like: - error list 1 of 3; 43 errors ~ - > error list 2 of 3; 0 errors ~ - error list 3 of 3; 15 errors ~ + error list 1 of 3; 43 errors :make ~ + > error list 2 of 3; 0 errors :helpgrep tag ~ + error list 3 of 3; 15 errors :grep ex_help *.c ~ When [count] is given, then the count'th quickfix list is made the current list. Example: > diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 9181684945..895ee5ebef 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -419,11 +419,12 @@ sign_getdefined([{name}]) *sign_getdefined()* following entries: icon full path to the bitmap file of the sign linehl highlight group used for the whole line the - sign is placed in. + sign is placed in; not present if not set. name name of the sign text text that is displayed when there is no icon or the GUI is not being used. - texthl highlight group used for the text item + texthl highlight group used for the text item; not + present if not set. numhl highlight group used for 'number' column at the associated line. Overrides |hl-LineNr|, |hl-CursorLineNr|. diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index e423c59efe..b57b8bfd5c 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1497,6 +1497,14 @@ gvim display. Here, statements are colored LightYellow instead of Yellow, and conditionals are LightBlue for better distinction. +FORTH *forth.vim* *ft-forth-syntax* + +Files matching "*.fs" could be F# or Forth. If the automatic detection +doesn't work for you, or you don't edit F# at all, use this in your +startup vimrc: > + :let filetype_fs = "forth" + + FORTRAN *fortran.vim* *ft-fortran-syntax* Default highlighting and dialect ~ @@ -3153,6 +3161,14 @@ buffer by buffer basis. For more detailed instructions see |ft_sql.txt|. +SQUIRREL *squirrel.vim* *ft-squirrel-syntax* + +Squirrel is a high level imperative, object-oriented programming language, +designed to be a light-weight scripting language that fits in the size, memory +bandwidth, and real-time requirements of applications like video games. Files +with the following extensions are recognized as squirrel files: .nut. + + TCSH *tcsh.vim* *ft-tcsh-syntax* This covers the shell named "tcsh". It is a superset of csh. See |csh.vim| diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index ac10aeec88..08dc0583ac 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -353,7 +353,7 @@ Lua module: vim.treesitter *lua-treesitter-core* get_parser({bufnr}, {lang}, {opts}) *get_parser()* Gets the parser for this bufnr / ft combination. - If needed this will create the parser. Unconditionnally attach + If needed this will create the parser. Unconditionally attach the provided callback Parameters: ~ @@ -380,7 +380,7 @@ Lua module: vim.treesitter.language *treesitter-language* inspect_language({lang}) *inspect_language()* Inspects the provided language. - Inspecting provides some useful informations on the language + Inspecting provides some useful information on the language like node names, ... Parameters: ~ @@ -479,7 +479,7 @@ Query:iter_captures({self}, {node}, {source}, {start}, {stop}) {source} is needed if the query contains predicates, then the caller must ensure to use a freshly parsed tree consistent - with the current text of the buffer (if relevent). {start_row} + with the current text of the buffer (if relevant). {start_row} and {end_row} can be used to limit matches inside a row range (this is typically used with root node as the node, i e to get syntax highlight matches in the current viewport). When @@ -503,8 +503,7 @@ Query:iter_captures({self}, {node}, {source}, {start}, {stop}) Parameters: ~ {node} The node under which the search will occur - {source} The source buffer or string to exctract text - from + {source} The source buffer or string to extract text from {start} The starting line of the search {stop} The stopping line of the search (end-exclusive) {self} @@ -531,9 +530,11 @@ Query:iter_matches({self}, {node}, {source}, {start}, {stop}) for id, node in pairs(match) do local name = query.captures[id] -- `node` was captured by the `name` capture in the match - - local node_data = metadata[id] -- Node level metadata - +< +> + local node_data = metadata[id] -- Node level metadata +< +> ... use the info here ... end end @@ -609,7 +610,7 @@ LanguageTree:children({self}) *LanguageTree:children()* {self} LanguageTree:contains({self}, {range}) *LanguageTree:contains()* - Determines wether This goes down the tree to recursively check childs. + Determines whether This goes down the tree to recursively check children. Parameters: ~ {range} is contained in this language tree diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index d88f4f42e8..956cb3e624 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -428,7 +428,7 @@ Working directory (Vim implemented some of these later than Nvim): - |DirChanged| can be triggered when switching to another window. - |getcwd()| and |haslocaldir()| may throw errors if the tab page or window cannot be found. *E5000* *E5001* *E5002* -- |haslocaldir()| only checks for tab-local directory when -1 is passed as +- |haslocaldir()| checks for tab-local directory if and only if -1 is passed as window number, and its only possible returns values are 0 and 1. - `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global working directory. Use `getcwd(-1, -1)` to get the global working directory. diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 1b48070128..e1c917ac64 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Nov 16 +" Last Change: 2021 Dec 03 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -119,7 +119,7 @@ au BufNewFile,BufRead *.aml setf aml " APT config file au BufNewFile,BufRead apt.conf setf aptconf au BufNewFile,BufRead */.aptitude/config setf aptconf -au BufNewFile,BufRead */etc/apt/apt.conf.d/{[-_[:alnum:]]\+,[-_.[:alnum:]]\+.conf} setf aptconf +" more generic pattern far down " Arch Inventory file au BufNewFile,BufRead .arch-inventory,=tagging-method setf arch @@ -484,7 +484,7 @@ au BufNewFile,BufRead *.desktop,*.directory setf desktop au BufNewFile,BufRead dict.conf,.dictrc setf dictconf " Dictd config -au BufNewFile,BufRead dictd.conf setf dictdconf +au BufNewFile,BufRead dictd*.conf setf dictdconf " Diff files au BufNewFile,BufRead *.diff,*.rej setf diff @@ -628,7 +628,7 @@ au BufNewFile,BufRead auto.master setf conf au BufNewFile,BufRead *.mas,*.master setf master " Forth -au BufNewFile,BufRead *.fs,*.ft,*.fth setf forth +au BufNewFile,BufRead *.ft,*.fth setf forth " Reva Forth au BufNewFile,BufRead *.frt setf reva @@ -645,6 +645,12 @@ au BufNewFile,BufRead *.fsl setf framescript " FStab au BufNewFile,BufRead fstab,mtab setf fstab +" F# or Forth +au BufNewFile,BufRead *.fs call dist#ft#FTfs() + +" F# +au BufNewFile,BufRead *.fsi,*.fsx setf fsharp + " GDB command files au BufNewFile,BufRead .gdbinit,gdbinit setf gdb @@ -1095,6 +1101,9 @@ au BufNewFile,BufRead *.moo setf moo " Modconf au BufNewFile,BufRead */etc/modules.conf,*/etc/modules,*/etc/conf.modules setf modconf +" MPD is based on XML +au BufNewFile,BufRead *.mpd setf xml + " Mplayer config au BufNewFile,BufRead mplayer.conf,*/.mplayer/config setf mplayerconf @@ -1110,14 +1119,15 @@ au BufNewFile,BufRead *.msql setf msql " Mysql au BufNewFile,BufRead *.mysql setf mysql -" Mutt setup files (must be before catch *.rc) -au BufNewFile,BufRead */etc/Muttrc.d/* call s:StarSetf('muttrc') - " Tcl Shell RC file au BufNewFile,BufRead tclsh.rc setf tcl " M$ Resource files -au BufNewFile,BufRead *.rc,*.rch setf rc +" /etc/Muttrc.d/file.rc is muttrc +au BufNewFile,BufRead *.rc,*.rch + \ if expand("<afile>") !~ "/etc/Muttrc.d/" | + \ setf rc | + \ endif " MuPAD source au BufRead,BufNewFile *.mu setf mupad @@ -1743,6 +1753,9 @@ au BufNewFile,BufRead *.sqlj setf sqlj " SQR au BufNewFile,BufRead *.sqr,*.sqi setf sqr +" Squirrel +au BufNewFile,BufRead *.nut setf squirrel + " OpenSSH configuration au BufNewFile,BufRead ssh_config,*/.ssh/config setf sshconfig au BufNewFile,BufRead */etc/ssh/ssh_config.d/*.conf setf sshconfig @@ -2055,9 +2068,15 @@ au BufNewFile,BufRead *.xml call dist#ft#FTxml() " XMI (holding UML models) is also XML au BufNewFile,BufRead *.xmi setf xml -" CSPROJ files are Visual Studio.NET's XML-based project config files +" CSPROJ files are Visual Studio.NET's XML-based C# project config files au BufNewFile,BufRead *.csproj,*.csproj.user setf xml +" FSPROJ files are Visual Studio.NET's XML-based F# project config files +au BufNewFile,BufRead *.fsproj,*.fsproj.user setf xml + +" VBPROJ files are Visual Studio.NET's XML-based Visual Basic project config files +au BufNewFile,BufRead *.vbproj,*.vbproj.user setf xml + " Qt Linguist translation source and Qt User Interface Files are XML " However, for .ts Typescript is more common. au BufNewFile,BufRead *.ui setf xml @@ -2143,6 +2162,12 @@ au BufNewFile,BufRead * au StdinReadPost * if !did_filetype() | runtime! scripts.vim | endif +" Plain text files, needs to be far down to not override others. This avoids +" the "conf" type being used if there is a line starting with '#'. +" But before patterns matching everything in a directory. +au BufNewFile,BufRead *.text,README,LICENSE,COPYING,AUTHORS setf text + + " Extra checks for when no filetype has been detected now. Mostly used for " patterns that end in "*". E.g., "zsh*" matches "zsh.vim", but that's a Vim " script file. @@ -2155,7 +2180,10 @@ au BufNewFile,BufRead proftpd.conf* call s:StarSetf('apachestyle') " More Apache config files au BufNewFile,BufRead access.conf*,apache.conf*,apache2.conf*,httpd.conf*,srm.conf* call s:StarSetf('apache') -au BufNewFile,BufRead */etc/apache2/*.conf*,*/etc/apache2/conf.*/*,*/etc/apache2/mods-*/*,*/etc/apache2/sites-*/*,*/etc/httpd/conf.d/*.conf* call s:StarSetf('apache') +au BufNewFile,BufRead */etc/apache2/*.conf*,*/etc/apache2/conf.*/*,*/etc/apache2/mods-*/*,*/etc/apache2/sites-*/*,*/etc/httpd/conf.*/*,*/etc/httpd/mods-*/*,*/etc/httpd/sites-*/*,*/etc/httpd/conf.d/*.conf* call s:StarSetf('apache') + +" APT config file +au BufNewFile,BufRead */etc/apt/apt.conf.d/{[-_[:alnum:]]\+,[-_.[:alnum:]]\+.conf} call s:StarSetf('aptconf') " Asterisk config file au BufNewFile,BufRead *asterisk/*.conf* call s:StarSetf('asterisk') @@ -2258,6 +2286,9 @@ au BufNewFile,BufRead */etc/modutils/* \|endif au BufNewFile,BufRead */etc/modprobe.* call s:StarSetf('modconf') +" Mutt setup files (must be before catch *.rc) +au BufNewFile,BufRead */etc/Muttrc.d/* call s:StarSetf('muttrc') + " Mutt setup file au BufNewFile,BufRead .mutt{ng,}rc*,*/.mutt{ng,}/mutt{ng,}rc* call s:StarSetf('muttrc') au BufNewFile,BufRead mutt{ng,}rc*,Mutt{ng,}rc* call s:StarSetf('muttrc') @@ -2350,10 +2381,6 @@ au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh') au BufNewFile,BufRead zsh*,zlog* call s:StarSetf('zsh') -" Plain text files, needs to be far down to not override others. This avoids -" the "conf" type being used if there is a line starting with '#'. -au BufNewFile,BufRead *.text,README setf text - " Help files match *.txt but should have a last line that is a modeline. au BufNewFile,BufRead *.txt \ if getline('$') !~ 'vim:.*ft=help' diff --git a/runtime/ftplugin/jsonc.vim b/runtime/ftplugin/jsonc.vim index 90d52cd0d3..e47a75f574 100644 --- a/runtime/ftplugin/jsonc.vim +++ b/runtime/ftplugin/jsonc.vim @@ -4,7 +4,7 @@ " Acknowledgement: Based off of vim-jsonc maintained by Kevin Locke <kevin@kevinlocke.name> " https://github.com/kevinoid/vim-jsonc " License: MIT -" Last Change: 2021-07-01 +" Last Change: 2021 Nov 22 runtime! ftplugin/json.vim @@ -14,14 +14,8 @@ else let b:did_ftplugin_jsonc = 1 endif -" A list of commands that undo buffer local changes made below. -let s:undo_ftplugin = [] - " Set comment (formatting) related options. {{{1 setlocal commentstring=//%s comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// -call add(s:undo_ftplugin, 'commentstring< comments<') " Let Vim know how to disable the plug-in. -call map(s:undo_ftplugin, "'execute ' . string(v:val)") -let b:undo_ftplugin = join(s:undo_ftplugin, ' | ') -unlet s:undo_ftplugin +let b:undo_ftplugin = 'setlocal commentstring< comments<' diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index a98c75e541..7c03ff2873 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Nov 03 +" Last Change: 2021 Nov 27 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -12,6 +12,7 @@ let b:did_indent = 1 setlocal indentexpr=GetVimIndent() setlocal indentkeys+==end,=},=else,=cat,=finall,=END,0\\,0=\"\\\ setlocal indentkeys-=0# +setlocal indentkeys-=: let b:undo_indent = "setl indentkeys< indentexpr<" diff --git a/runtime/lua/vim/F.lua b/runtime/lua/vim/F.lua index 1a258546a5..9327c652db 100644 --- a/runtime/lua/vim/F.lua +++ b/runtime/lua/vim/F.lua @@ -27,7 +27,7 @@ function F.nil_wrap(fn) end end ---- like {...} except preserve the lenght explicitly +--- like {...} except preserve the length explicitly function F.pack_len(...) return {n=select('#', ...), ...} end diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index f7d47c1030..522e26caa7 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -16,10 +16,6 @@ for _, v in pairs(a.nvim_get_all_options_info()) do if v.shortname ~= "" then options_info[v.shortname] = v end end -local is_global_option = function(info) return info.scope == "global" end -local is_buffer_option = function(info) return info.scope == "buf" end -local is_window_option = function(info) return info.scope == "win" end - local get_scoped_options = function(scope) local result = {} for name, option_info in pairs(options_info) do @@ -133,107 +129,18 @@ do -- window option accessor vim.wo = new_win_opt_accessor(nil) end ---[[ -Local window setter - -buffer options: does not get copied when split - nvim_set_option(buf_opt, value) -> sets the default for NEW buffers - this sets the hidden global default for buffer options - - nvim_buf_set_option(...) -> sets the local value for the buffer - - set opt=value, does BOTH global default AND buffer local value - setlocal opt=value, does ONLY buffer local value - -window options: gets copied - does not need to call nvim_set_option because nobody knows what the heck this does⸮ - We call it anyway for more readable code. - - - Command global value local value - :set option=value set set - :setlocal option=value - set -:setglobal option=value set - ---]] -local function set_scoped_option(k, v, set_type) - local info = options_info[k] - - -- Don't let people do setlocal with global options. - -- That is a feature that doesn't make sense. - if set_type == SET_TYPES.LOCAL and is_global_option(info) then - error(string.format("Unable to setlocal option: '%s', which is a global option.", k)) - end - - -- Only `setlocal` skips setting the default/global value - -- This will more-or-less noop for window options, but that's OK - if set_type ~= SET_TYPES.LOCAL then - a.nvim_set_option(k, v) - end - - if is_window_option(info) then - if set_type ~= SET_TYPES.GLOBAL then - a.nvim_win_set_option(0, k, v) - end - elseif is_buffer_option(info) then - if set_type == SET_TYPES.LOCAL - or (set_type == SET_TYPES.SET and not info.global_local) then - a.nvim_buf_set_option(0, k, v) - end - end -end - ---[[ -Local window getter - - Command global value local value - :set option? - display - :setlocal option? - display -:setglobal option? display - ---]] -local function get_scoped_option(k, set_type) - local info = assert(options_info[k], "Must be a valid option: " .. tostring(k)) - - if set_type == SET_TYPES.GLOBAL or is_global_option(info) then - return a.nvim_get_option(k) - end - - if is_buffer_option(info) then - local was_set, value = pcall(a.nvim_buf_get_option, 0, k) - if was_set then return value end - - if info.global_local then - return a.nvim_get_option(k) - end - - error("buf_get: This should not be able to happen, given my understanding of options // " .. k) - end - - if is_window_option(info) then - local ok, value = pcall(a.nvim_win_get_option, 0, k) - if ok then - return value - end - - local global_ok, global_val = pcall(a.nvim_get_option, k) - if global_ok then - return global_val - end - - error("win_get: This should never happen. File an issue and tag @tjdevries") - end - - error("This fallback case should not be possible. " .. k) -end - -- vim global option -- this ONLY sets the global option. like `setglobal` -vim.go = make_meta_accessor(a.nvim_get_option, a.nvim_set_option) +vim.go = make_meta_accessor( + function(k) return a.nvim_get_option_value(k, {scope = "global"}) end, + function(k, v) return a.nvim_set_option_value(k, v, {scope = "global"}) end +) -- vim `set` style options. -- it has no additional metamethod magic. vim.o = make_meta_accessor( - function(k) return get_scoped_option(k, SET_TYPES.SET) end, - function(k, v) return set_scoped_option(k, v, SET_TYPES.SET) end + function(k) return a.nvim_get_option_value(k, {}) end, + function(k, v) return a.nvim_set_option_value(k, v, {}) end ) ---@brief [[ @@ -389,6 +296,10 @@ local convert_value_to_vim = (function() } return function(name, info, value) + if value == nil then + return vim.NIL + end + local option_type = get_option_type(name, info) assert_valid_value(name, value, valid_types[option_type]) @@ -398,7 +309,7 @@ end)() --- Converts a vimoption_T style value to a Lua value local convert_value_to_lua = (function() - -- Map of OptionType to functions that take vimoption_T values and conver to lua values. + -- Map of OptionType to functions that take vimoption_T values and convert to lua values. -- Each function takes (info, vim_value) -> lua_value local to_lua_value = { [OptionTypes.BOOLEAN] = function(_, value) return value end, @@ -671,15 +582,19 @@ local create_option_metatable = function(set_type) }, option_mt) end - -- TODO(tjdevries): consider supporting `nil` for set to remove the local option. - -- vim.cmd [[set option<]] + local scope + if set_type == SET_TYPES.GLOBAL then + scope = "global" + elseif set_type == SET_TYPES.LOCAL then + scope = "local" + end option_mt = { -- To set a value, instead use: -- opt[my_option] = value _set = function(self) local value = convert_value_to_vim(self._name, self._info, self._value) - set_scoped_option(self._name, value, set_type) + a.nvim_set_option_value(self._name, value, {scope = scope}) return self end, @@ -716,7 +631,7 @@ local create_option_metatable = function(set_type) set_mt = { __index = function(_, k) - return make_option(k, get_scoped_option(k, set_type)) + return make_option(k, a.nvim_get_option_value(k, {scope = scope})) end, __newindex = function(_, k, v) diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 015db54d34..ac4081bb54 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -386,7 +386,7 @@ local function get_diagnostics(bufnr, opts, clamp) if not opts.lnum or d.lnum == opts.lnum then if clamp and vim.api.nvim_buf_is_loaded(b) then local line_count = buf_line_count[b] - 1 - if (d.lnum > line_count or d.end_lnum > line_count) then + if (d.lnum > line_count or d.end_lnum > line_count or d.lnum < 0 or d.end_lnum < 0) then d = vim.deepcopy(d) d.lnum = math.max(math.min(d.lnum, line_count), 0) d.end_lnum = math.max(math.min(d.end_lnum, line_count), 0) @@ -508,10 +508,13 @@ local function diagnostic_move_pos(opts, pos) return end - -- Save position in the window's jumplist - vim.api.nvim_win_call(win_id, function() vim.cmd("normal! m'") end) - - vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) + vim.api.nvim_win_call(win_id, function() + -- Save position in the window's jumplist + vim.cmd("normal! m'") + vim.api.nvim_win_set_cursor(win_id, {pos[1] + 1, pos[2]}) + -- Open folds under the cursor + vim.cmd("normal! zv") + end) if float then local float_opts = type(float) == "table" and float or {} @@ -520,7 +523,7 @@ local function diagnostic_move_pos(opts, pos) vim.api.nvim_win_get_buf(win_id), vim.tbl_extend("keep", float_opts, { scope = "cursor", - focusable = false, + focus = false, }) ) end) @@ -579,26 +582,7 @@ end --- * priority: (number, default 10) Base priority to use for signs. When --- {severity_sort} is used, the priority of a sign is adjusted based on --- its severity. Otherwise, all signs use the same priority. ---- - float: Options for floating windows: ---- * severity: See |diagnostic-severity|. ---- * header: (string or table) String to use as the header for the floating ---- window. If a table, it is interpreted as a [text, hl_group] tuple. ---- Defaults to "Diagnostics:". ---- * source: (string) Include the diagnostic source in ---- the message. One of "always" or "if_many". ---- * format: (function) A function that takes a diagnostic as input and returns a ---- string. The return value is the text used to display the diagnostic. ---- * prefix: (function, string, or table) Prefix each diagnostic in the floating ---- window. If a function, it must have the signature (diagnostic, i, ---- total) -> (string, string), where {i} is the index of the diagnostic ---- being evaluated and {total} is the total number of diagnostics ---- displayed in the window. The function should return a string which ---- is prepended to each diagnostic in the window as well as an ---- (optional) highlight group which will be used to highlight the ---- prefix. If {prefix} is a table, it is interpreted as a [text, ---- hl_group] tuple as in |nvim_echo()|; otherwise, if {prefix} is a ---- string, it is prepended to each diagnostic in the window with no ---- highlight. +--- - float: Options for floating windows. See |vim.diagnostic.open_float()|. --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in @@ -606,6 +590,7 @@ end --- are displayed before lower severities (e.g. ERROR is displayed before WARN). --- Options: --- * reverse: (boolean) Reverse sort order +--- ---@param namespace number|nil Update the options for the given namespace. When omitted, update the --- global diagnostic options. function M.config(opts, namespace) @@ -668,12 +653,17 @@ function M.set(namespace, bufnr, diagnostics, opts) end if vim.api.nvim_buf_is_loaded(bufnr) then - M.show(namespace, bufnr, diagnostics, opts) + M.show(namespace, bufnr, nil, opts) end - vim.api.nvim_command( - string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr)) - ) + vim.api.nvim_buf_call(bufnr, function() + vim.api.nvim_command( + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) + ) + end) end --- Get namespace metadata. @@ -800,7 +790,9 @@ end --- - severity: See |diagnostic-severity|. --- - float: (boolean or table, default true) If "true", call |vim.diagnostic.open_float()| --- after moving. If a table, pass the table as the {opts} parameter to ---- |vim.diagnostic.open_float()|. +--- |vim.diagnostic.open_float()|. Unless overridden, the float will show +--- diagnostics at the new cursor position (as if "cursor" were passed to +--- the "scope" option). --- - win_id: (number, default 0) Window ID function M.goto_next(opts) return diagnostic_move_pos( @@ -1147,7 +1139,7 @@ end ---@param opts table|nil Configuration table with the same keys as --- |vim.lsp.util.open_floating_preview()| in addition to the following: --- - namespace: (number) Limit diagnostics to the given namespace ---- - scope: (string, default "buffer") Show diagnostics from the whole buffer ("buffer"), +--- - scope: (string, default "line") Show diagnostics from the whole buffer ("buffer"), --- the current cursor line ("line"), or the current cursor position ("cursor"). --- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather --- than the cursor position. If a number, interpreted as a line number; @@ -1164,7 +1156,17 @@ end --- - format: (function) A function that takes a diagnostic as input and returns a --- string. The return value is the text used to display the diagnostic. --- Overrides the setting from |vim.diagnostic.config()|. ---- - prefix: (function, string, or table) Prefix each diagnostic in the floating window. +--- - prefix: (function, string, or table) Prefix each diagnostic in the floating +--- window. If a function, it must have the signature (diagnostic, i, +--- total) -> (string, string), where {i} is the index of the diagnostic +--- being evaluated and {total} is the total number of diagnostics +--- displayed in the window. The function should return a string which +--- is prepended to each diagnostic in the window as well as an +--- (optional) highlight group which will be used to highlight the +--- prefix. If {prefix} is a table, it is interpreted as a [text, +--- hl_group] tuple as in |nvim_echo()|; otherwise, if {prefix} is a +--- string, it is prepended to each diagnostic in the window with no +--- highlight. --- Overrides the setting from |vim.diagnostic.config()|. ---@return tuple ({float_bufnr}, {win_id}) function M.open_float(bufnr, opts) @@ -1175,7 +1177,7 @@ function M.open_float(bufnr, opts) opts = opts or {} bufnr = get_bufnr(bufnr) - local scope = opts.scope or "buffer" + local scope = opts.scope or "line" local lnum, col if scope == "line" or scope == "cursor" then if not opts.pos then @@ -1334,7 +1336,10 @@ function M.reset(namespace, bufnr) end vim.api.nvim_command( - string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr)) + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) ) end diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 6abb81d144..dbbfd7d1d8 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -137,12 +137,6 @@ local all_buffer_active_clients = {} local uninitialized_clients = {} ---@private ---- Invokes a function for each LSP client attached to the buffer {bufnr}. ---- ----@param bufnr (Number) of buffer ----@param fn (function({client}, {client_id}, {bufnr}) Function to run on ----each client attached to that buffer. ----@param restrict_client_ids table list of client ids on which to restrict function application. local function for_each_buffer_client(bufnr, fn, restrict_client_ids) validate { fn = { fn, 'f' }; @@ -327,6 +321,7 @@ do --- timer?: uv_timer local state_by_client = {} + ---@private function changetracking.init(client, bufnr) local state = state_by_client[client.id] if not state then @@ -348,6 +343,7 @@ do state.buffers[bufnr] = nvim_buf_get_lines(bufnr, 0, -1, true) end + ---@private function changetracking.reset_buf(client, bufnr) changetracking.flush(client) local state = state_by_client[client.id] @@ -356,6 +352,7 @@ do end end + ---@private function changetracking.reset(client_id) local state = state_by_client[client_id] if state then @@ -364,6 +361,7 @@ do end end + ---@private function changetracking.prepare(bufnr, firstline, lastline, new_lastline) local incremental_changes = function(client) local cached_buffers = state_by_client[client.id].buffers @@ -447,6 +445,7 @@ do end --- Flushes any outstanding change notification. + ---@private function changetracking.flush(client) local state = state_by_client[client.id] if state then @@ -560,6 +559,12 @@ end --- --- - {handlers} (table): The handlers used by the client as described in |lsp-handler|. --- +--- - {requests} (table): The current pending requests in flight +--- to the server. Entries are key-value pairs with the key +--- being the request ID while the value is a table with `type`, +--- `bufnr`, and `method` key-value pairs. `type` is either "pending" +--- for an active request, or "cancel" for a cancel request. +--- --- - {config} (table): copy of the table that was passed by the user --- to |vim.lsp.start_client()|. --- @@ -621,7 +626,7 @@ end --- ---@param commands table Table that maps string of clientside commands to user-defined functions. --- Commands passed to start_client take precedence over the global command registry. Each key ---- must be a unique comand name, and the value is a function which is called if any LSP action +--- must be a unique command name, and the value is a function which is called if any LSP action --- (code action, code lenses, ...) triggers the command. --- ---@param init_options Values to pass in the initialization request @@ -675,8 +680,8 @@ end --- notifications to the server by the given number in milliseconds. No debounce --- occurs if nil --- - exit_timeout (number, default 500): Milliseconds to wait for server to --- exit cleanly after sending the 'shutdown' request before sending kill -15. --- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. +--- exit cleanly after sending the 'shutdown' request before sending kill -15. +--- If set to false, nvim exits immediately after sending the 'shutdown' request to the server. --- ---@param root_dir string Directory where the LSP --- server will base its workspaceFolders, rootUri, and rootPath @@ -1034,7 +1039,7 @@ function lsp.start_client(config) return rpc.notify("$/cancelRequest", { id = id }) end - -- Track this so that we can escalate automatically if we've alredy tried a + -- Track this so that we can escalate automatically if we've already tried a -- graceful shutdown local graceful_shutdown_failed = false ---@private @@ -1218,7 +1223,7 @@ end --- Gets a client by id, or nil if the id is invalid. --- The returned client may not yet be fully initialized. --- +--- ---@param client_id number client id --- ---@returns |vim.lsp.client| object, or nil @@ -1227,7 +1232,7 @@ function lsp.get_client_by_id(client_id) end --- Returns list of buffers attached to client_id. --- +--- ---@param client_id number client id ---@returns list of buffer ids function lsp.get_buffers_by_client_id(client_id) @@ -1297,6 +1302,7 @@ function lsp._vim_exit_handler() local poll_time = 50 + ---@private local function check_clients_closed() for client_id, timeout in pairs(timeouts) do timeouts[client_id] = timeout - poll_time @@ -1331,8 +1337,8 @@ nvim_command("autocmd VimLeavePre * lua vim.lsp._vim_exit_handler()") ---@param method (string) LSP method name ---@param params (optional, table) Parameters to send to the server ---@param handler (optional, function) See |lsp-handler| --- If nil, follows resolution strategy defined in |lsp-handler-configuration| --- +--- If nil, follows resolution strategy defined in |lsp-handler-configuration| +--- ---@returns 2-tuple: --- - Map of client-id:request-id pairs for all successful requests. --- - Function which can be used to cancel all the requests. You could instead @@ -1683,7 +1689,17 @@ function lsp.get_log_path() return log.get_filename() end ---- Call {fn} for every client attached to {bufnr} +--- Invokes a function for each LSP client attached to a buffer. +--- +---@param bufnr number Buffer number +---@param fn function Function to run on each client attached to buffer +--- {bufnr}. The function takes the client, client ID, and +--- buffer number as arguments. Example: +--- <pre> +--- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) +--- print(vim.inspect(client)) +--- end) +--- </pre> function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) end @@ -1742,11 +1758,11 @@ end --- using `workspace/executeCommand`. --- --- The first argument to the function will be the `Command`: --- Command --- title: String --- command: String --- arguments?: any[] --- +--- Command +--- title: String +--- command: String +--- arguments?: any[] +--- --- The second argument is the `ctx` of |lsp-handler| lsp.commands = setmetatable({}, { __newindex = function(tbl, key, value) diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index be9bd9d223..f02ebfb9dc 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -165,7 +165,7 @@ end --- saved. {timeout_ms} is passed on to |vim.lsp.buf_request_sync()|. Example: --- --- <pre> ---- vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()]] +--- autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync() --- </pre> --- ---@param options Table with valid `FormattingOptions` entries @@ -263,6 +263,7 @@ function M.rename(new_name) request('textDocument/rename', params) end + ---@private local function prepare_rename(err, result) if err == nil and result == nil then vim.notify('nothing to rename', vim.log.levels.INFO) @@ -446,9 +447,9 @@ end --- by events such as `CursorHold`, eg: --- --- <pre> ---- vim.api.nvim_command [[autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()]] ---- vim.api.nvim_command [[autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight()]] ---- vim.api.nvim_command [[autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()]] +--- autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight() +--- autocmd CursorHoldI <buffer> lua vim.lsp.buf.document_highlight() +--- autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references() --- </pre> --- --- Note: Usage of |vim.lsp.buf.document_highlight()| requires the following highlight groups diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 3b214c49f9..075da41b23 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -222,7 +222,7 @@ function M.on_publish_diagnostics(_, result, ctx, config) vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) end ---- Clear diagnotics and diagnostic cache. +--- Clear diagnostics and diagnostic cache. --- --- Diagnostic producers should prefer |vim.diagnostic.reset()|. However, --- this method signature is still used internally in some parts of the LSP @@ -431,24 +431,6 @@ end --- Move to the next diagnostic --- ---@deprecated Prefer |vim.diagnostic.goto_next()| ---- ----@param opts table|nil Configuration table. Keys: ---- - {client_id}: (number) ---- - If nil, will consider all clients attached to buffer. ---- - {cursor_position}: (Position, default current position) ---- - See |nvim_win_get_cursor()| ---- - {wrap}: (boolean, default true) ---- - Whether to loop around file or not. Similar to 'wrapscan' ---- - {severity}: (DiagnosticSeverity) ---- - Exclusive severity to consider. Overrides {severity_limit} ---- - {severity_limit}: (DiagnosticSeverity) ---- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. ---- - {enable_popup}: (boolean, default true) ---- - Call |vim.lsp.diagnostic.show_line_diagnostics()| on jump ---- - {popup_opts}: (table) ---- - Table to pass as {opts} parameter to |vim.lsp.diagnostic.show_line_diagnostics()| ---- - {win_id}: (number, default 0) ---- - Window ID function M.goto_next(opts) if opts then if opts.severity then @@ -562,7 +544,7 @@ end --- Open a floating window with the diagnostics from {line_nr} --- ----@deprecated Prefer |vim.diagnostic.show_line_diagnostics()| +---@deprecated Prefer |vim.diagnostic.open_float()| --- ---@param opts table Configuration table --- - all opts for |vim.lsp.diagnostic.get_line_diagnostics()| and diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index fa4f2b22a5..22089aaaa6 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -28,7 +28,7 @@ local function progress_handler(_, result, ctx, _) local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") - return + return vim.NIL end local val = result.value -- unspecified yet local token = result.token -- string or number @@ -70,6 +70,7 @@ M['window/workDoneProgress/create'] = function(_, result, ctx) local client_name = client and client.name or string.format("id=%d", client_id) if not client then err_message("LSP[", client_name, "] client has shut down after sending the message") + return vim.NIL end client.messages.progress[token] = {} return vim.NIL diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 4597f1919a..dbc473b52c 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -101,6 +101,7 @@ function log.set_level(level) end --- Gets the current log level. +---@return string current log level function log.get_level() return current_log_level end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index ff8d7e0338..1fb75ddeb7 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -230,7 +230,7 @@ function default_dispatchers.on_error(code, err) end --- Starts an LSP server process and create an LSP RPC client object to ---- interact with it. +--- interact with it. Communication with the server is currently limited to stdio. --- ---@param cmd (string) Command to start the LSP server. ---@param cmd_args (table) List of additional string arguments to pass to {cmd}. @@ -329,7 +329,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) else msg = msg .. string.format(" with error message: %s", pid) end - vim.notify(msg, vim.log.levels.ERROR) + vim.notify(msg, vim.log.levels.WARN) return end end diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index f185e3973f..5df2a4d144 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -74,6 +74,7 @@ local function byte_to_utf(line, byte, offset_encoding) return utf_idx + 1 end +---@private local function compute_line_length(line, offset_encoding) local length local _ @@ -133,7 +134,7 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, -- occur on a new line pointed to by lastline. This occurs during insertion of -- new lines(O), the new newline is inserted at the line indicated by -- new_lastline. - -- If firstline == new_lastline, the first change occured on a line that was deleted. + -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the first byte change is also at the first byte of firstline if firstline == new_lastline or firstline == lastline then return { line_idx = firstline, byte_idx = 1, char_idx = 1 } @@ -187,7 +188,7 @@ end ---@param offset_encoding string ---@returns (int, int) end_line_idx and end_col_idx of range local function compute_end_range(prev_lines, curr_lines, start_range, firstline, lastline, new_lastline, offset_encoding) - -- If firstline == new_lastline, the first change occured on a line that was deleted. + -- If firstline == new_lastline, the first change occurred on a line that was deleted. -- In this case, the last_byte... if firstline == new_lastline then return { line_idx = (lastline - new_lastline + firstline), byte_idx = 1, char_idx = 1 }, { line_idx = firstline, byte_idx = 1, char_idx = 1 } diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index b88ddcd207..059e66c53a 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -158,6 +158,7 @@ end local function get_lines(bufnr, rows) rows = type(rows) == "table" and rows or { rows } + ---@private local function buf_lines() local lines = {} for _, row in pairs(rows) do @@ -262,6 +263,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) end --- Process and return progress reports from lsp server +---@private function M.get_progress_messages() local new_messages = {} @@ -557,7 +559,7 @@ local function remove_unmatch_completion_items(items, prefix) end, items) end ---- Acording to LSP spec, if the client set `completionItemKind.valueSet`, +--- According to LSP spec, if the client set `completionItemKind.valueSet`, --- the client must handle it properly even if it receives a value outside the --- specification. --- @@ -657,7 +659,7 @@ function M.rename(old_fname, new_fname, opts) api.nvim_buf_delete(oldbuf, { force = true }) end - +---@private local function create_file(change) local opts = change.options or {} -- from spec: Overwrite wins over `ignoreIfExists` @@ -669,7 +671,7 @@ local function create_file(change) vim.fn.bufadd(fname) end - +---@private local function delete_file(change) local opts = change.options or {} local fname = vim.uri_to_fname(change.uri) @@ -963,6 +965,8 @@ function M.jump_to_location(location) local row = range.start.line local col = get_line_byte_from_position(0, range.start) api.nvim_win_set_cursor(0, {row + 1, col}) + -- Open folds under the cursor + vim.cmd("normal! zv") return true end @@ -1137,7 +1141,7 @@ function M.stylize_markdown(bufnr, contents, opts) markdown_lines[#stripped] = true end else - -- strip any emty lines or separators prior to this separator in actual markdown + -- strip any empty lines or separators prior to this separator in actual markdown if line:match("^---+$") then while markdown_lines[#stripped] and (stripped[#stripped]:match("^%s*$") or stripped[#stripped]:match("^---+$")) do markdown_lines[#stripped] = false @@ -1170,7 +1174,7 @@ function M.stylize_markdown(bufnr, contents, opts) local idx = 1 ---@private - -- keep track of syntaxes we already inlcuded. + -- keep track of syntaxes we already included. -- no need to include the same syntax more than once local langs = {} local fences = get_markdown_fences() @@ -1306,18 +1310,21 @@ end --- ---@param contents table of lines to show in window ---@param syntax string of syntax to set for opened buffer ----@param opts dictionary with optional fields ---- - height of floating window ---- - width of floating window ---- - wrap boolean enable wrapping of long lines (defaults to true) ---- - wrap_at character to wrap at for computing height when wrap is enabled ---- - max_width maximal width of floating window ---- - max_height maximal height of floating window ---- - pad_top number of lines to pad contents at top ---- - pad_bottom number of lines to pad contents at bottom ---- - focus_id if a popup with this id is opened, then focus it ---- - close_events list of events that closes the floating window ---- - focusable (boolean, default true): Make float focusable +---@param opts table with optional fields (additional keys are passed on to |vim.api.nvim_open_win()|) +--- - height: (number) height of floating window +--- - width: (number) width of floating window +--- - wrap: (boolean, default true) wrap long lines +--- - wrap_at: (string) character to wrap at for computing height when wrap is enabled +--- - max_width: (number) maximal width of floating window +--- - max_height: (number) maximal height of floating window +--- - pad_top: (number) number of lines to pad contents at top +--- - pad_bottom: (number) number of lines to pad contents at bottom +--- - focus_id: (string) if a popup with this id is opened, then focus it +--- - close_events: (table) list of events that closes the floating window +--- - focusable: (boolean, default true) Make float focusable +--- - focus: (boolean, default true) If `true`, and if {focusable} +--- is also `true`, focus an existing floating window with the same +--- {focus_id} ---@returns bufnr,winnr buffer and window number of the newly created floating ---preview window function M.open_floating_preview(contents, syntax, opts) @@ -1329,12 +1336,13 @@ function M.open_floating_preview(contents, syntax, opts) opts = opts or {} opts.wrap = opts.wrap ~= false -- wrapping by default opts.stylize_markdown = opts.stylize_markdown ~= false + opts.focus = opts.focus ~= false opts.close_events = opts.close_events or {"CursorMoved", "CursorMovedI", "BufHidden", "InsertCharPre"} local bufnr = api.nvim_get_current_buf() -- check if this popup is focusable and we need to focus - if opts.focus_id and opts.focusable ~= false then + if opts.focus_id and opts.focusable ~= false and opts.focus then -- Go back to previous window if we are in a focusable one local current_winnr = api.nvim_get_current_win() if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then @@ -1414,7 +1422,7 @@ do --[[ References ]] --- Removes document highlights from a buffer. --- - ---@param bufnr buffer id + ---@param bufnr number Buffer id function M.buf_clear_references(bufnr) validate { bufnr = {bufnr, 'n', true} } api.nvim_buf_clear_namespace(bufnr, reference_ns, 0, -1) @@ -1422,9 +1430,9 @@ do --[[ References ]] --- Shows a list of document highlights for a certain buffer. --- - ---@param bufnr buffer id - ---@param references List of `DocumentHighlight` objects to highlight - ---@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 + ---@param bufnr number Buffer id + ---@param references table List of `DocumentHighlight` objects to highlight + ---@param offset_encoding string One of "utf-8", "utf-16", "utf-32", or nil. Defaults to utf-16 ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#documentHighlight function M.buf_highlight_references(bufnr, references, offset_encoding) validate { bufnr = {bufnr, 'n', true} } @@ -1542,7 +1550,7 @@ function M.set_qflist(items) }) end --- Acording to LSP spec, if the client set "symbolKind.valueSet", +-- According to LSP spec, if the client set "symbolKind.valueSet", -- the client must handle it properly even if it receives a value outside the specification. -- https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol function M._get_symbol_kind_name(symbol_kind) diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index bf77f0c776..1cf618725d 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -559,6 +559,7 @@ do return type(val) == t or (t == 'callable' and vim.is_callable(val)) end + ---@private local function is_valid(opt) if type(opt) ~= 'table' then return false, string.format('opt: expected table, got %s', type(opt)) diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 66999c5f7f..07f6418c0c 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -72,7 +72,7 @@ end --- Gets the parser for this bufnr / ft combination. --- --- If needed this will create the parser. ---- Unconditionnally attach the provided callback +--- Unconditionally attach the provided callback --- ---@param bufnr The buffer the parser should be tied to ---@param lang The filetype of this parser diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 89ddd6cd5a..6f347ff25f 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -38,7 +38,7 @@ end --- Inspects the provided language. --- ---- Inspecting provides some useful informations on the language like node names, ... +--- Inspecting provides some useful information on the language like node names, ... --- ---@param lang The language. function M.inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 7e392f72a4..594765761d 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -493,9 +493,9 @@ local function tree_contains(tree, range) return false end ---- Determines wether @param range is contained in this language tree +--- Determines whether @param range is contained in this language tree --- ---- This goes down the tree to recursively check childs. +--- This goes down the tree to recursively check children. --- ---@param range A range, that is a `{ start_line, start_col, end_line, end_col }` table. function LanguageTree:contains(range) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 66da179ea3..b8255e61ed 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -48,7 +48,7 @@ function M.get_query_files(lang, query_name, is_included) local base_langs = {} -- Now get the base languages by looking at the first line of every file - -- The syntax is the folowing : + -- The syntax is the following : -- ;+ inherits: ({language},)*{language} -- -- {language} ::= {lang} | ({lang}) @@ -446,7 +446,7 @@ end --- --- {source} is needed if the query contains predicates, then the caller --- must ensure to use a freshly parsed tree consistent with the current ---- text of the buffer (if relevent). {start_row} and {end_row} can be used to limit +--- text of the buffer (if relevant). {start_row} and {end_row} can be used to limit --- matches inside a row range (this is typically used with root node --- as the node, i e to get syntax highlight matches in the current --- viewport). When omitted the start and end row values are used from the given node. @@ -466,7 +466,7 @@ end --- </pre> --- ---@param node The node under which the search will occur ----@param source The source buffer or string to exctract text from +---@param source The source buffer or string to extract text from ---@param start The starting line of the search ---@param stop The stopping line of the search (end-exclusive) --- diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 099c9a57c0..225dd79878 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,7 @@ </screenshots> <releases> + <release date="2021-11-30" version="0.6.0"/> <release date="2021-07-02" version="0.5.0"/> <release date="2020-08-04" version="0.4.4"/> <release date="2019-11-06" version="0.4.3"/> diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index c5a9f503d5..67d91360d8 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2021 Nov 21 +" Last Change: 2021 Nov 29 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -102,6 +102,7 @@ endfunc call s:Highlight(1, '', &background) hi default debugBreakpoint term=reverse ctermbg=red guibg=red +hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray func s:StartDebug(bang, ...) " First argument is the command to debug, second core file or process ID. @@ -241,15 +242,29 @@ func s:StartDebug_term(dict) let comm_job_info = nvim_get_chan_info(s:comm_job_id) let commpty = comm_job_info['pty'] - " Open a terminal window to run the debugger. - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) - let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args - "call ch_log('executing "' . join(cmd) . '"') + let gdb_cmd = [g:termdebugger] + " 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'] + + " Adding arguments requested by the user + let gdb_cmd += gdb_args + execute 'new' - let s:gdb_job_id = termopen(cmd, {'on_exit': function('s:EndTermDebug')}) + " call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')}) if s:gdb_job_id == 0 echoerr 'invalid argument (or job table is full) while opening gdb terminal window' exe 'bwipe! ' . s:ptybuf @@ -272,8 +287,8 @@ func s:StartDebug_term(dict) for lnum in range(1, 200) if get(getbufline(s:gdbbuf, lnum), 0, '') =~ 'startupdone' - let try_count = 9999 - break + let try_count = 9999 + break endif endfor let try_count += 1 @@ -286,7 +301,7 @@ func s:StartDebug_term(dict) " Set arguments to be run. if len(proc_args) - call chansend(s:gdb_job_id, 'set args ' . join(proc_args) . "\r") + call chansend(s:gdb_job_id, 'server set args ' . join(proc_args) . "\r") endif " Connect gdb to the communication pty, using the GDB/MI interface. @@ -310,6 +325,7 @@ func s:StartDebug_term(dict) let response = line1 . line2 if response =~ 'Undefined command' 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 @@ -333,17 +349,6 @@ func s:StartDebug_term(dict) sleep 10m endwhile - " 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. - call s:SendCommand('-gdb-set mi-async on') - " Older gdb uses a different command. - call s:SendCommand('-gdb-set target-async on') - - " Disable pagination, it causes everything to stop at the gdb - " "Type <return> to continue" prompt. - call s:SendCommand('set pagination off') - " Set the filetype, this can be used to add mappings. set filetype=termdebug @@ -371,14 +376,26 @@ func s:StartDebug_prompt(dict) exe (&columns / 2 - 1) . "wincmd |" endif - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) - let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args - "call ch_log('executing "' . join(cmd) . '"') + let gdb_cmd = [g:termdebugger] + " 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 - let s:gdbjob = jobstart(cmd, { + " call ch_log('executing "' . join(gdb_cmd) . '"') + let s:gdbjob = jobstart(gdb_cmd, { \ 'on_exit': function('s:EndPromptDebug'), \ 'on_stdout': function('s:GdbOutCallback'), \ }) @@ -392,13 +409,6 @@ func s:StartDebug_prompt(dict) return endif - " 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. - call s:SendCommand('-gdb-set mi-async on') - " Older gdb uses a different command. - call s:SendCommand('-gdb-set target-async on') - let s:ptybuf = 0 if has('win32') " MS-Windows: run in a new console window for maximum compatibility @@ -433,8 +443,6 @@ func s:StartDebug_prompt(dict) endif call s:SendCommand('set print pretty on') call s:SendCommand('set breakpoint pending on') - " Disable pagination, it causes everything to stop at the gdb - call s:SendCommand('set pagination off') " Set arguments to be run if len(proc_args) @@ -477,7 +485,7 @@ func s:StartDebugCommon(dict) " Run the command if the bang attribute was given and got to the debug " window. if get(a:dict, 'bang', 0) - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') call win_gotoid(s:ptywin) endif endfunc @@ -516,6 +524,20 @@ func TermDebugSendCommand(cmd) 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) @@ -584,32 +606,23 @@ func s:GdbOutCallback(job_id, msgs, event) endfunc " Decode a message from gdb. quotedText starts with a ", return the text up -" to the next ", unescaping characters. +" to the next ", unescaping characters: +" - remove line breaks +" - change \\t to \t +" - change \0xhh to \xhh +" - change \ooo to octal +" - change \\ to \ func s:DecodeMessage(quotedText) if a:quotedText[0] != '"' echoerr 'DecodeMessage(): missing quote in ' . a:quotedText return endif - let result = '' - let i = 1 - while a:quotedText[i] != '"' && i < len(a:quotedText) - if a:quotedText[i] == '\' - let i += 1 - if a:quotedText[i] == 'n' - " drop \n - let i += 1 - continue - elseif a:quotedText[i] == 't' - " append \t - let i += 1 - let result .= "\t" - continue - endif - endif - let result .= a:quotedText[i] - let i += 1 - endwhile - return result + return a:quotedText + \->substitute('^"\|".*\|\\n', '', 'g') + \->substitute('\\t', "\t", 'g') + \->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + \->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') + \->substitute('\\\\', '\', 'g') endfunc " Extract the "name" value from a gdb message with fullname="name". @@ -770,8 +783,10 @@ func s:CommOutput(job_id, msgs, event) elseif msg != '' if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' call s:HandleCursor(msg) - elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' || msg =~ '^=breakpoint-modified,' - call s:HandleNewBreakpoint(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' @@ -805,11 +820,11 @@ func s:InstallCommands() command -nargs=? Break call s:SetBreakpoint(<q-args>) command Clear call s:ClearBreakpoint() - command Step call s:SendCommand('-exec-step') - command Over call s:SendCommand('-exec-next') - command Finish call s:SendCommand('-exec-finish') + command Step call s:SendResumingCommand('-exec-step') + command Over call s:SendResumingCommand('-exec-next') + command Finish call s:SendResumingCommand('-exec-finish') command -nargs=* Run call s:Run(<q-args>) - command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) + command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . <q-args>) if s:way == 'prompt' command Stop call s:PromptInterrupt() @@ -968,9 +983,9 @@ endfunc func s:Run(args) if a:args != '' - call s:SendCommand('-exec-arguments ' . a:args) + call s:SendResumingCommand('-exec-arguments ' . a:args) endif - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') endfunc func s:SendEval(expr) @@ -1024,21 +1039,24 @@ endfunc " clean up expression that may got 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') + let expr = substitute(a:expr, '\_s', ' ', 'g') if &filetype ==# 'cobol' - " extra cleanup for COBOL: _every: expression ends with a period, - " a semicolon nmay be used instead of a space - " a trailing comma is ignored as it commonly separates multiple expr - let expr = substitute(expr, '\..*', '', '') + " 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, ',*$', '', '') + let expr = substitute(expr, '[,.]\+ *$', '', '') endif - " get rid of surrounding spaces - let expr = substitute(expr, '^ *\(.*\) *', '\1', '') + " get rid of leading and trailing spaces + let expr = substitute(expr, '^ *', '', '') + let expr = substitute(expr, ' *$', '', '') return expr endfunc @@ -1050,6 +1068,8 @@ let s:evalFromBalloonExprResult = '' func s:HandleEvaluate(msg) let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') let value = substitute(value, '\\"', '"', 'g') + " multi-byte characters arrive in octal form + let value = substitute(value, '\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') let value = substitute(value, '
', '\1', '') if s:evalFromBalloonExpr if s:evalFromBalloonExprResult == '' @@ -1329,11 +1349,16 @@ endfunc let s:BreakpointSigns = [] -func s:CreateBreakpoint(id, subid) +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) - exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" + if a:enabled == "n" + let hiName = "debugBreakpointDisabled" + else + let hiName = "debugBreakpoint" + endif + exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=" . hiName endif endfunc @@ -1343,7 +1368,7 @@ endfunction " Handle setting a breakpoint " Will update the sign that shows the breakpoint -func s:HandleNewBreakpoint(msg) +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=' @@ -1366,7 +1391,8 @@ func s:HandleNewBreakpoint(msg) " 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') - call s:CreateBreakpoint(id, subid) + let enabled = substitute(msg, '.*enabled="\([yn]\)".*', '\1', '') + call s:CreateBreakpoint(id, subid, enabled) if has_key(s:breakpoints, id) let entries = s:breakpoints[id] @@ -1393,8 +1419,18 @@ func s:HandleNewBreakpoint(msg) 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 . ' created at line ' . lnum . '.' + echomsg 'Breakpoint ' . nr . ' ' . actionTaken . posMsg endfor endfunc diff --git a/runtime/scripts.vim b/runtime/scripts.vim index 0ff8e49088..3790b1c10f 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -198,6 +198,10 @@ if s:line1 =~# "^#!" elseif s:name =~# 'fish\>' set ft=fish + " Gforth + elseif s:name =~# 'gforth\>' + set ft=forth + endif unlet s:name diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index d3ca4de0e2..76b47d2e59 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Django template " Maintainer: Dave Hodder <dmh@dmh.org.uk> -" Last Change: 2014 Jul 13 +" Last Change: 2021 Nov 29 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -31,6 +31,7 @@ syn keyword djangoStatement contained closecomment widthratio url with endwith syn keyword djangoStatement contained get_current_language trans noop blocktrans syn keyword djangoStatement contained endblocktrans get_available_languages syn keyword djangoStatement contained get_current_language_bidi plural +syn keyword djangoStatement contained translate blocktranslate endblocktranslate " Django templete built-in filters syn keyword djangoFilter contained add addslashes capfirst center cut date diff --git a/runtime/syntax/squirrel.vim b/runtime/syntax/squirrel.vim new file mode 100644 index 0000000000..81d59cc986 --- /dev/null +++ b/runtime/syntax/squirrel.vim @@ -0,0 +1,50 @@ +" Vim syntax file +" Language: squirrel +" Current Maintainer: Matt Dunford (zenmatic@gmail.com) +" URL: https://github.com/zenmatic/vim-syntax-squirrel +" Last Change: 2021 Nov 28 + +" http://squirrel-lang.org/ + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +" inform C syntax that the file was included from cpp.vim +let b:filetype_in_cpp_family = 1 + +" Read the C syntax to start with +runtime! syntax/c.vim +unlet b:current_syntax + +" squirrel extensions +syn keyword squirrelStatement delete this in yield resume base clone +syn keyword squirrelAccess local +syn keyword cConstant null +syn keyword squirrelModifier static +syn keyword squirrelType bool instanceof typeof +syn keyword squirrelExceptions throw try catch +syn keyword squirrelStructure class function extends constructor +syn keyword squirrelBoolean true false +syn keyword squirrelRepeat foreach + +syn region squirrelMultiString start='@"' end='"$' end='";$'me=e-1 + +syn match squirrelShComment "^\s*#.*$" + +" Default highlighting +hi def link squirrelAccess squirrelStatement +hi def link squirrelExceptions Exception +hi def link squirrelStatement Statement +hi def link squirrelModifier Type +hi def link squirrelType Type +hi def link squirrelStructure Structure +hi def link squirrelBoolean Boolean +hi def link squirrelMultiString String +hi def link squirrelRepeat cRepeat +hi def link squirrelShComment Comment + +let b:current_syntax = "squirrel" + +" vim: ts=8 diff --git a/runtime/syntax/vb.vim b/runtime/syntax/vb.vim index 8ddb1efac3..607f6130ba 100644 --- a/runtime/syntax/vb.vim +++ b/runtime/syntax/vb.vim @@ -1,9 +1,11 @@ " Vim syntax file -" Language: Visual Basic -" Maintainer: Tim Chase <vb.vim@tim.thechases.com> -" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> -" (tried multiple times to contact, but email bounced) +" Language: Visual Basic +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Former Maintainer: Tim Chase <vb.vim@tim.thechases.com> +" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> +" (tried multiple times to contact, but email bounced) " Last Change: +" 2021 Nov 26 Incorporated additions from Doug Kearns " 2005 May 25 Synched with work by Thomas Barthel " 2004 May 30 Added a few keywords @@ -13,7 +15,7 @@ " quit when a syntax file was already loaded if exists("b:current_syntax") - finish + finish endif " VB is case insensitive @@ -233,7 +235,7 @@ syn keyword vbKeyword Public PublicNotCreateable OnNewProcessSingleUse syn keyword vbKeyword InSameProcessMultiUse GlobalMultiUse Resume Seek syn keyword vbKeyword Set Static Step String Time WithEvents -syn keyword vbTodo contained TODO +syn keyword vbTodo contained TODO "Datatypes syn keyword vbTypes Boolean Byte Currency Date Decimal Double Empty @@ -319,46 +321,54 @@ syn match vbNumber "\<\d\+\>" syn match vbNumber "\<\d\+\.\d*\>" "floating point number, starting with a dot syn match vbNumber "\.\d\+\>" -"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" -"syn match vbNumber ":[[:xdigit:]]\+" -"syn match vbNumber "[-+]\=\<\d\+\>" -syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" -syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" -syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" +"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" +"syn match vbNumber ":[[:xdigit:]]\+" +"syn match vbNumber "[-+]\=\<\d\+\>" +syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" +syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" +syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" -" String and Character contstants +" String and Character constants syn region vbString start=+"+ end=+"\|$+ syn region vbComment start="\(^\|\s\)REM\s" end="$" contains=vbTodo syn region vbComment start="\(^\|\s\)\'" end="$" contains=vbTodo -syn match vbLineNumber "^\d\+\(\s\|$\)" -syn match vbTypeSpecifier "[a-zA-Z0-9][\$%&!#]"ms=s+1 +syn match vbLineLabel "^\h\w\+:" +syn match vbLineNumber "^\d\+\(:\|\s\|$\)" +syn match vbTypeSpecifier "\<\a\w*[@\$%&!#]"ms=s+1 syn match vbTypeSpecifier "#[a-zA-Z0-9]"me=e-1 +" Conditional Compilation +syn match vbPreProc "^#const\>" +syn region vbPreProc matchgroup=PreProc start="^#if\>" end="\<then\>" transparent contains=TOP +syn region vbPreProc matchgroup=PreProc start="^#elseif\>" end="\<then\>" transparent contains=TOP +syn match vbPreProc "^#else\>" +syn match vbPreProc "^#end\s*if\>" " Define the default highlighting. " Only when an item doesn't have highlighting yet -hi def link vbBoolean Boolean -hi def link vbLineNumber Comment -hi def link vbComment Comment -hi def link vbConditional Conditional -hi def link vbConst Constant -hi def link vbDefine Constant -hi def link vbError Error -hi def link vbFunction Identifier -hi def link vbIdentifier Identifier -hi def link vbNumber Number -hi def link vbFloat Float -hi def link vbMethods PreProc -hi def link vbOperator Operator -hi def link vbRepeat Repeat -hi def link vbString String -hi def link vbStatement Statement -hi def link vbKeyword Statement -hi def link vbEvents Special -hi def link vbTodo Todo -hi def link vbTypes Type -hi def link vbTypeSpecifier Type - +hi def link vbBoolean Boolean +hi def link vbLineNumber Comment +hi def link vbLineLabel Comment +hi def link vbComment Comment +hi def link vbConditional Conditional +hi def link vbConst Constant +hi def link vbDefine Constant +hi def link vbError Error +hi def link vbFunction Identifier +hi def link vbIdentifier Identifier +hi def link vbNumber Number +hi def link vbFloat Float +hi def link vbMethods PreProc +hi def link vbOperator Operator +hi def link vbRepeat Repeat +hi def link vbString String +hi def link vbStatement Statement +hi def link vbKeyword Statement +hi def link vbEvents Special +hi def link vbTodo Todo +hi def link vbTypes Type +hi def link vbTypeSpecifier Type +hi def link vbPreProc PreProc let b:current_syntax = "vb" diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index f7b5ce0f63..41993b65b0 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -53,7 +53,7 @@ syn case ignore syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu +syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineFold CursorLineNr CursorLineSign DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu syn match vimHLGroup contained "Conceal" syn keyword vimOnlyHLGroup contained LineNrAbove LineNrBelow StatusLineTerm Terminal VisualNOS syn keyword nvimHLGroup contained Substitute TermCursor TermCursorNC @@ -741,10 +741,10 @@ if g:vimsyn_embed =~# 'P' && filereadable(s:pythonpath) unlet! b:current_syntax syn cluster vimFuncBodyList add=vimPythonRegion exe "syn include @vimPythonScript ".s:pythonpath - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\s*<<\s*\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript - VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\=\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\%(trim\s*\)\=\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon][3x]\=\s*<<\s*\%(trim\s*\)\=$+ end=+\.$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\s*<<\s*\%(trim\s*\)\=\z(\S*\)\ze\(\s*#.*\)\=$+ end=+^\z1\ze\(\s*".*\)\=$+ contains=@vimPythonScript + VimFoldP syn region vimPythonRegion matchgroup=vimScriptDelim start=+Py\%[thon]2or3\=\s*<<\s*\%(trim\s*\)\=$+ end=+\.$+ contains=@vimPythonScript syn cluster vimFuncBodyList add=vimPythonRegion else syn region vimEmbedError start=+py\%[thon]3\=\s*<<\s*\z(.*\)$+ end=+^\z1$+ diff --git a/runtime/tools/check_colors.vim b/runtime/tools/check_colors.vim index 966072c706..85df882d1e 100644 --- a/runtime/tools/check_colors.vim +++ b/runtime/tools/check_colors.vim @@ -226,7 +226,13 @@ fu! Result(err) endif endfu -call Test_check_colors() - -let &cpo = s:save_cpo -unlet s:save_cpo +try + call Test_check_colors() +catch + echohl ErrorMsg + echomsg v:exception + echohl NONE +finally + let &cpo = s:save_cpo + unlet s:save_cpo +endtry diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua index c30a1b10da..0a7da4d4ef 100644 --- a/scripts/lintcommit.lua +++ b/scripts/lintcommit.lua @@ -47,7 +47,7 @@ end local function validate_commit(commit_message) local commit_split = vim.split(commit_message, ":") - -- Return true if the type is vim-patch since most of the normal rules don't + -- Return nil if the type is vim-patch since most of the normal rules don't -- apply. if commit_split[1] == "vim-patch" then return nil diff --git a/src/mpack/object.h b/src/mpack/object.h index 5327e56e18..e69821f9de 100644 --- a/src/mpack/object.h +++ b/src/mpack/object.h @@ -22,7 +22,7 @@ enum { }; /* Storing integer in pointers in undefined behavior according to the C - * standard. Define a union type to accomodate arbitrary user data associated + * standard. Define a union type to accommodate arbitrary user data associated * with nodes(and with requests in rpc.h). */ typedef union { void *p; diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 4076a0d220..718743ed9c 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -535,7 +535,7 @@ end: /// @param channel_id /// @param buffer Buffer handle, or 0 for current buffer /// @param start_row First line index -/// @param start_column Last column +/// @param start_column First column /// @param end_row Last line index /// @param end_column Last column /// @param replacement Array of lines to use as replacement @@ -1246,7 +1246,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// If the current window already shows "buffer", the window is not switched /// If a window inside the current tabpage (including a float) already shows the /// buffer One of these windows will be set as current window temporarily. -/// Otherwise a temporary scratch window (calleed the "autocmd window" for +/// Otherwise a temporary scratch window (called the "autocmd window" for /// historical reasons) will be used. /// /// This is useful e.g. to call vimL functions that only work with the current diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 6f1fb15dac..6e25e627b9 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -839,7 +839,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// - on_win: called when starting to redraw a specific window. /// ["win", winid, bufnr, topline, botline_guess] /// - on_line: called for each buffer line being redrawn. (The -/// interation with fold lines is subject to change) +/// interaction with fold lines is subject to change) /// ["win", winid, bufnr, row] /// - on_end: called at the end of a redraw cycle /// ["end", tick] diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 144c252687..e956a54dbc 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -58,5 +58,8 @@ return { "highlights"; "use_tabline"; }; + option = { + "scope"; + }; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3103905819..36179b1fab 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -642,7 +642,7 @@ void nvim_set_vvar(String name, Object value, Error *err) dict_set_var(&vimvardict, name, value, false, false, err); } -/// Gets an option value string. +/// Gets the global value of an option. /// /// @param name Option name /// @param[out] err Error details, if any @@ -653,6 +653,115 @@ Object nvim_get_option(String name, Error *err) return get_option_from(NULL, SREQ_GLOBAL, name, err); } +/// Gets the value of an option. The behavior of this function matches that of +/// |:set|: the local value of an option is returned if it exists; otherwise, +/// the global value is returned. Local values always correspond to the current +/// buffer or window. To get a buffer-local or window-local option for a +/// specific buffer or window, use |nvim_buf_get_option()| or +/// |nvim_win_get_option()|. +/// +/// @param name Option name +/// @param opts Optional parameters +/// - scope: One of 'global' or 'local'. Analagous to +/// |:setglobal| and |:setlocal|, respectively. +/// @param[out] err Error details, if any +/// @return Option value +Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) + FUNC_API_SINCE(9) +{ + Object rv = OBJECT_INIT; + + int scope = 0; + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + goto end; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + goto end; + } + + long numval = 0; + char *stringval = NULL; + switch (get_option_value(name.data, &numval, (char_u **)&stringval, scope)) { + case 0: + rv = STRING_OBJ(cstr_as_string(stringval)); + break; + case 1: + rv = INTEGER_OBJ(numval); + break; + case 2: + rv = BOOLEAN_OBJ(!!numval); + break; + default: + api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data); + goto end; + } + +end: + return rv; +} + +/// Sets the value of an option. The behavior of this function matches that of +/// |:set|: for global-local options, both the global and local value are set +/// unless otherwise specified with {scope}. +/// +/// @param name Option name +/// @param value New option value +/// @param opts Optional parameters +/// - scope: One of 'global' or 'local'. Analagous to +/// |:setglobal| and |:setlocal|, respectively. +/// @param[out] err Error details, if any +void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error *err) + FUNC_API_SINCE(9) +{ + int scope = 0; + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + return; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + return; + } + + long numval = 0; + char *stringval = NULL; + + switch (value.type) { + case kObjectTypeInteger: + numval = value.data.integer; + break; + case kObjectTypeBoolean: + numval = value.data.boolean ? 1 : 0; + break; + case kObjectTypeString: + stringval = value.data.string.data; + break; + case kObjectTypeNil: + // Do nothing + break; + default: + api_set_error(err, kErrorTypeValidation, "invalid value for option"); + return; + } + + char *e = set_option_value(name.data, numval, stringval, scope); + if (e) { + api_set_error(err, kErrorTypeException, "%s", e); + } +} + /// Gets the option information for all options. /// /// The dictionary has the full option names as keys and option metadata @@ -694,7 +803,7 @@ Dictionary nvim_get_option_info(String name, Error *err) return get_vimoption(name, err); } -/// Sets an option value. +/// Sets the global value of an option. /// /// @param channel_id /// @param name Option name diff --git a/src/nvim/assert.h b/src/nvim/assert.h index ad92d9a2af..65519a8004 100644 --- a/src/nvim/assert.h +++ b/src/nvim/assert.h @@ -1,3 +1,5 @@ +// uncrustify:off + #ifndef NVIM_ASSERT_H #define NVIM_ASSERT_H diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index af519dcba9..a236b47027 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -8,6 +8,7 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/main.h" +#include "nvim/misc1.h" #include "nvim/os/os.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -25,13 +26,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached) } recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); assert(chanid < VARNUMBER_MAX); tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid); tv_dict_set_keys_readonly(dict); apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 7e7114b291..1daae85c5e 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -70,6 +70,7 @@ return { 'InsertLeave', -- just after leaving Insert mode 'InsertLeavePre', -- just before leaving Insert mode 'MenuPopup', -- just before popup menu is displayed + 'ModeChanged', -- after changing the mode 'OptionSet', -- after setting any option 'QuickFixCmdPost', -- after :make, :grep etc. 'QuickFixCmdPre', -- before :make, :grep etc. diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2d0c0f3fd5..490fe5a0ac 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -925,6 +925,13 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c return FAIL; } } + + // need to initialize last_mode for the first ModeChanged autocmd + if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) { + xfree(last_mode); + last_mode = get_mode(); + } + ap->cmds = NULL; *prev_ap = ap; last_autopat[(int)event] = ap; @@ -1440,7 +1447,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, // invalid. if (fname_io == NULL) { if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE - || event == EVENT_OPTIONSET) { + || event == EVENT_OPTIONSET || event == EVENT_MODECHANGED) { autocmd_fname = NULL; } else if (fname != NULL && !ends_excmd(*fname)) { autocmd_fname = fname; @@ -1494,11 +1501,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, || event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED || event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE || event == EVENT_DIRCHANGED || event == EVENT_FILETYPE - || event == EVENT_FUNCUNDEFINED || event == EVENT_OPTIONSET - || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE - || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING - || event == EVENT_SYNTAX || event == EVENT_SIGNAL - || event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) { + || event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED + || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST + || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY + || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX + || event == EVENT_SIGNAL || event == EVENT_TABCLOSED + || event == EVENT_WINCLOSED) { fname = vim_strsave(fname); } else { fname = (char_u *)FullName_save((char *)fname, false); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 49e527e98b..e53b2d1dfa 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1116,6 +1116,12 @@ typedef struct { pos_T w_cursor_corr; // corrected cursor position } pos_save_T; +// Struct passed to get_v_event() and restore_v_event(). +typedef struct { + bool sve_did_save; + hashtab_T sve_hashtab; +} save_v_event_T; + /// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode /// \addtogroup MENU_INDEX /// @{ diff --git a/src/nvim/change.c b/src/nvim/change.c index c52d992fbe..ef771125f1 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -625,7 +625,7 @@ void ins_char_bytes(char_u *buf, size_t charlen) } } - char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen)); + char_u *newp = xmalloc(linelen + newlen - oldlen); // Copy bytes before the cursor. if (col > 0) { diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 9662f6205f..a662f3a951 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -10,6 +10,7 @@ #include "nvim/event/socket.h" #include "nvim/fileio.h" #include "nvim/lua/executor.h" +#include "nvim/misc1.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/shell.h" @@ -821,7 +822,8 @@ static void set_info_event(void **argv) Channel *chan = argv[0]; event_T event = (event_T)(ptrdiff_t)argv[1]; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); Dictionary info = channel_info(chan->id); typval_T retval; (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); @@ -829,7 +831,7 @@ static void set_info_event(void **argv) apply_autocmds(event, NULL, NULL, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); api_free_dictionary(info); channel_decref(chan); } diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 47d89717e8..c4e5d9522b 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -14,8 +14,8 @@ /// @return Folded variant. #define CH_FOLD(c) \ utf_fold((sizeof(c) == sizeof(char)) \ - ?((int)(uint8_t)(c)) \ - :((int)(c))) + ? ((int)(uint8_t)(c)) \ + : ((int)(c))) /// Flags for vim_str2nr() typedef enum { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d505b40935..fe69da7fcb 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -385,6 +385,7 @@ static void insert_enter(InsertState *s) State = INSERT; } + trigger_modechanged(); stop_insert_mode = false; // Need to recompute the cursor position, it might move when the cursor is @@ -2048,6 +2049,8 @@ static void ins_ctrl_x(void) // CTRL-V look like CTRL-N ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; } + + trigger_modechanged(); } // Whether other than default completion has been selected. @@ -2660,6 +2663,7 @@ void set_completion(colnr_T startcol, list_T *list) show_pum(save_w_wrow, save_w_leftcol); } + trigger_modechanged(); ui_flush(); } @@ -2715,12 +2719,13 @@ static bool pum_enough_matches(void) static void trigger_complete_changed_event(int cur) { static bool recursive = false; + save_v_event_T save_v_event; if (recursive) { return; } - dict_T *v_event = get_vim_var_dict(VV_EVENT); + dict_T *v_event = get_v_event(&save_v_event); if (cur < 0) { tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc()); } else { @@ -2736,7 +2741,7 @@ static void trigger_complete_changed_event(int cur) textlock--; recursive = false; - tv_dict_clear(v_event); + restore_v_event(v_event, &save_v_event); } /// Show the popup menu for the list of matches. @@ -3838,6 +3843,8 @@ static bool ins_compl_prep(int c) ins_apply_autocmds(EVENT_COMPLETEDONE); } + trigger_modechanged(); + /* reset continue_* if we left expansion-mode, if we stay they'll be * (re)set properly in ins_complete() */ if (!vim_is_ctrl_x_key(c)) { @@ -3919,7 +3926,8 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag) /// Get the user-defined completion function name for completion 'type' -static char_u *get_complete_funcname(int type) { +static char_u *get_complete_funcname(int type) +{ switch (type) { case CTRL_X_FUNCTION: return curbuf->b_p_cfu; @@ -4586,6 +4594,8 @@ static int ins_compl_get_exp(pos_T *ini) compl_curr_match = compl_old_match; } } + trigger_modechanged(); + return i; } @@ -5231,7 +5241,7 @@ static int ins_complete(int c, bool enable_pum) funcname = get_complete_funcname(ctrl_x_mode); if (*funcname == NUL) { semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); + ? "completefunc" : "omnifunc"); // restore did_ai, so that adding comment leader works did_ai = save_did_ai; return FAIL; @@ -6564,7 +6574,7 @@ static void spell_back_to_badword(void) int stop_arrow(void) { if (arrow_used) { - Insstart = curwin->w_cursor; //new insertion starts here + Insstart = curwin->w_cursor; // new insertion starts here if (Insstart.col > Insstart_orig.col && !ins_need_undo) { // Don't update the original insert position when moved to the // right, except when nothing was inserted yet. @@ -7630,16 +7640,34 @@ int hkmap(int c) KAFsofit, hKAF, LAMED, MEMsofit, MEM, NUNsofit, NUN, SAMEH, AIN, PEIsofit, PEI, ZADIsofit, ZADI, KOF, RESH, hSHIN, TAV, }; - static char_u map[26] = - { (char_u)hALEF /*a*/, (char_u)BET /*b*/, (char_u)hKAF /*c*/, - (char_u)DALET /*d*/, (char_u)-1 /*e*/, (char_u)PEIsofit /*f*/, - (char_u)GIMEL /*g*/, (char_u)HEI /*h*/, (char_u)IUD /*i*/, - (char_u)HET /*j*/, (char_u)KOF /*k*/, (char_u)LAMED /*l*/, - (char_u)MEM /*m*/, (char_u)NUN /*n*/, (char_u)SAMEH /*o*/, - (char_u)PEI /*p*/, (char_u)-1 /*q*/, (char_u)RESH /*r*/, - (char_u)ZAIN /*s*/, (char_u)TAV /*t*/, (char_u)TET /*u*/, - (char_u)VAV /*v*/, (char_u)hSHIN /*w*/, (char_u)-1 /*x*/, - (char_u)AIN /*y*/, (char_u)ZADI /*z*/ }; + static char_u map[26] = { + (char_u)hALEF, // a + (char_u)BET, // b + (char_u)hKAF, // c + (char_u)DALET, // d + (char_u)-1, // e + (char_u)PEIsofit, // f + (char_u)GIMEL, // g + (char_u)HEI, // h + (char_u)IUD, // i + (char_u)HET, // j + (char_u)KOF, // k + (char_u)LAMED, // l + (char_u)MEM, // m + (char_u)NUN, // n + (char_u)SAMEH, // o + (char_u)PEI, // p + (char_u)-1, // q + (char_u)RESH, // r + (char_u)ZAIN, // s + (char_u)TAV, // t + (char_u)TET, // u + (char_u)VAV, // v + (char_u)hSHIN, // w + (char_u)-1, // x + (char_u)AIN, // y + (char_u)ZADI, // z + }; if (c == 'N' || c == 'M' || c == 'P' || c == 'C' || c == 'Z') { return (int)(map[CharOrd(c)] - 1 + p_aleph); @@ -7965,6 +7993,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = NORMAL; + trigger_modechanged(); // need to position cursor again (e.g. when on a TAB ) changed_cline_bef_curs(); @@ -8066,6 +8095,7 @@ static void ins_insert(int replaceState) } else { State = replaceState | (State & LANGMAP); } + trigger_modechanged(); AppendCharToRedobuff(K_INS); showmode(); ui_cursor_shape(); // may show different cursor shape diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d3d0121632..85e81ee975 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -134,13 +134,15 @@ typedef struct { .vv_flags = flags, \ } +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -150,100 +152,103 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), - VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), - VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), - VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), - VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), - VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), - VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), - VV(VV_FNAME, "fname", VAR_STRING, VV_RO), - VV(VV_LANG, "lang", VAR_STRING, VV_RO), - VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), - VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), - VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), - VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), - VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), - VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), - VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), - VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), - VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), - VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), - VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), - VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), - VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), - VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), - VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_REG, "register", VAR_STRING, VV_RO), - VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), - VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), - VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), - VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), - VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), - VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), - VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), - VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), - VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), - VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), - VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), - VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), - VV(VV_CHAR, "char", VAR_STRING, 0), - VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), - VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), - VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), - VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), - VV(VV_OP, "operator", VAR_STRING, VV_RO), - VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), - VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), - VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), - VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), - VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), - VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), - VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), - VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_FALSE, "false", VAR_BOOL, VV_RO), - VV(VV_TRUE, "true", VAR_BOOL, VV_RO), - VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), - VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), - VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), - VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), - VV(VV_TESTING, "testing", VAR_NUMBER, 0), - VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), - VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), - VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), - VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), - VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), - VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), - VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), // Neovim - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), - VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), - VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -344,7 +349,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= 16); + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -4523,7 +4528,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) * dict.name */ key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) { + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { } if (len == 0) { return FAIL; @@ -4828,7 +4833,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval } else if (opt_type == -1) { // hidden number option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; - } else if (opt_type == 1) { // number option + } else if (opt_type == 1 || opt_type == 2) { // number or boolean option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = numval; } else { // string option @@ -6603,8 +6608,9 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL - && (is_funcref ? find_func(trans_name) == NULL - : !translated_function_exists((const char *)trans_name))) { + && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -7146,30 +7152,30 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha typval_T tv_item = { .v_lock = VAR_UNLOCKED }; switch (what) { - case kDictListKeys: - tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = vim_strsave(di->di_key); - break; - case kDictListValues: - tv_copy(&di->di_tv, &tv_item); - break; - case kDictListItems: { - // items() - list_T *const sub_l = tv_list_alloc(2); - tv_item.v_type = VAR_LIST; - tv_item.vval.v_list = sub_l; - tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), }); - tv_list_append_tv(sub_l, &di->di_tv); + tv_list_append_tv(sub_l, &di->di_tv); - break; - } + break; + } } tv_list_append_owned_tv(rettv->vval.v_list, tv_item); @@ -9649,8 +9655,8 @@ bool var_check_func_name(const char *const name, const bool new_var) { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] - : name[0])) { + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { semsg(_("E704: Funcref variable name must start with a capital: %s"), name); return false; } @@ -10446,11 +10452,15 @@ void option_last_set_msg(LastSet last_set) } } -// reset v:option_new, v:option_old and v:option_type +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. void reset_v_option_vars(void) { - set_vim_var_string(VV_OPTION_NEW, NULL, -1); - set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } diff --git a/src/nvim/eval.h b/src/nvim/eval.h index d34348a274..3b3a68bd29 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -138,6 +138,9 @@ typedef enum { VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, + VV_OPTION_OLDLOCAL, + VV_OPTION_OLDGLOBAL, + VV_OPTION_COMMAND, VV_OPTION_TYPE, VV_ERRORS, VV_FALSE, @@ -209,8 +212,8 @@ typedef enum { /// flags for find_name_end() #define FNE_INCL_BR 1 // find_name_end(): include [] in name -#define FNE_CHECK_START 2 /* find_name_end(): check name starts with - valid character */ +#define FNE_CHECK_START 2 // find_name_end(): check name starts with + // valid character typedef struct { TimeWatcher tw; diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 5008945f09..797420c150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -726,11 +726,11 @@ json_decode_string_cycle_start: semsg(_("E474: Using comma in place of colon: %.*s"), LENP(p, e)); goto json_decode_string_fail; } else if (last_container.special_val == NULL - ? (last_container.container.v_type == VAR_DICT - ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (tv_list_len(last_container.container.vval.v_list) - == 0)) - : (tv_list_len(last_container.special_val) == 0)) { + ? (last_container.container.v_type == VAR_DICT + ? (DICT_LEN(last_container.container.vval.v_dict) == 0) + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { semsg(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index d694b8a374..6f4357421b 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -352,20 +352,20 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const float_T flt_ = (flt); \ switch (xfpclassify(flt_)) { \ case FP_NAN: { \ - ga_concat(gap, "str2float('nan')"); \ - break; \ + ga_concat(gap, "str2float('nan')"); \ + break; \ } \ case FP_INFINITE: { \ - if (flt_ < 0) { \ - ga_append(gap, '-'); \ - } \ - ga_concat(gap, "str2float('inf')"); \ - break; \ + if (flt_ < 0) { \ + ga_append(gap, '-'); \ + } \ + ga_concat(gap, "str2float('inf')"); \ + break; \ } \ default: { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, numbuf); \ } \ } \ } while (0) @@ -556,18 +556,18 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s const float_T flt_ = (flt); \ switch (xfpclassify(flt_)) { \ case FP_NAN: { \ - emsg(_("E474: Unable to represent NaN value in JSON")); \ - return FAIL; \ + emsg(_("E474: Unable to represent NaN value in JSON")); \ + return FAIL; \ } \ case FP_INFINITE: { \ - emsg(_("E474: Unable to represent infinity in JSON")); \ - return FAIL; \ + emsg(_("E474: Unable to represent infinity in JSON")); \ + return FAIL; \ } \ default: { \ - char numbuf[NUMBUFLEN]; \ - vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ - ga_concat(gap, numbuf); \ - break; \ + char numbuf[NUMBUFLEN]; \ + vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \ + ga_concat(gap, numbuf); \ + break; \ } \ } \ } while (0) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 77944851d2..f95fe84f69 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -305,9 +305,8 @@ void ex_align(exarg_T *eap) */ do { (void)set_indent(++new_indent, 0); - } - while (linelen(NULL) <= width); - --new_indent; + } while (linelen(NULL) <= width); + new_indent--; break; } --new_indent; @@ -1457,7 +1456,7 @@ error: filterend: if (curbuf != old_curbuf) { - --no_wait_return; + no_wait_return--; emsg(_("E135: *Filter* Autocommands must not change current buffer")); } if (itmp != NULL) { @@ -2094,7 +2093,7 @@ void do_wqall(exarg_T *eap) } if (buf->b_ffname == NULL) { semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum); - ++error; + error++; } else if (check_readonly(&eap->forceit, buf) || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, FALSE) == FAIL) { @@ -2120,17 +2119,15 @@ void do_wqall(exarg_T *eap) } } -/* - * Check the 'write' option. - * Return TRUE and give a message when it's not st. - */ -int not_writing(void) +// Check the 'write' option. +// Return true and give a message when it's not st. +bool not_writing(void) { if (p_write) { - return FALSE; + return false; } emsg(_("E142: File not written: Writing is disabled by 'write' option")); - return TRUE; + return true; } /* @@ -2945,7 +2942,7 @@ void ex_append(exarg_T *eap) } for (;;) { - msg_scroll = TRUE; + msg_scroll = true; need_wait_return = false; if (curbuf->b_p_ai) { if (append_indent >= 0) { @@ -3131,7 +3128,7 @@ void ex_z(exarg_T *eap) // the number of '-' and '+' multiplies the distance if (*kind == '-' || *kind == '+') { - for (x = kind + 1; *x == *kind; ++x) { + for (x = kind + 1; *x == *kind; x++) { } } @@ -3214,26 +3211,24 @@ void ex_z(exarg_T *eap) ex_no_reprint = true; } -/* - * Check if the secure flag is set (.exrc or .vimrc in current directory). - * If so, give an error message and return TRUE. - * Otherwise, return FALSE. - */ -int check_secure(void) +// Check if the secure flag is set (.exrc or .vimrc in current directory). +// If so, give an error message and return true. +// Otherwise, return false. +bool check_secure(void) { if (secure) { secure = 2; emsg(_(e_curdir)); - return TRUE; + return true; } // In the sandbox more things are not allowed, including the things // disallowed in secure mode. if (sandbox != 0) { emsg(_(e_sandbox)); - return TRUE; + return true; } - return FALSE; + return false; } /// Previous substitute replacement string diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 62919c98f7..77cd50ecb7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -196,6 +196,7 @@ void do_exmode(void) exmode_active = true; State = NORMAL; + trigger_modechanged(); // When using ":global /pat/ visual" and then "Q" we return to continue // the :global command. @@ -434,7 +435,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) && cstack.cs_idx < 0 && !(getline_is_func && func_has_abort(real_cookie))) { - did_emsg = FALSE; + did_emsg = false; } /* @@ -818,7 +819,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // of interrupts or errors to exceptions, and ensure that no more // commands are executed. if (current_exception) { - void *p = NULL; + char *p = NULL; char_u *saved_sourcing_name; int saved_sourcing_lnum; struct msglist *messages = NULL; @@ -835,7 +836,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) vim_snprintf((char *)IObuff, IOSIZE, _("E605: Exception not caught: %s"), current_exception->value); - p = vim_strsave(IObuff); + p = (char *)vim_strsave(IObuff); break; case ET_ERROR: messages = current_exception->messages; @@ -2716,7 +2717,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * */ gap = &curbuf->b_ucmds; for (;;) { - for (j = 0; j < gap->ga_len; ++j) { + for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); cp = eap->cmd; np = uc->uc_name; @@ -5327,7 +5328,7 @@ static void uc_list(char_u *name, size_t name_len) ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; for (;;) { - for (i = 0; i < gap->ga_len; ++i) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); a = cmd->uc_argt; @@ -5714,7 +5715,7 @@ static void ex_delcommand(exarg_T *eap) gap = &curbuf->b_ucmds; for (;;) { - for (i = 0; i < gap->ga_len; ++i) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); cmp = STRCMP(eap->arg, cmd->uc_name); if (cmp <= 0) { @@ -8337,9 +8338,7 @@ static void ex_redir(exarg_T *eap) if (var_redir_start(skipwhite(arg), append) == OK) { redir_vname = 1; } - } - // TODO: redirect to a buffer - else { + } else { // TODO(vim): redirect to a buffer semsg(_(e_invarg2), eap->arg); } } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index f60f0ebe98..b1c59a607c 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -437,7 +437,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int } } } else { - *should_free = FALSE; + *should_free = false; ret = value; } @@ -888,11 +888,10 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int skip; int result; cstack_T *const cstack = eap->cstack; - skip = CHECK_SKIP; + bool skip = CHECK_SKIP; if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] @@ -902,14 +901,14 @@ void ex_else(exarg_T *eap) return; } eap->errmsg = N_("E582: :elseif without :if"); - skip = TRUE; + skip = true; } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { if (eap->cmdidx == CMD_else) { eap->errmsg = N_("E583: multiple :else"); return; } eap->errmsg = N_("E584: :elseif after :else"); - skip = TRUE; + skip = true; } // if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it @@ -917,7 +916,7 @@ void ex_else(exarg_T *eap) if (eap->errmsg == NULL) { cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; } - skip = TRUE; // don't evaluate an ":elseif" + skip = true; // don't evaluate an ":elseif" } else { cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE; } @@ -932,7 +931,7 @@ void ex_else(exarg_T *eap) // later on. if (!skip && dbg_check_skipped(eap) && got_int) { (void)do_intthrow(cstack); - skip = TRUE; + skip = true; } if (eap->cmdidx == CMD_elseif) { @@ -1322,9 +1321,9 @@ void ex_try(exarg_T *eap) void ex_catch(exarg_T *eap) { int idx = 0; - int give_up = FALSE; - int skip = FALSE; - int caught = FALSE; + bool give_up = false; + bool skip = false; + bool caught = false; char_u *end; char_u save_char = 0; char_u *save_cpo; @@ -1335,13 +1334,13 @@ void ex_catch(exarg_T *eap) if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = N_("E603: :catch without :try"); - give_up = TRUE; + give_up = true; } else { if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { // Report what's missing if the matching ":try" is not in its // finally clause. eap->errmsg = get_end_emsg(cstack); - skip = TRUE; + skip = true; } for (idx = cstack->cs_idx; idx > 0; --idx) { if (cstack->cs_flags[idx] & CSF_TRY) { @@ -1352,7 +1351,7 @@ void ex_catch(exarg_T *eap) // Give up for a ":catch" after ":finally" and ignore it. // Just parse. eap->errmsg = N_("E604: :catch after :finally"); - give_up = TRUE; + give_up = true; } else { rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); @@ -1425,7 +1424,7 @@ void ex_catch(exarg_T *eap) // CTRL-C while matching should abort it. // prev_got_int = got_int; - got_int = FALSE; + got_int = false; caught = vim_regexec_nl(®match, (char_u *)current_exception->value, (colnr_T)0); got_int |= prev_got_int; @@ -1602,8 +1601,7 @@ void ex_finally(exarg_T *eap) void ex_endtry(exarg_T *eap) { int idx; - int skip; - int rethrow = FALSE; + bool rethrow = false; int pending = CSTP_NONE; void *rettv = NULL; cstack_T *const cstack = eap->cstack; @@ -1621,20 +1619,19 @@ void ex_endtry(exarg_T *eap) // made inactive by a ":continue", ":break", ":return", or ":finish" in // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. - skip = (did_emsg || got_int || current_exception - || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); + bool skip = did_emsg || got_int || current_exception + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); // Find the matching ":try" and report what's missing. idx = cstack->cs_idx; do { - --idx; - } - while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); + idx--; + } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); - skip = TRUE; + skip = true; /* * If an exception is being thrown, discard it to prevent it from @@ -1677,7 +1674,7 @@ void ex_endtry(exarg_T *eap) // before the ":endtry". That is, throw an interrupt exception and // set "skip" and "rethrow". if (got_int) { - skip = TRUE; + skip = true; (void)do_intthrow(cstack); // The do_intthrow() call may have reset current_exception or // cstack->cs_pending[idx]. diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 25b30b2805..15da4b2d60 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -17,8 +17,8 @@ #define CSF_THROWN 0x0400 // exception thrown to this try conditional #define CSF_CAUGHT 0x0800 // exception caught by this try conditional #define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" -/* Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset - * (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. */ +// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset +// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. /* * What's pending for being reactivated at the ":endtry" of this try diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3b5953ae69..475f22d061 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -880,7 +880,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) TryState tstate; Error err = ERROR_INIT; bool tl_ret = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(firstc > 0 ? firstc : '-'); firstcbuf[1] = 0; @@ -894,7 +895,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); tl_ret = try_leave(&tstate, &err); @@ -906,6 +907,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } tl_ret = true; } + trigger_modechanged(); state_enter(&s->state); @@ -924,7 +926,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (tv_dict_get_number(dict, "abort") != 0) { s->gotesc = 1; } - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } cmdmsg_rl = false; @@ -2289,7 +2291,8 @@ static int command_line_changed(CommandLineState *s) if (has_event(EVENT_CMDLINECHANGED)) { TryState tstate; Error err = ERROR_INIT; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char firstcbuf[2]; firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-'); @@ -2303,7 +2306,7 @@ static int command_line_changed(CommandLineState *s) apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf, (char_u *)firstcbuf, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); bool tl_ret = try_leave(&tstate, &err); if (!tl_ret && ERROR_SET(&err)) { @@ -2488,27 +2491,25 @@ char *get_text_locked_msg(void) } /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and -/// return TRUE when it is and give an error message. -int curbuf_locked(void) +/// return true when it is and give an error message. +bool curbuf_locked(void) { if (curbuf->b_ro_locked > 0) { emsg(_("E788: Not allowed to edit another buffer now")); - return TRUE; + return true; } return allbuf_locked(); } -/* - * Check if "allbuf_lock" is set and return TRUE when it is and give an error - * message. - */ -int allbuf_locked(void) +// Check if "allbuf_lock" is set and return true when it is and give an error +// message. +bool allbuf_locked(void) { if (allbuf_lock > 0) { emsg(_("E811: Not allowed to change buffer information now")); - return TRUE; + return true; } - return FALSE; + return false; } static int cmdline_charsize(int idx) @@ -6547,6 +6548,7 @@ static int open_cmdwin(void) cmdmsg_rl = save_cmdmsg_rl; State = save_State; + trigger_modechanged(); setmouse(); return cmdwin_result; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 3a9acbd61c..5953a574f3 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1603,7 +1603,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) recursive = true; - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); char buf[8]; switch (scope) { @@ -1648,7 +1649,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, curbuf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 1b39a7410a..24428c2d9a 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -100,8 +100,8 @@ struct bw_info { char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] int bw_first; // first write call - char_u *bw_conv_buf; // buffer for writing converted chars - int bw_conv_buflen; // size of bw_conv_buf + char_u *bw_conv_buf; // buffer for writing converted chars + size_t bw_conv_buflen; // size of bw_conv_buf int bw_conv_error; // set for conversion error linenr_T bw_conv_error_lnum; // first line with error or zero linenr_T bw_start_lnum; // line number at start of buffer @@ -1851,7 +1851,7 @@ failed: msg_scrolled_ign = true; if (!read_stdin && !read_buffer) { - p = (char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0); + p = (char_u *)msg_trunc_attr((char *)IObuff, false, 0); } if (read_stdin || read_buffer || restart_edit != 0 diff --git a/src/nvim/fold.h b/src/nvim/fold.h index a96ea8a039..6b29214760 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -14,10 +14,10 @@ */ typedef struct foldinfo { linenr_T fi_lnum; // line number where fold starts - int fi_level; /* level of the fold; when this is zero the - other fields are invalid */ - int fi_low_level; /* lowest fold level that starts in the same - line */ + int fi_level; // level of the fold; when this is zero the + // other fields are invalid + int fi_low_level; // lowest fold level that starts in the same + // line long fi_lines; } foldinfo_T; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 2f4b59837a..27e8cb36af 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1680,6 +1680,325 @@ int char_avail(void) return retval != NUL; } +typedef enum { + map_result_fail, // failed, break loop + map_result_get, // get a character from typeahead + map_result_retry, // try to map again + map_result_nomatch // no matching mapping, get char +} map_result_T; + +/// Handle mappings in the typeahead buffer. +/// - When something was mapped, return map_result_retry for recursive mappings. +/// - When nothing mapped and typeahead has a character: return map_result_get. +/// - When there is no match yet, return map_result_nomatch, need to get more +/// typeahead. +static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) +{ + mapblock_T *mp = NULL; + mapblock_T *mp2; + mapblock_T *mp_match; + int mp_match_len = 0; + int max_mlen = 0; + int tb_c1; + int mlen; + int nolmaplen; + int keylen = *keylenp; + int i; + int local_State = get_real_state(); + + // Check for a mappable key sequence. + // Walk through one maphash[] list until we find an entry that matches. + // + // Don't look for mappings if: + // - no_mapping set: mapping disabled (e.g. for CTRL-V) + // - maphash_valid not set: no mappings present. + // - typebuf.tb_buf[typebuf.tb_off] should not be remapped + // - in insert or cmdline mode and 'paste' option set + // - waiting for "hit return to continue" and CR or SPACE typed + // - waiting for a char with --more-- + // - in Ctrl-X mode, and we get a valid char for that mode + tb_c1 = typebuf.tb_buf[typebuf.tb_off]; + if (no_mapping == 0 && maphash_valid + && (no_zero_mapping == 0 || tb_c1 != '0') + && (typebuf.tb_maplen == 0 + || (p_remap + && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)))) + && !(p_paste && (State & (INSERT + CMDLINE))) + && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' ')) + && State != ASKMORE + && State != CONFIRM + && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1)) + || ((compl_cont_status & CONT_LOCAL) + && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) { + if (tb_c1 == K_SPECIAL) { + nolmaplen = 2; + } else { + LANGMAP_ADJUST(tb_c1, !(State & (CMDLINE | INSERT)) && get_real_state() != SELECTMODE); + nolmaplen = 0; + } + // First try buffer-local mappings. + mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)]; + mp2 = maphash[MAP_HASH(local_State, tb_c1)]; + if (mp == NULL) { + // There are no buffer-local mappings. + mp = mp2; + mp2 = NULL; + } + // Loop until a partly matching mapping is found or all (local) + // mappings have been checked. + // The longest full match is remembered in "mp_match". + // A full match is only accepted if there is no partly match, so "aa" + // and "aaa" can both be mapped. + mp_match = NULL; + mp_match_len = 0; + for (; mp != NULL; mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next)) { + // Only consider an entry if the first character matches and it is + // for the current state. + // Skip ":lmap" mappings if keys were mapped. + if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) + && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { + int nomap = nolmaplen; + int c2; + // find the match length of this mapping + for (mlen = 1; mlen < typebuf.tb_len; mlen++) { + c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; + if (nomap > 0) { + nomap--; + } else if (c2 == K_SPECIAL) { + nomap = 2; + } else { + LANGMAP_ADJUST(c2, true); + } + if (mp->m_keys[mlen] != c2) { + break; + } + } + + // Don't allow mapping the first byte(s) of a multi-byte char. + // Happens when mapping <M-a> and then changing 'encoding'. + // Beware that 0x80 is escaped. + char_u *p1 = mp->m_keys; + char_u *p2 = (char_u *)mb_unescape((const char **)&p1); + + if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) { + mlen = 0; + } + + // Check an entry whether it matches. + // - Full match: mlen == keylen + // - Partly match: mlen == typebuf.tb_len + keylen = mp->m_keylen; + if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) { + char_u *s; + int n; + + // If only script-local mappings are allowed, check if the + // mapping starts with K_SNR. + s = typebuf.tb_noremap + typebuf.tb_off; + if (*s == RM_SCRIPT + && (mp->m_keys[0] != K_SPECIAL + || mp->m_keys[1] != KS_EXTRA + || mp->m_keys[2] != KE_SNR)) { + continue; + } + + // If one of the typed keys cannot be remapped, skip the entry. + for (n = mlen; --n >= 0;) { + if (*s++ & (RM_NONE|RM_ABBR)) { + break; + } + } + if (n >= 0) { + continue; + } + + if (keylen > typebuf.tb_len) { + if (!*timedout && !(mp_match != NULL && mp_match->m_nowait)) { + // break at a partly match + keylen = KEYLEN_PART_MAP; + break; + } + } else if (keylen > mp_match_len + || (keylen == mp_match_len + && mp_match != NULL + && (mp_match->m_mode & LANGMAP) == 0 + && (mp->m_mode & LANGMAP) != 0)) { + // found a longer match + mp_match = mp; + mp_match_len = keylen; + } + } else { + // No match; may have to check for termcode at next character. + if (max_mlen < mlen) { + max_mlen = mlen; + } + } + } + } + + // If no partly match found, use the longest full match. + if (keylen != KEYLEN_PART_MAP) { + mp = mp_match; + keylen = mp_match_len; + } + } + + // Check for match with 'pastetoggle' + if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { + bool match = typebuf_match_len(p_pt, &mlen); + if (match) { + // write chars to script file(s) + if (mlen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(mlen - typebuf.tb_maplen)); + } + + del_typebuf(mlen, 0); // remove the chars + set_option_value("paste", !p_paste, NULL, 0); + if (!(State & INSERT)) { + msg_col = 0; + msg_row = Rows - 1; + msg_clr_eos(); // clear ruler + } + status_redraw_all(); + redraw_statuslines(); + showmode(); + setcursor(); + *keylenp = keylen; + return map_result_retry; + } + // Need more chars for partly match. + if (mlen == typebuf.tb_len) { + keylen = KEYLEN_PART_KEY; + } else if (max_mlen < mlen) { + // no match, may have to check for termcode at next character + max_mlen = mlen + 1; + } + } + + if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { + // No matching mapping found or found a non-matching mapping that + // matches at least what the matching mapping matched + keylen = 0; + (void)keylen; // suppress clang/dead assignment + // If there was no mapping, use the character from the typeahead + // buffer right here. Otherwise, use the mapping (loop around). + if (mp == NULL) { + *keylenp = keylen; + return map_result_get; // got character, break for loop + } else { + keylen = mp_match_len; + } + } + + // complete match + if (keylen >= 0 && keylen <= typebuf.tb_len) { + char_u *map_str; + int save_m_expr; + int save_m_noremap; + int save_m_silent; + + // Write chars to script file(s). + // Note: :lmap mappings are written *after* being applied. #5658 + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(keylen - typebuf.tb_maplen)); + } + + cmd_silent = (typebuf.tb_silent > 0); + del_typebuf(keylen, 0); // remove the mapped keys + + // Put the replacement string in front of mapstr. + // The depth check catches ":map x y" and ":map y x". + if (++*mapdepth >= p_mmd) { + emsg(_("E223: recursive mapping")); + if (State & CMDLINE) { + redrawcmdline(); + } else { + setcursor(); + } + flush_buffers(FLUSH_MINIMAL); + *mapdepth = 0; // for next one + *keylenp = keylen; + return map_result_fail; + } + + // In Select mode and a Visual mode mapping is used: Switch to Visual + // mode temporarily. Append K_SELECT to switch back to Select mode. + if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) { + VIsual_select = false; + (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); + } + + // Copy the values from *mp that are used, because evaluating the + // expression may invoke a function that redefines the mapping, thereby + // making *mp invalid. + save_m_expr = mp->m_expr; + save_m_noremap = mp->m_noremap; + save_m_silent = mp->m_silent; + char_u *save_m_keys = NULL; // only saved when needed + char_u *save_m_str = NULL; // only saved when needed + + // Handle ":map <expr>": evaluate the {rhs} as an + // expression. Also save and restore the command line + // for "normal :". + if (mp->m_expr) { + int save_vgetc_busy = vgetc_busy; + const bool save_may_garbage_collect = may_garbage_collect; + + vgetc_busy = 0; + may_garbage_collect = false; + + save_m_keys = vim_strsave(mp->m_keys); + save_m_str = vim_strsave(mp->m_str); + map_str = eval_map_expr(save_m_str, NUL); + vgetc_busy = save_vgetc_busy; + may_garbage_collect = save_may_garbage_collect; + } else { + map_str = mp->m_str; + } + + // Insert the 'to' part in the typebuf.tb_buf. + // If 'from' field is the same as the start of the 'to' field, don't + // remap the first character (but do allow abbreviations). + // If m_noremap is set, don't remap the whole 'to' part. + if (map_str == NULL) { + i = FAIL; + } else { + int noremap; + + // If this is a LANGMAP mapping, then we didn't record the keys + // at the start of the function and have to record them now. + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { + gotchars(map_str, STRLEN(map_str)); + } + + if (save_m_noremap != REMAP_YES) { + noremap = save_m_noremap; + } else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys, + (size_t)keylen) != 0) { + noremap = REMAP_YES; + } else { + noremap = REMAP_SKIP; + } + i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent); + if (save_m_expr) { + xfree(map_str); + } + } + xfree(save_m_keys); + xfree(save_m_str); + *keylenp = keylen; + if (i == FAIL) { + return map_result_fail; + } + return map_result_retry; + } + + *keylenp = keylen; + return map_result_nomatch; +} + // unget one character (can only be done once!) void vungetc(int c) { @@ -1715,44 +2034,27 @@ void vungetc(int c) static int vgetorpeek(bool advance) { int c, c1; - int keylen; - char_u *s; - mapblock_T *mp; - mapblock_T *mp2; - mapblock_T *mp_match; - int mp_match_len = 0; - bool timedout = false; // waited for more than 1 second - // for mapping to complete + bool timedout = false; // waited for more than 1 second for mapping to complete int mapdepth = 0; // check for recursive mapping bool mode_deleted = false; // set when mode has been deleted - int local_State; - int mlen; - int max_mlen; - int i; int new_wcol, new_wrow; int n; - int nolmaplen; int old_wcol, old_wrow; int wait_tb_len; - /* - * This function doesn't work very well when called recursively. This may - * happen though, because of: - * 1. The call to add_to_showcmd(). char_avail() is then used to check if - * there is a character available, which calls this function. In that - * case we must return NUL, to indicate no character is available. - * 2. A GUI callback function writes to the screen, causing a - * wait_return(). - * Using ":normal" can also do this, but it saves the typeahead buffer, - * thus it should be OK. But don't get a key from the user then. - */ - if (vgetc_busy > 0 - && ex_normal_busy == 0) { + // This function doesn't work very well when called recursively. This may + // happen though, because of: + // 1. The call to add_to_showcmd(). char_avail() is then used to check if + // there is a character available, which calls this function. In that + // case we must return NUL, to indicate no character is available. + // 2. A GUI callback function writes to the screen, causing a + // wait_return(). + // Using ":normal" can also do this, but it saves the typeahead buffer, + // thus it should be OK. But don't get a key from the user then. + if (vgetc_busy > 0 && ex_normal_busy == 0) { return NUL; } - local_State = get_real_state(); - ++vgetc_busy; if (advance) { @@ -1765,9 +2067,7 @@ static int vgetorpeek(bool advance) reg_executing = 0; } do { - /* - * get a character: 1. from the stuffbuffer - */ + // get a character: 1. from the stuffbuffer if (typeahead_char != 0) { c = typeahead_char; if (advance) { @@ -1784,30 +2084,27 @@ static int vgetorpeek(bool advance) KeyStuffed = true; } if (typebuf.tb_no_abbr_cnt == 0) { - typebuf.tb_no_abbr_cnt = 1; // no abbreviations now + typebuf.tb_no_abbr_cnt = 1; // no abbreviations now } } else { - /* - * Loop until we either find a matching mapped key, or we - * are sure that it is not a mapped key. - * If a mapped key sequence is found we go back to the start to - * try re-mapping. - */ + // Loop until we either find a matching mapped key, or we + // are sure that it is not a mapped key. + // If a mapped key sequence is found we go back to the start to + // try re-mapping. for (;;) { - /* - * os_breakcheck() is slow, don't use it too often when - * inside a mapping. But call it each time for typed - * characters. - */ + // os_breakcheck() is slow, don't use it too often when + // inside a mapping. But call it each time for typed + // characters. if (typebuf.tb_maplen) { line_breakcheck(); } else { - os_breakcheck(); // check for CTRL-C + os_breakcheck(); // check for CTRL-C } - keylen = 0; + int keylen = 0; if (got_int) { // flush all input c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L); + // If inchar() returns TRUE (script file was active) or we // are inside a mapping, get out of Insert mode. // Otherwise we behave like having gotten a CTRL-C. @@ -1831,361 +2128,49 @@ static int vgetorpeek(bool advance) break; } else if (typebuf.tb_len > 0) { - /* - * Check for a mappable key sequence. - * Walk through one maphash[] list until we find an - * entry that matches. - * - * Don't look for mappings if: - * - no_mapping set: mapping disabled (e.g. for CTRL-V) - * - maphash_valid not set: no mappings present. - * - typebuf.tb_buf[typebuf.tb_off] should not be remapped - * - in insert or cmdline mode and 'paste' option set - * - waiting for "hit return to continue" and CR or SPACE - * typed - * - waiting for a char with --more-- - * - in Ctrl-X mode, and we get a valid char for that mode - */ - mp = NULL; - max_mlen = 0; - c1 = typebuf.tb_buf[typebuf.tb_off]; - if (no_mapping == 0 && maphash_valid - && (no_zero_mapping == 0 || c1 != '0') - && (typebuf.tb_maplen == 0 - || (p_remap - && (typebuf.tb_noremap[typebuf.tb_off] - & (RM_NONE|RM_ABBR)) == 0)) - && !(p_paste && (State & (INSERT + CMDLINE))) - && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) - && State != ASKMORE - && State != CONFIRM - && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1)) - || ((compl_cont_status & CONT_LOCAL) - && (c1 == Ctrl_N - || c1 == Ctrl_P)))) { - if (c1 == K_SPECIAL) { - nolmaplen = 2; - } else { - LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0 - && get_real_state() != SELECTMODE); - nolmaplen = 0; - } - // First try buffer-local mappings. - mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; - mp2 = maphash[MAP_HASH(local_State, c1)]; - if (mp == NULL) { - // There are no buffer-local mappings. - mp = mp2; - mp2 = NULL; - } - /* - * Loop until a partly matching mapping is found or - * all (local) mappings have been checked. - * The longest full match is remembered in "mp_match". - * A full match is only accepted if there is no partly - * match, so "aa" and "aaa" can both be mapped. - */ - mp_match = NULL; - mp_match_len = 0; - for (; mp != NULL; - mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : - (mp = mp->m_next)) { - /* - * Only consider an entry if the first character - * matches and it is for the current state. - * Skip ":lmap" mappings if keys were mapped. - */ - if (mp->m_keys[0] == c1 - && (mp->m_mode & local_State) - && ((mp->m_mode & LANGMAP) == 0 - || typebuf.tb_maplen == 0)) { - int nomap = nolmaplen; - int c2; - // find the match length of this mapping - for (mlen = 1; mlen < typebuf.tb_len; mlen++) { - c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; - if (nomap > 0) { - --nomap; - } else if (c2 == K_SPECIAL) { - nomap = 2; - } else { - LANGMAP_ADJUST(c2, TRUE); - } - if (mp->m_keys[mlen] != c2) { - break; - } - } + // Check for a mapping in "typebuf". + map_result_T result = (map_result_T)handle_mapping(&keylen, &timedout, &mapdepth); - /* Don't allow mapping the first byte(s) of a - * multi-byte char. Happens when mapping - * <M-a> and then changing 'encoding'. Beware - * that 0x80 is escaped. */ - char_u *p1 = mp->m_keys; - char_u *p2 = (char_u *)mb_unescape((const char **)&p1); - - if (p2 != NULL && MB_BYTE2LEN(c1) > utfc_ptr2len(p2)) { - mlen = 0; - } - - // Check an entry whether it matches. - // - Full match: mlen == keylen - // - Partly match: mlen == typebuf.tb_len - keylen = mp->m_keylen; - if (mlen == keylen - || (mlen == typebuf.tb_len - && typebuf.tb_len < keylen)) { - /* - * If only script-local mappings are - * allowed, check if the mapping starts - * with K_SNR. - */ - s = typebuf.tb_noremap + typebuf.tb_off; - if (*s == RM_SCRIPT - && (mp->m_keys[0] != K_SPECIAL - || mp->m_keys[1] != KS_EXTRA - || mp->m_keys[2] - != KE_SNR)) { - continue; - } - /* - * If one of the typed keys cannot be - * remapped, skip the entry. - */ - for (n = mlen; --n >= 0;) { - if (*s++ & (RM_NONE|RM_ABBR)) { - break; - } - } - if (n >= 0) { - continue; - } - - if (keylen > typebuf.tb_len) { - if (!timedout && !(mp_match != NULL - && mp_match->m_nowait)) { - // break at a partly match - keylen = KEYLEN_PART_MAP; - break; - } - } else if (keylen > mp_match_len - || (keylen == mp_match_len - && mp_match != NULL - && (mp_match->m_mode & LANGMAP) == 0 - && (mp->m_mode & LANGMAP) != 0)) { - // found a longer match - mp_match = mp; - mp_match_len = keylen; - } - } else { - // No match; may have to check for termcode at next character. - if (max_mlen < mlen) { - max_mlen = mlen; - } - } - } - } - - /* If no partly match found, use the longest full - * match. */ - if (keylen != KEYLEN_PART_MAP) { - mp = mp_match; - keylen = mp_match_len; - } - } - - if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { - bool match = typebuf_match_len(p_pt, &mlen); - if (match) { - // write chars to script file(s) - if (mlen > typebuf.tb_maplen) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(mlen - typebuf.tb_maplen)); - } - - del_typebuf(mlen, 0); // Remove the chars. - set_option_value("paste", !p_paste, NULL, 0); - if (!(State & INSERT)) { - msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); // clear ruler - } - status_redraw_all(); - redraw_statuslines(); - showmode(); - setcursor(); - continue; - } - // Need more chars for partly match. - if (mlen == typebuf.tb_len) { - keylen = KEYLEN_PART_KEY; - } else if (max_mlen < mlen) { - // no match, may have to check for termcode at - // next character - max_mlen = mlen + 1; - } + if (result == map_result_retry) { + // try mapping again + continue; } - if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { - // No matching mapping found or found a non-matching mapping that - // matches at least what the matching mapping matched - keylen = 0; - (void)keylen; // suppress clang/dead assignment - // If there was no mapping, use the character from the typeahead - // buffer right here. Otherwise, use the mapping (loop around). - if (mp == NULL) { - // get a character: 2. from the typeahead buffer - c = typebuf.tb_buf[typebuf.tb_off] & 255; - if (advance) { // remove chars from tb_buf - cmd_silent = (typebuf.tb_silent > 0); - if (typebuf.tb_maplen > 0) { - KeyTyped = false; - } else { - KeyTyped = true; - // write char to script file(s) - gotchars(typebuf.tb_buf + typebuf.tb_off, 1); - } - KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; - del_typebuf(1, 0); - } - break; // got character, break for loop - } else { - keylen = mp_match_len; - } + if (result == map_result_fail) { + // failed, use the outer loop + c = -1; + break; } - // complete match - if (keylen >= 0 && keylen <= typebuf.tb_len) { - int save_m_expr; - int save_m_noremap; - int save_m_silent; - - // Write chars to script file(s) - // Note: :lmap mappings are written *after* being applied. #5658 - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(keylen - typebuf.tb_maplen)); - } - - cmd_silent = (typebuf.tb_silent > 0); - del_typebuf(keylen, 0); // remove the mapped keys - - /* - * Put the replacement string in front of mapstr. - * The depth check catches ":map x y" and ":map y x". - */ - if (++mapdepth >= p_mmd) { - emsg(_("E223: recursive mapping")); - if (State & CMDLINE) { - redrawcmdline(); + if (result == map_result_get) { + // get a character: 2. from the typeahead buffer + c = typebuf.tb_buf[typebuf.tb_off] & 255; + if (advance) { // remove chars from tb_buf + cmd_silent = (typebuf.tb_silent > 0); + if (typebuf.tb_maplen > 0) { + KeyTyped = false; } else { - setcursor(); + KeyTyped = true; + // write char to script file(s) + gotchars(typebuf.tb_buf + typebuf.tb_off, 1); } - flush_buffers(FLUSH_MINIMAL); - mapdepth = 0; // for next one - c = -1; - break; - } - - /* - * In Select mode and a Visual mode mapping is used: - * Switch to Visual mode temporarily. Append K_SELECT - * to switch back to Select mode. - */ - if (VIsual_active && VIsual_select - && (mp->m_mode & VISUAL)) { - VIsual_select = false; - (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); - } - - /* Copy the values from *mp that are used, because - * evaluating the expression may invoke a function - * that redefines the mapping, thereby making *mp - * invalid. */ - save_m_expr = mp->m_expr; - save_m_noremap = mp->m_noremap; - save_m_silent = mp->m_silent; - char_u *save_m_keys = NULL; // only saved when needed - char_u *save_m_str = NULL; // only saved when needed - - /* - * Handle ":map <expr>": evaluate the {rhs} as an - * expression. Also save and restore the command line - * for "normal :". - */ - if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; - const bool save_may_garbage_collect = may_garbage_collect; - - vgetc_busy = 0; - may_garbage_collect = false; - - save_m_keys = vim_strsave(mp->m_keys); - save_m_str = vim_strsave(mp->m_str); - s = eval_map_expr(save_m_str, NUL); - vgetc_busy = save_vgetc_busy; - may_garbage_collect = save_may_garbage_collect; - } else { - s = mp->m_str; + KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; + del_typebuf(1, 0); } - - /* - * Insert the 'to' part in the typebuf.tb_buf. - * If 'from' field is the same as the start of the - * 'to' field, don't remap the first character (but do - * allow abbreviations). - * If m_noremap is set, don't remap the whole 'to' - * part. - */ - if (s == NULL) { - i = FAIL; - } else { - int noremap; - - // If this is a LANGMAP mapping, then we didn't record the keys - // at the start of the function and have to record them now. - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { - gotchars(s, STRLEN(s)); - } - - if (save_m_noremap != REMAP_YES) { - noremap = save_m_noremap; - } else if ( - STRNCMP(s, save_m_keys != NULL - ? save_m_keys : mp->m_keys, - (size_t)keylen) - != 0) { - noremap = REMAP_YES; - } else { - noremap = REMAP_SKIP; - } - i = ins_typebuf(s, noremap, - 0, TRUE, cmd_silent || save_m_silent); - if (save_m_expr) { - xfree(s); - } - } - xfree(save_m_keys); - xfree(save_m_str); - if (i == FAIL) { - c = -1; - break; - } - continue; + break; } + + // not enough characters, get more } - /* - * get a character: 3. from the user - handle <Esc> in Insert mode - */ - /* - * special case: if we get an <ESC> in insert mode and there - * are no more characters at once, we pretend to go out of - * insert mode. This prevents the one second delay after - * typing an <ESC>. If we get something after all, we may - * have to redisplay the mode. That the cursor is in the wrong - * place does not matter. - */ + // get a character: 3. from the user - handle <Esc> in Insert mode + + // special case: if we get an <ESC> in insert mode and there + // are no more characters at once, we pretend to go out of + // insert mode. This prevents the one second delay after + // typing an <ESC>. If we get something after all, we may + // have to redisplay the mode. That the cursor is in the wrong + // place does not matter. c = 0; new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; @@ -2196,10 +2181,8 @@ static int vgetorpeek(bool advance) && ex_normal_busy == 0 && typebuf.tb_maplen == 0 && (State & INSERT) - && (p_timeout - || (keylen == KEYLEN_PART_KEY && p_ttimeout)) - && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, - 3, 25L)) == 0) { + && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout)) + && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { colnr_T col = 0, vcol; char_u *ptr; @@ -2215,11 +2198,9 @@ static int vgetorpeek(bool advance) if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { if (did_ai) { - /* - * We are expecting to truncate the trailing - * white-space, so find the last non-white - * character -- webb - */ + // We are expecting to truncate the trailing + // white-space, so find the last non-white + // character -- webb col = vcol = curwin->w_wcol = 0; ptr = get_cursor_line_ptr(); while (col < curwin->w_cursor.col) { @@ -2233,7 +2214,7 @@ static int vgetorpeek(bool advance) + curwin->w_wcol / curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner; curwin->w_wcol += curwin_col_off(); - col = 0; // no correction needed + col = 0; // no correction needed } else { --curwin->w_wcol; col = curwin->w_cursor.col - 1; @@ -2261,7 +2242,7 @@ static int vgetorpeek(bool advance) curwin->w_wrow = old_wrow; } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } // Allow mapping for just typed characters. When we get here c @@ -2307,9 +2288,8 @@ static int vgetorpeek(bool advance) break; } - /* - * get a character: 3. from the user - update display - */ + // get a character: 3. from the user - update display + // In insert mode a screen update is skipped when characters // are still available. But when those available characters // are part of a mapping, and we are going to do a blocking @@ -2320,26 +2300,21 @@ static int vgetorpeek(bool advance) if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 && advance && must_redraw != 0 && !need_wait_return) { update_screen(0); - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } - /* - * If we have a partial match (and are going to wait for more - * input from the user), show the partially matched characters - * to the user with showcmd. - */ - i = 0; + // If we have a partial match (and are going to wait for more + // input from the user), show the partially matched characters + // to the user with showcmd. + int showcmd_idx = 0; c1 = 0; if (typebuf.tb_len > 0 && advance && !exmode_active) { - if (((State & (NORMAL | INSERT)) || State == LANGMAP) - && State != HITRETURN) { + if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) { // this looks nice when typing a dead character map if (State & INSERT - && ptr2cells(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len - 1) == 1) { - edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], - false); - setcursor(); // put cursor back where it belongs + && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) { + edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], false); + setcursor(); // put cursor back where it belongs c1 = 1; } // need to use the col and row from above here @@ -2349,11 +2324,10 @@ static int vgetorpeek(bool advance) curwin->w_wrow = new_wrow; push_showcmd(); if (typebuf.tb_len > SHOWCMD_COLS) { - i = typebuf.tb_len - SHOWCMD_COLS; + showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } - while (i < typebuf.tb_len) { - (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off - + i++]); + while (showcmd_idx < typebuf.tb_len) { + (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; @@ -2369,9 +2343,7 @@ static int vgetorpeek(bool advance) } } - /* - * get a character: 3. from the user - get it - */ + // get a character: 3. from the user - get it if (typebuf.tb_len == 0) { // timedout may have been set while waiting for a mapping // that has a <Nop> RHS. @@ -2381,8 +2353,7 @@ static int vgetorpeek(bool advance) long wait_time = 0; if (advance) { - if (typebuf.tb_len == 0 - || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { + if (typebuf.tb_len == 0 || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { // blocking wait wait_time = -1L; } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) { @@ -2397,7 +2368,7 @@ static int vgetorpeek(bool advance) typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, wait_time); - if (i != 0) { + if (showcmd_idx != 0) { pop_showcmd(); } if (c1 == 1) { @@ -2407,47 +2378,45 @@ static int vgetorpeek(bool advance) if (State & CMDLINE) { unputcmdline(); } else { - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } - if (c == NUL) { // no character available + if (c == NUL) { // no character available if (!advance) { break; } - if (wait_tb_len > 0) { // timed out + if (wait_tb_len > 0) { // timed out timedout = true; continue; } - } else { // allow mapping for just typed characters + } else { // allow mapping for just typed characters while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) { typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES; } } - } // for (;;) - } // if (!character from stuffbuf) + } // for (;;) + } // if (!character from stuffbuf) // if advance is false don't loop on NULs } while (c < 0 || (advance && c == NUL)); - /* - * The "INSERT" message is taken care of here: - * if we return an ESC to exit insert mode, the message is deleted - * if we don't return an ESC but deleted the message before, redisplay it - */ + // The "INSERT" message is taken care of here: + // if we return an ESC to exit insert mode, the message is deleted + // if we don't return an ESC but deleted the message before, redisplay it if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // delete mode later + redraw_cmdline = true; // delete mode later } else { unshowmode(false); } } else if (c != ESC && mode_deleted) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // show mode later + redraw_cmdline = true; // show mode later } else { showmode(); } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index b5e1fda9f1..b2422fd531 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include <stdbool.h> +#include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" @@ -533,6 +534,11 @@ EXTERN int VIsual_mode INIT(= 'v'); /// true when redoing Visual. EXTERN int redo_VIsual_busy INIT(= false); +// The Visual area is remembered for reselection. +EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V +EXTERN linenr_T resel_VIsual_line_count; // number of lines +EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col + /// When pasting text with the middle mouse button in visual mode with /// restart_edit set, remember where it started so we can set Insstart. EXTERN pos_T where_paste_started; @@ -727,6 +733,7 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or // :bufdo is executing EXTERN bool need_start_insertmode INIT(= false); // start insert mode soon +EXTERN char *last_mode INIT(= NULL); EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 93fcdc55a6..6fc70144ac 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -1253,7 +1253,7 @@ static struct prt_dsc_comment_S prt_dsc_table[] = * Variables for the output PostScript file. */ static FILE *prt_ps_fd; -static int prt_file_error; +static bool prt_file_error; static char_u *prt_ps_file_name = NULL; /* @@ -1329,7 +1329,7 @@ static void prt_write_file_raw_len(char_u *buffer, size_t bytes) if (!prt_file_error && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd) != bytes) { emsg(_("E455: Error writing to PostScript output file")); - prt_file_error = TRUE; + prt_file_error = true; } } @@ -1981,7 +1981,7 @@ void mch_print_cleanup(void) if (prt_ps_fd != NULL) { fclose(prt_ps_fd); prt_ps_fd = NULL; - prt_file_error = FALSE; + prt_file_error = false; } if (prt_ps_file_name != NULL) { XFREE_CLEAR(prt_ps_file_name); @@ -2203,7 +2203,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Check encoding and character set are compatible if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0) { emsg(_("E673: Incompatible multi-byte encoding and character set.")); - return FALSE; + return false; } // Add charset name if not empty @@ -2215,7 +2215,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Add custom CMap character set name if (*p_pmcs == NUL) { emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding.")); - return FALSE; + return false; } STRLCPY(prt_cmap, p_pmcs, sizeof(prt_cmap) - 2); STRCAT(prt_cmap, "-"); @@ -2231,7 +2231,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) if (!mbfont_opts[OPT_MBFONT_REGULAR].present) { emsg(_("E675: No default font specified for multi-byte printing.")); - return FALSE; + return false; } // Derive CID font names with fallbacks if not defined @@ -2425,12 +2425,12 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) prt_need_bgcol = false; prt_need_underline = false; - prt_file_error = FALSE; + prt_file_error = false; return OK; } -static int prt_add_resource(struct prt_ps_resource_S *resource) +static bool prt_add_resource(struct prt_ps_resource_S *resource) { FILE *fd_resource; char_u resource_buffer[512]; @@ -2439,7 +2439,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) fd_resource = os_fopen((char *)resource->filename, READBIN); if (fd_resource == NULL) { semsg(_("E456: Can't open file \"%s\""), resource->filename); - return FALSE; + return false; } switch (resource->type) { case PRT_RESOURCE_TYPE_PROCSET: @@ -2449,7 +2449,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) (char *)resource->title); break; default: - return FALSE; + return false; } prt_dsc_textline("BeginDocument", (char *)resource->filename); @@ -2461,7 +2461,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) semsg(_("E457: Can't read PostScript resource file \"%s\""), resource->filename); fclose(fd_resource); - return FALSE; + return false; } if (bytes_read == 0) { break; @@ -2469,7 +2469,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_write_file_raw_len(resource_buffer, bytes_read); if (prt_file_error) { fclose(fd_resource); - return FALSE; + return false; } } fclose(fd_resource); @@ -2478,10 +2478,10 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_dsc_noarg("EndResource"); - return TRUE; + return true; } -int mch_print_begin(prt_settings_T *psettings) +bool mch_print_begin(prt_settings_T *psettings) { int bbox[4]; double left; @@ -2495,7 +2495,6 @@ int mch_print_begin(prt_settings_T *psettings) char_u *p; struct prt_ps_resource_S res_cidfont; struct prt_ps_resource_S res_cmap; - int retval = FALSE; /* * PS DSC Header comments - no PS code! @@ -2567,25 +2566,25 @@ int mch_print_begin(prt_settings_T *psettings) // Search for external resources VIM supplies if (!prt_find_resource("prolog", &res_prolog)) { emsg(_("E456: Can't find PostScript resource file \"prolog.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_prolog)) { - return FALSE; + return false; } if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION)) { - return FALSE; + return false; } if (prt_out_mbyte) { // Look for required version of multi-byte printing procset if (!prt_find_resource("cidfont", &res_cidfont)) { emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_cidfont)) { - return FALSE; + return false; } if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION)) { - return FALSE; + return false; } } @@ -2610,12 +2609,12 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(p_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), p_encoding); - return FALSE; + return false; } } } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform @@ -2629,10 +2628,10 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(prt_ascii_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_ascii_encoding); - return FALSE; + return false; } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform @@ -2655,10 +2654,10 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(prt_cmap, &res_cmap)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_cmap); - return FALSE; + return false; } if (!prt_open_resource(&res_cmap)) { - return FALSE; + return false; } } @@ -2736,7 +2735,7 @@ int mch_print_begin(prt_settings_T *psettings) // There will be only one Roman font encoding to be included in the PS // file. if (!prt_add_resource(&res_encoding)) { - return FALSE; + return false; } } @@ -2846,9 +2845,7 @@ int mch_print_begin(prt_settings_T *psettings) prt_dsc_noarg("EndSetup"); // Fail if any problems writing out to the PS file - retval = !prt_file_error; - - return retval; + return !prt_file_error; } void mch_print_end(prt_settings_T *psettings) diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 6f02ebfb48..daef8db267 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -223,9 +223,9 @@ void ex_cstag(exarg_T *eap) switch (p_csto) { case 0: if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); if (msg_col) { msg_putchar('\n'); @@ -249,16 +249,16 @@ void ex_cstag(exarg_T *eap) if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, - FALSE, FALSE, *eap->cmdlinep); - if (ret == FALSE) { + false, false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } } } else if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } @@ -520,7 +520,7 @@ add_err: } -static int cs_check_for_connections(void) +static bool cs_check_for_connections(void) { return cs_cnt_connections() > 0; } @@ -887,20 +887,20 @@ static int cs_find(exarg_T *eap) { char *opt, *pat; - if (cs_check_for_connections() == FALSE) { + if (cs_check_for_connections() == false) { (void)emsg(_("E567: no cscope connections")); - return FALSE; + return false; } if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) { cs_usage_msg(Find); - return FALSE; + return false; } pat = opt + strlen(opt) + 1; if (pat >= (char *)eap->arg + eap_arg_len) { cs_usage_msg(Find); - return FALSE; + return false; } /* @@ -919,8 +919,8 @@ static int cs_find(exarg_T *eap) /// Common code for cscope find, shared by cs_find() and ex_cstag(). -static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, - char_u *cmdline) +static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, + bool use_ll, char_u *cmdline) { char *cmd; int *nummatches; @@ -967,7 +967,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us // next symbol must be + or - if (strchr(CSQF_FLAGS, *qfpos) == NULL) { (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1)); - return FALSE; + return false; } if (*qfpos != '0' @@ -982,7 +982,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us // create the actual command to send to cscope cmd = cs_create_cmd(opt, pat); if (cmd == NULL) { - return FALSE; + return false; } nummatches = xmalloc(sizeof(int) * csinfo_size); @@ -1019,7 +1019,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat); } xfree(nummatches); - return FALSE; + return false; } if (qfpos != NULL && *qfpos != '0') { @@ -1064,7 +1064,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us os_remove((char *)tmp); xfree(tmp); xfree(nummatches); - return TRUE; + return true; } else { char **matches = NULL, **contexts = NULL; size_t matched = 0; @@ -1073,7 +1073,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched); xfree(nummatches); if (matches == NULL) { - return FALSE; + return false; } (void)cs_manage_matches(matches, contexts, matched, Store); @@ -1499,12 +1499,13 @@ static void cs_file_results(FILE *f, int *nummatches_a) continue; } - context = xmalloc(strlen(cntx) + 5); + size_t context_len = strlen(cntx) + 5; + context = xmalloc(context_len); if (strcmp(cntx, "<global>") == 0) { - strcpy(context, "<<global>>"); + xstrlcpy(context, "<<global>>", context_len); } else { - sprintf(context, "<<%s>>", cntx); + snprintf(context, context_len, "<<%s>>", cntx); } if (search == NULL) { diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h index 8cfeb03cc4..e81db43038 100644 --- a/src/nvim/lib/khash.h +++ b/src/nvim/lib/khash.h @@ -666,7 +666,7 @@ static kh_inline khint_t __ac_Wang_hash(khint_t key) } \ } -// More conenient interfaces +// More convenient interfaces /*! @function @abstract Instantiate a hash set containing integer keys diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 9f2372f831..b6792a5a97 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -782,10 +782,10 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) break; } #define ADD_TYPE(type, data_key) \ -case kObjectType##type: { \ - nlua_push_##type(lstate, obj.data.data_key, special); \ - break; \ -} + case kObjectType##type: { \ + nlua_push_##type(lstate, obj.data.data_key, special); \ + break; \ + } ADD_TYPE(Boolean, boolean) ADD_TYPE(Integer, integer) ADD_TYPE(Float, floating) @@ -794,10 +794,10 @@ case kObjectType##type: { \ ADD_TYPE(Dictionary, dictionary) #undef ADD_TYPE #define ADD_REMOTE_TYPE(type) \ -case kObjectType##type: { \ - nlua_push_##type(lstate, (type)obj.data.integer, special); \ - break; \ -} + case kObjectType##type: { \ + nlua_push_##type(lstate, (type)obj.data.integer, special); \ + break; \ + } ADD_REMOTE_TYPE(Buffer) ADD_REMOTE_TYPE(Window) ADD_REMOTE_TYPE(Tabpage) diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index db79e9e7e9..b5553060a1 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -175,13 +175,13 @@ int nlua_str_utfindex(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL size_t s1_len; const char *s1 = luaL_checklstring(lstate, 1, &s1_len); intptr_t idx; - if (lua_gettop(lstate) >= 2) { + if (lua_isnoneornil(lstate, 2)) { + idx = (intptr_t)s1_len; + } else { idx = luaL_checkinteger(lstate, 2); if (idx < 0 || idx > (intptr_t)s1_len) { return luaL_error(lstate, "index out of range"); } - } else { - idx = (intptr_t)s1_len; } size_t codepoints = 0, codeunits = 0; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index ad886b40dd..c1a1e7f162 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -454,7 +454,7 @@ local on_key_cbs = {} --- On each key press, Nvim passes the key char to fn(). |i_CTRL-V| --- If {fn} is nil, it removes the callback for the associated {ns_id} ---@param ns_id number? Namespace ID. If nil or 0, generates and returns a new ---- |nvim_create_namesapce()| id. +--- |nvim_create_namespace()| id. --- ---@return number Namespace id associated with {fn}. Or count of all callbacks ---if on_key() is called without arguments. @@ -580,6 +580,7 @@ function vim._expand_pat(pat, env) end local keys = {} + ---@private local function insert_keys(obj) for k,_ in pairs(obj) do if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 559a3cc435..3d621ebbb7 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -631,6 +631,7 @@ void free_all_mem(void) clear_sb_text(true); // free any scrollback text // Free some global vars. + xfree(last_mode); xfree(last_cmdline); xfree(new_last_cmdline); set_keep_msg(NULL, 0); diff --git a/src/nvim/message.c b/src/nvim/message.c index 6fcd4cef8a..8a6ac2decc 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -839,13 +839,11 @@ void msg_schedule_semsg(const char *const fmt, ...) multiqueue_put(main_loop.events, msg_semsg_event, 1, s); } -/* - * Like msg(), but truncate to a single line if p_shm contains 't', or when - * "force" is TRUE. This truncates in another way as for normal messages. - * Careful: The string may be changed by msg_may_trunc()! - * Returns a pointer to the printed message, if wait_return() not called. - */ -char *msg_trunc_attr(char *s, int force, int attr) +// Like msg(), but truncate to a single line if p_shm contains 't', or when +// "force" is true. This truncates in another way as for normal messages. +// Careful: The string may be changed by msg_may_trunc()! +// Returns a pointer to the printed message, if wait_return() not called. +char *msg_trunc_attr(char *s, bool force, int attr) { int n; @@ -869,7 +867,7 @@ char *msg_trunc_attr(char *s, int force, int attr) * Return a pointer to where the truncated message starts. * Note: May change the message by replacing a character with '<'. */ -char_u *msg_may_trunc(int force, char_u *s) +char_u *msg_may_trunc(bool force, char_u *s) { int room; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index fd5d154cea..872a2c58e3 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -1059,3 +1059,58 @@ void add_time(char_u *buf, size_t buflen, time_t tt) seconds); } } + +dict_T *get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = true; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } else { + sve->sve_did_save = false; + } + return v_event; +} + +void restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + +/// Fires a ModeChanged autocmd. +void trigger_modechanged(void) +{ + if (!has_event(EVENT_MODECHANGED)) { + return; + } + + char *mode = get_mode(); + if (STRCMP(mode, last_mode) == 0) { + xfree(mode); + return; + } + + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + tv_dict_add_str(v_event, S_LEN("new_mode"), mode); + tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode); + + char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":"); + char_u *pat = concat_str(pat_pre, (char_u *)mode); + xfree(pat_pre); + + apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf); + xfree(last_mode); + last_mode = mode; + + xfree(pat); + restore_v_event(v_event, &save_v_event); +} diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index f805858904..32014fcf2b 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -92,15 +92,15 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) break; } #define STR_CASE(type, attr, obj, dest, conv) \ -case type: { \ - dest = conv(((String) { \ + case type: { \ + dest = conv(((String) { \ .size = obj->via.attr.size, \ .data = (obj->via.attr.ptr == NULL || obj->via.attr.size == 0 \ ? xmemdupz("", 0) \ : xmemdupz(obj->via.attr.ptr, obj->via.attr.size)), \ })); \ - break; \ -} + break; \ + } STR_CASE(MSGPACK_OBJECT_STR, str, cur.mobj, *cur.aobj, STRING_OBJ) STR_CASE(MSGPACK_OBJECT_BIN, bin, cur.mobj, *cur.aobj, STRING_OBJ) case MSGPACK_OBJECT_ARRAY: { @@ -143,10 +143,10 @@ case type: { \ const msgpack_object *const key = &cur.mobj->via.map.ptr[idx].key; switch (key->type) { #define ID(x) x - STR_CASE(MSGPACK_OBJECT_STR, str, key, - cur.aobj->data.dictionary.items[idx].key, ID) - STR_CASE(MSGPACK_OBJECT_BIN, bin, key, - cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_STR, str, key, + cur.aobj->data.dictionary.items[idx].key, ID) + STR_CASE(MSGPACK_OBJECT_BIN, bin, key, + cur.aobj->data.dictionary.items[idx].key, ID) #undef ID case MSGPACK_OBJECT_NIL: case MSGPACK_OBJECT_BOOLEAN: diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 03312e5df4..9332c55b5f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -31,8 +31,8 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/main.h" @@ -84,12 +84,6 @@ typedef struct normal_state { pos_T old_pos; } NormalState; -/* - * The Visual area is remembered for reselection. - */ -static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V -static linenr_T resel_VIsual_line_count; // number of lines -static colnr_T resel_VIsual_vcol; // nr of cols or end col static int VIsual_mode_orig = NUL; // saved Visual mode @@ -487,6 +481,7 @@ static void normal_prepare(NormalState *s) if (finish_op != c) { ui_cursor_shape(); // may show different cursor shape } + trigger_modechanged(); // When not finishing an operator and no register name typed, reset the count. if (!finish_op && !s->oa.regname) { @@ -928,6 +923,7 @@ normal_end: // Reset finish_op, in case it was set s->c = finish_op; finish_op = false; + trigger_modechanged(); // Redraw the cursor with another shape, if we were in Operator-pending // mode or did a replace command. if (s->c || s->ca.cmdchar == 'r') { @@ -965,6 +961,7 @@ normal_end: && s->oa.regname == 0) { if (restart_VIsual_select == 1) { VIsual_select = true; + trigger_modechanged(); showmode(); restart_VIsual_select = 0; } @@ -1452,758 +1449,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) *set_prevcount = false; // only set v:prevcount once } -// Handle an operator after Visual mode or when the movement is finished. -// "gui_yank" is true when yanking text for the clipboard. -void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) -{ - oparg_T *oap = cap->oap; - pos_T old_cursor; - bool empty_region_error; - int restart_edit_save; - int lbr_saved = curwin->w_p_lbr; - - - // The visual area is remembered for redo - static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V - static linenr_T redo_VIsual_line_count; // number of lines - static colnr_T redo_VIsual_vcol; // number of cols or end column - static long redo_VIsual_count; // count for Visual operator - static int redo_VIsual_arg; // extra argument - bool include_line_break = false; - - old_cursor = curwin->w_cursor; - - /* - * If an operation is pending, handle it... - */ - if ((finish_op - || VIsual_active) - && oap->op_type != OP_NOP) { - // Yank can be redone when 'y' is in 'cpoptions', but not when yanking - // for the clipboard. - const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; - - // Avoid a problem with unwanted linebreaks in block mode - if (curwin->w_p_lbr) { - curwin->w_valid &= ~VALID_VIRTCOL; - } - curwin->w_p_lbr = false; - oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') { - oap->motion_type = kMTLineWise; - } else if (oap->motion_force == 'v') { - // If the motion was linewise, "inclusive" will not have been set. - // Use "exclusive" to be consistent. Makes "dvj" work nice. - if (oap->motion_type == kMTLineWise) { - oap->inclusive = false; - } else if (oap->motion_type == kMTCharWise) { - // If the motion already was charwise, toggle "inclusive" - oap->inclusive = !oap->inclusive; - } - oap->motion_type = kMTCharWise; - } else if (oap->motion_force == Ctrl_V) { - // Change line- or charwise motion into Visual block mode. - if (!VIsual_active) { - VIsual_active = true; - VIsual = oap->start; - } - VIsual_mode = Ctrl_V; - VIsual_select = false; - VIsual_reselect = false; - } - - // Only redo yank when 'y' flag is in 'cpoptions'. - // Never redo "zf" (define fold). - if ((redo_yank || oap->op_type != OP_YANK) - && ((!VIsual_active || oap->motion_force) - // Also redo Operator-pending Visual mode mappings. - || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) - && oap->op_type != OP_COLON)) - && cap->cmdchar != 'D' - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search - /* - * If 'cpoptions' does not contain 'r', insert the search - * pattern to really repeat the same command. - */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) { - AppendToRedobuffLit(cap->searchbuf, -1); - } - AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { - // do_cmdline() has stored the first typed line in - // "repeat_cmdline". When several lines are typed repeating - // won't be possible. - if (repeat_cmdline == NULL) { - ResetRedobuff(); - } else { - AppendToRedobuffLit(repeat_cmdline, -1); - AppendToRedobuff(NL_STR); - XFREE_CLEAR(repeat_cmdline); - } - } - } - - if (redo_VIsual_busy) { - /* Redo of an operation on a Visual area. Use the same size from - * redo_VIsual_line_count and redo_VIsual_vcol. */ - oap->start = curwin->w_cursor; - curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } - VIsual_mode = redo_VIsual_mode; - if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { - if (VIsual_mode == 'v') { - if (redo_VIsual_line_count <= 1) { - validate_virtcol(); - curwin->w_curswant = - curwin->w_virtcol + redo_VIsual_vcol - 1; - } else { - curwin->w_curswant = redo_VIsual_vcol; - } - } else { - curwin->w_curswant = MAXCOL; - } - coladvance(curwin->w_curswant); - } - cap->count0 = redo_VIsual_count; - cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); - } else if (VIsual_active) { - if (!gui_yank) { - // Save the current VIsual area for '< and '> marks, and "gv" - curbuf->b_visual.vi_start = VIsual; - curbuf->b_visual.vi_end = curwin->w_cursor; - curbuf->b_visual.vi_mode = VIsual_mode; - if (VIsual_mode_orig != NUL) { - curbuf->b_visual.vi_mode = VIsual_mode_orig; - VIsual_mode_orig = NUL; - } - curbuf->b_visual.vi_curswant = curwin->w_curswant; - curbuf->b_visual_mode_eval = VIsual_mode; - } - - // In Select mode, a linewise selection is operated upon like a - // charwise selection. - // Special case: gH<Del> deletes the last line. - if (VIsual_select && VIsual_mode == 'V' - && cap->oap->op_type != OP_DELETE) { - if (lt(VIsual, curwin->w_cursor)) { - VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); - } else { - curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); - } - VIsual_mode = 'v'; - } - /* If 'selection' is "exclusive", backup one character for - * charwise selections. */ - else if (VIsual_mode == 'v') { - include_line_break = - unadjust_for_sel(); - } - - oap->start = VIsual; - if (VIsual_mode == 'V') { - oap->start.col = 0; - oap->start.coladd = 0; - } - } - - /* - * Set oap->start to the first position of the operated text, oap->end - * to the end of the operated text. w_cursor is equal to oap->start. - */ - if (lt(oap->start, curwin->w_cursor)) { - // Include folded lines completely. - if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { - oap->start.col = 0; - } - if ((curwin->w_cursor.col > 0 - || oap->inclusive - || oap->motion_type == kMTLineWise) - && hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum)) { - curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); - } - } - oap->end = curwin->w_cursor; - curwin->w_cursor = oap->start; - - /* w_virtcol may have been updated; if the cursor goes back to its - * previous position w_virtcol becomes invalid and isn't updated - * automatically. */ - curwin->w_valid &= ~VALID_VIRTCOL; - } else { - // Include folded lines completely. - if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) { - curwin->w_cursor.col = 0; - } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); - } - } - oap->end = oap->start; - oap->start = curwin->w_cursor; - } - - // Just in case lines were deleted that make the position invalid. - check_pos(curwin->w_buffer, &oap->end); - oap->line_count = oap->end.lnum - oap->start.lnum + 1; - - // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); - - if (VIsual_active || redo_VIsual_busy) { - get_op_vcol(oap, redo_VIsual_vcol, true); - - if (!redo_VIsual_busy && !gui_yank) { - /* - * Prepare to reselect and redo Visual: this is based on the - * size of the Visual text - */ - resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) { - resel_VIsual_vcol = MAXCOL; - } else { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); - } - if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); - } - resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } else { - resel_VIsual_vcol = oap->end_vcol; - } - } - resel_VIsual_line_count = oap->line_count; - } - - // can't redo yank (unless 'y' is in 'cpoptions') and ":" - if ((redo_yank || oap->op_type != OP_YANK) - && oap->op_type != OP_COLON - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC - && oap->motion_force == NUL) { - /* Prepare for redoing. Only use the nchar field for "r", - * otherwise it might be the second char of the operator. */ - if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { - int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; - - // reverse what nv_replace() did - if (nchar == REPLACE_CR_NCHAR) { - nchar = CAR; - } else if (nchar == REPLACE_NL_NCHAR) { - nchar = NL; - } - prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), nchar); - } - if (!redo_VIsual_busy) { - redo_VIsual_mode = resel_VIsual_mode; - redo_VIsual_vcol = resel_VIsual_vcol; - redo_VIsual_line_count = resel_VIsual_line_count; - redo_VIsual_count = cap->count0; - redo_VIsual_arg = cap->arg; - } - } - - // oap->inclusive defaults to true. - // If oap->end is on a NUL (empty line) oap->inclusive becomes - // false. This makes "d}P" and "v}dP" work the same. - if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { - oap->inclusive = true; - } - if (VIsual_mode == 'V') { - oap->motion_type = kMTLineWise; - } else if (VIsual_mode == 'v') { - oap->motion_type = kMTCharWise; - if (*ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op)) { - oap->inclusive = false; - // Try to include the newline, unless it's an operator - // that works on lines only. - if (*p_sel != 'o' - && !op_on_lines(oap->op_type) - && oap->end.lnum < curbuf->b_ml.ml_line_count) { - oap->end.lnum++; - oap->end.col = 0; - oap->end.coladd = 0; - oap->line_count++; - } - } - } - - redo_VIsual_busy = false; - - /* - * Switch Visual off now, so screen updating does - * not show inverted text when the screen is redrawn. - * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is - * no screen redraw, so it is done here to remove the inverted - * part. - */ - if (!gui_yank) { - VIsual_active = false; - setmouse(); - mouse_dragging = 0; - may_clear_cmdline(); - if ((oap->op_type == OP_YANK - || oap->op_type == OP_COLON - || oap->op_type == OP_FUNCTION - || oap->op_type == OP_FILTER) - && oap->motion_force == NUL) { - // Make sure redrawing is correct. - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - } - } - - // Include the trailing byte of a multi-byte char. - if (oap->inclusive) { - const int l = utfc_ptr2len(ml_get_pos(&oap->end)); - if (l > 1) { - oap->end.col += l - 1; - } - } - curwin->w_set_curswant = true; - - /* - * oap->empty is set when start and end are the same. The inclusive - * flag affects this too, unless yanking and the end is on a NUL. - */ - oap->empty = (oap->motion_type != kMTLineWise - && (!oap->inclusive - || (oap->op_type == OP_YANK - && gchar_pos(&oap->end) == NUL)) - && equalpos(oap->start, oap->end) - && !(virtual_op && oap->start.coladd != oap->end.coladd) - ); - /* - * For delete, change and yank, it's an error to operate on an - * empty region, when 'E' included in 'cpoptions' (Vi compatible). - */ - empty_region_error = (oap->empty - && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); - - /* Force a redraw when operating on an empty Visual region, when - * 'modifiable is off or creating a fold. */ - if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) - || oap->op_type == OP_FOLD - )) { - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - - /* - * If the end of an operator is in column one while oap->motion_type - * is kMTCharWise and oap->inclusive is false, we put op_end after the last - * character in the previous line. If op_start is on or before the - * first non-blank in the line, the operator becomes linewise - * (strange, but that's the way vi does it). - */ - if (oap->motion_type == kMTCharWise - && oap->inclusive == false - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && oap->line_count > 1) { - oap->end_adjusted = true; // remember that we did this - oap->line_count--; - oap->end.lnum--; - if (inindent(0)) { - oap->motion_type = kMTLineWise; - } else { - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) { - --oap->end.col; - oap->inclusive = true; - } - } - } else { - oap->end_adjusted = false; - } - - switch (oap->op_type) { - case OP_LSHIFT: - case OP_RSHIFT: - op_shift(oap, true, - oap->is_VIsual ? (int)cap->count1 : - 1); - auto_format(false, true); - break; - - case OP_JOIN_NS: - case OP_JOIN: - if (oap->line_count < 2) { - oap->line_count = 2; - } - if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) { - beep_flush(); - } else { - do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, - true, true, true); - auto_format(false, true); - } - break; - - case OP_DELETE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - (void)op_delete(oap); - // save cursor line for undo if it wasn't saved yet - if (oap->motion_type == kMTLineWise - && has_format_option(FO_AUTO) - && u_save_cursor() == OK) { - auto_format(false, true); - } - } - break; - - case OP_YANK: - if (empty_region_error) { - if (!gui_yank) { - vim_beep(BO_OPER); - CancelRedo(); - } - } else { - curwin->w_p_lbr = lbr_saved; - oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); - } - check_cursor_col(); - break; - - case OP_CHANGE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once and not when typed and - * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) { - restart_edit_save = restart_edit; - } else { - restart_edit_save = 0; - } - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - // Reset finish_op now, don't want it set inside edit(). - finish_op = false; - if (op_change(oap)) { // will call edit() - cap->retval |= CA_COMMAND_BUSY; - } - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } - } - break; - - case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { - AppendToRedobuff("!\r"); // Use any last used !cmd. - } else { - bangredo = true; // do_bang() will put cmd in redo buffer. - } - FALLTHROUGH; - - case OP_INDENT: - case OP_COLON: - - /* - * If 'equalprg' is empty, do the indenting internally. - */ - if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { - if (curbuf->b_p_lisp) { - op_reindent(oap, get_lisp_indent); - break; - } - op_reindent(oap, - *curbuf->b_p_inde != NUL ? get_expr_indent : - get_c_indent); - break; - } - - op_colon(oap); - break; - - case OP_TILDE: - case OP_UPPER: - case OP_LOWER: - case OP_ROT13: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - op_tilde(oap); - } - check_cursor_col(); - break; - - case OP_FORMAT: - if (*curbuf->b_p_fex != NUL) { - op_formatexpr(oap); // use expression - } else { - if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { - op_colon(oap); // use external command - } else { - op_format(oap, false); // use internal function - } - } - break; - - case OP_FORMAT2: - op_format(oap, true); // use internal function - break; - - case OP_FUNCTION: - // Restore linebreak, so that when the user edits it looks as - // before. - curwin->w_p_lbr = lbr_saved; - op_function(oap); // call 'operatorfunc' - break; - - case OP_INSERT: - case OP_APPEND: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once. */ - restart_edit_save = restart_edit; - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_insert(oap, cap->count1); - - // Reset linebreak, so that formatting works correctly. - curwin->w_p_lbr = false; - - /* TODO: when inserting in several lines, should format all - * the lines. */ - auto_format(false, true); - - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } else { - cap->retval |= CA_COMMAND_BUSY; - } - } - break; - - case OP_REPLACE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_replace(oap, cap->nchar); - } - break; - - case OP_FOLD: - VIsual_reselect = false; // don't reselect now - foldCreate(curwin, oap->start, oap->end); - break; - - case OP_FOLDOPEN: - case OP_FOLDOPENREC: - case OP_FOLDCLOSE: - case OP_FOLDCLOSEREC: - VIsual_reselect = false; // don't reselect now - opFoldRange(oap->start, oap->end, - oap->op_type == OP_FOLDOPEN - || oap->op_type == OP_FOLDOPENREC, - oap->op_type == OP_FOLDOPENREC - || oap->op_type == OP_FOLDCLOSEREC, - oap->is_VIsual); - break; - - case OP_FOLDDEL: - case OP_FOLDDELREC: - VIsual_reselect = false; // don't reselect now - deleteFold(curwin, oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDDELREC, oap->is_VIsual); - break; - - case OP_NR_ADD: - case OP_NR_SUB: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - VIsual_active = true; - curwin->w_p_lbr = lbr_saved; - op_addsub(oap, cap->count1, redo_VIsual_arg); - VIsual_active = false; - } - check_cursor_col(); - break; - default: - clearopbeep(oap); - } - virtual_op = kNone; - if (!gui_yank) { - /* - * if 'sol' not set, go back to old column for some commands - */ - if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted - && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT - || oap->op_type == OP_DELETE)) { - curwin->w_p_lbr = false; - coladvance(curwin->w_curswant = old_col); - } - } else { - curwin->w_cursor = old_cursor; - } - clearop(oap); - motion_force = NUL; - } - curwin->w_p_lbr = lbr_saved; -} - -/* - * Handle indent and format operators and visual mode ":". - */ -static void op_colon(oparg_T *oap) -{ - stuffcharReadbuff(':'); - if (oap->is_VIsual) { - stuffReadbuff("'<,'>"); - } else { - // Make the range look nice, so it can be repeated. - if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else { - stuffnumReadbuff((long)oap->start.lnum); - } - if (oap->end.lnum != oap->start.lnum) { - stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { - stuffcharReadbuff('$'); - } else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff(".+"); - stuffnumReadbuff(oap->line_count - 1); - } else { - stuffnumReadbuff((long)oap->end.lnum); - } - } - } - if (oap->op_type != OP_COLON) { - stuffReadbuff("!"); - } - if (oap->op_type == OP_INDENT) { - stuffReadbuff((const char *)get_equalprg()); - stuffReadbuff("\n"); - } else if (oap->op_type == OP_FORMAT) { - if (*curbuf->b_p_fp != NUL) { - stuffReadbuff((const char *)curbuf->b_p_fp); - } else if (*p_fp != NUL) { - stuffReadbuff((const char *)p_fp); - } else { - stuffReadbuff("fmt"); - } - stuffReadbuff("\n']"); - } - - /* - * do_cmdline() does the rest - */ -} - -/* - * Handle the "g@" operator: call 'operatorfunc'. - */ -static void op_function(const oparg_T *oap) - FUNC_ATTR_NONNULL_ALL -{ - const TriState save_virtual_op = virtual_op; - const bool save_finish_op = finish_op; - - if (*p_opfunc == NUL) { - emsg(_("E774: 'operatorfunc' is empty")); - } else { - // Set '[ and '] marks to text to be operated on. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (oap->motion_type != kMTLineWise && !oap->inclusive) { - // Exclude the end position. - decl(&curbuf->b_op_end); - } - - typval_T argv[2]; - argv[0].v_type = VAR_STRING; - argv[1].v_type = VAR_UNKNOWN; - argv[0].vval.v_string = - (char_u *)(((const char *const[]) { - [kMTBlockWise] = "block", - [kMTLineWise] = "line", - [kMTCharWise] = "char", - })[oap->motion_type]); - - // Reset virtual_op so that 'virtualedit' can be changed in the - // function. - virtual_op = kNone; - - // Reset finish_op so that mode() returns the right value. - finish_op = false; - - (void)call_func_retnr(p_opfunc, 1, argv); - - virtual_op = save_virtual_op; - finish_op = save_finish_op; - } -} - // Move the current tab to tab in same column as mouse or to end of the // tabline if there is no tab there. static void move_tab_to_mouse(void) @@ -3066,6 +2311,7 @@ void end_visual_mode(void) may_clear_cmdline(); adjust_cursor_eol(); + trigger_modechanged(); } /* @@ -3092,6 +2338,14 @@ void reset_VIsual(void) } } +void restore_visual_mode(void) +{ + if (VIsual_mode_orig != NUL) { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } +} + // Check for a balloon-eval special item to include when searching for an // identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! // Returns true if the character at "*ptr" should be included. @@ -3270,7 +2524,7 @@ static void prep_redo_cmd(cmdarg_T *cap) * Prepare for redo of any command. * Note that only the last argument can be a multi-byte char. */ -static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); if (regname != 0) { // yank from specified buffer @@ -3327,7 +2581,7 @@ static bool checkclearopq(oparg_T *oap) return true; } -static void clearop(oparg_T *oap) +void clearop(oparg_T *oap) { oap->op_type = OP_NOP; oap->regname = 0; @@ -3336,7 +2590,7 @@ static void clearop(oparg_T *oap) motion_force = NUL; } -static void clearopbeep(oparg_T *oap) +void clearopbeep(oparg_T *oap) { clearop(oap); beep_flush(); @@ -3366,7 +2620,7 @@ static void unshift_special(cmdarg_T *cap) /// If the mode is currently displayed clear the command line or update the /// command displayed. -static void may_clear_cmdline(void) +void may_clear_cmdline(void) { if (mode_displayed) { // unshow visual mode later @@ -4851,6 +4105,7 @@ static void nv_ctrlg(cmdarg_T *cap) { if (VIsual_active) { // toggle Selection/Visual mode VIsual_select = !VIsual_select; + trigger_modechanged(); showmode(); } else if (!checkclearop(cap->oap)) { // print full name if count given or :cd used @@ -4894,6 +4149,7 @@ static void nv_ctrlo(cmdarg_T *cap) { if (VIsual_active && VIsual_select) { VIsual_select = false; + trigger_modechanged(); showmode(); restart_VIsual_select = 2; // restart Select mode later } else { @@ -6680,6 +5936,7 @@ static void nv_visual(cmdarg_T *cap) // or char/line mode VIsual_mode = cap->cmdchar; showmode(); + trigger_modechanged(); } redraw_curbuf_later(INVERTED); // update the inversion } else { // start Visual mode @@ -6793,6 +6050,7 @@ static void n_start_visual_mode(int c) foldAdjustVisual(); + trigger_modechanged(); setmouse(); // Check for redraw after changing the state. conceal_check_cursor_line(); @@ -7730,7 +6988,7 @@ static void adjust_for_sel(cmdarg_T *cap) * Should check VIsual_mode before calling this. * Returns true when backed up to the previous line. */ -static bool unadjust_for_sel(void) +bool unadjust_for_sel(void) { pos_T *pp; @@ -8320,71 +7578,6 @@ static void nv_open(cmdarg_T *cap) } } -/// Calculate start/end virtual columns for operating in block mode. -/// -/// @param initial when true: adjust position for 'selectmode' -static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) -{ - colnr_T start; - colnr_T end; - - if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width_inner)) { - return; - } - - oap->motion_type = kMTBlockWise; - - // prevent from moving onto a trail byte - mark_mb_adjustpos(curwin->w_buffer, &oap->end); - - getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - if (!redo_VIsual_busy) { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) { - oap->start_vcol = start; - } - if (end > oap->end_vcol) { - if (initial && *p_sel == 'e' - && start >= 1 - && start - 1 >= oap->end_vcol) { - oap->end_vcol = start - 1; - } else { - oap->end_vcol = end; - } - } - } - - // if '$' was used, get oap->end_vcol from longest line - if (curwin->w_curswant == MAXCOL) { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) { - oap->end_vcol = end; - } - } - } else if (redo_VIsual_busy) { - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - } - - // Correct oap->end.col and oap->start.col to be the - // upper-left and lower-right corner of the block area. - // - // (Actually, this does convert column positions into character - // positions) - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; -} - // Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index cbfed5daa5..52c382028e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -27,7 +27,9 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -37,6 +39,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -1768,7 +1771,7 @@ static void replace_character(int c) /* * Replace a whole area with one character. */ -int op_replace(oparg_T *oap, int c) +static int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; @@ -2162,7 +2165,8 @@ void op_insert(oparg_T *oap, long count1) { long ins_len, pre_textlen = 0; char_u *firstline, *ins_text; - colnr_T ind_pre = 0; + colnr_T ind_pre_col = 0, ind_post_col; + int ind_pre_vcol = 0, ind_post_vcol = 0; struct block_def bd; int i; pos_T t1; @@ -2196,7 +2200,8 @@ void op_insert(oparg_T *oap, long count1) // Get the info about the block before entering the text block_prep(oap, &bd, oap->start.lnum, true); // Get indent information - ind_pre = (colnr_T)getwhitecols_curline(); + ind_pre_col = (colnr_T)getwhitecols_curline(); + ind_pre_vcol = get_indent(); firstline = ml_get(oap->start.lnum) + bd.textcol; if (oap->op_type == OP_APPEND) { @@ -2261,10 +2266,11 @@ void op_insert(oparg_T *oap, long count1) // if indent kicked in, the firstline might have changed // but only do that, if the indent actually increased - const colnr_T ind_post = (colnr_T)getwhitecols_curline(); - if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre) { - bd.textcol += ind_post - ind_pre; - bd.start_vcol += ind_post - ind_pre; + ind_post_col = (colnr_T)getwhitecols_curline(); + if (curbuf->b_op_start.col > ind_pre_col && ind_post_col > ind_pre_col) { + bd.textcol += ind_post_col - ind_pre_col; + ind_post_vcol = get_indent(); + bd.start_vcol += ind_post_vcol - ind_pre_vcol; did_indent = true; } @@ -2297,12 +2303,26 @@ void op_insert(oparg_T *oap, long count1) } } - /* - * Spaces and tabs in the indent may have changed to other spaces and - * tabs. Get the starting column again and correct the length. - * Don't do this when "$" used, end-of-line will have changed. - */ + // Spaces and tabs in the indent may have changed to other spaces and + // tabs. Get the starting column again and correct the length. + // Don't do this when "$" used, end-of-line will have changed. + // + // if indent was added and the inserted text was after the indent, + // correct the selection for the new indent. + if (did_indent && bd.textcol - ind_post_col > 0) { + oap->start.col += ind_post_col - ind_pre_col; + oap->start_vcol += ind_post_vcol - ind_pre_vcol; + oap->end.col += ind_post_col - ind_pre_col; + oap->end_vcol += ind_post_vcol - ind_pre_vcol; + } block_prep(oap, &bd2, oap->start.lnum, true); + if (did_indent && bd.textcol - ind_post_col > 0) { + // undo for where "oap" is used below + oap->start.col -= ind_post_col - ind_pre_col; + oap->start_vcol -= ind_post_vcol - ind_pre_vcol; + oap->end.col -= ind_post_col - ind_pre_col; + oap->end_vcol -= ind_post_vcol - ind_pre_vcol; + } if (!bd.is_MAX || bd2.textlen < bd.textlen) { if (oap->op_type == OP_APPEND) { pre_textlen += bd2.textlen - bd.textlen; @@ -2792,8 +2812,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) recursive = true; + save_v_event_T save_v_event; // Set the v:event dictionary with information about the yank. - dict_T *dict = get_vim_var_dict(VV_EVENT); + dict_T *dict = get_v_event(&save_v_event); // The yanked text contents. list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); @@ -2830,7 +2851,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); recursive = false; } @@ -4159,7 +4180,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in /// Implementation of the format operator 'gq'. /// /// @param keep_cursor keep cursor on same text char -void op_format(oparg_T *oap, int keep_cursor) +static void op_format(oparg_T *oap, int keep_cursor) { long old_line_count = curbuf->b_ml.ml_line_count; @@ -4227,7 +4248,7 @@ void op_format(oparg_T *oap, int keep_cursor) /* * Implementation of the format operator 'gq' for when using 'formatexpr'. */ -void op_formatexpr(oparg_T *oap) +static void op_formatexpr(oparg_T *oap) { if (oap->is_VIsual) { // When there is no change: need to remove the Visual selection @@ -5918,6 +5939,791 @@ void cursor_pos_info(dict_T *dict) } } +// Handle indent and format operators and visual mode ":". +static void op_colon(oparg_T *oap) +{ + stuffcharReadbuff(':'); + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else { + stuffnumReadbuff((long)oap->start.lnum); + } + if (oap->end.lnum != oap->start.lnum) { + stuffcharReadbuff(','); + if (oap->end.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { + stuffcharReadbuff('$'); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); + stuffnumReadbuff(oap->line_count - 1); + } else { + stuffnumReadbuff((long)oap->end.lnum); + } + } + } + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } + if (oap->op_type == OP_INDENT) { + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); + } else if (oap->op_type == OP_FORMAT) { + if (*curbuf->b_p_fp != NUL) { + stuffReadbuff((const char *)curbuf->b_p_fp); + } else if (*p_fp != NUL) { + stuffReadbuff((const char *)p_fp); + } else { + stuffReadbuff("fmt"); + } + stuffReadbuff("\n']"); + } + + // do_cmdline() does the rest +} + +// Handle the "g@" operator: call 'operatorfunc'. +static void op_function(const oparg_T *oap) + FUNC_ATTR_NONNULL_ALL +{ + const TriState save_virtual_op = virtual_op; + const bool save_finish_op = finish_op; + + if (*p_opfunc == NUL) { + emsg(_("E774: 'operatorfunc' is empty")); + } else { + // Set '[ and '] marks to text to be operated on. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. + decl(&curbuf->b_op_end); + } + + typval_T argv[2]; + argv[0].v_type = VAR_STRING; + argv[1].v_type = VAR_UNKNOWN; + argv[0].vval.v_string = + (char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]); + + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. + virtual_op = kNone; + + // Reset finish_op so that mode() returns the right value. + finish_op = false; + + (void)call_func_retnr(p_opfunc, 1, argv); + + virtual_op = save_virtual_op; + finish_op = save_finish_op; + } +} + +/// Calculate start/end virtual columns for operating in block mode. +/// +/// @param initial when true: adjust position for 'selectmode' +static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) +{ + colnr_T start; + colnr_T end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width_inner)) { + return; + } + + oap->motion_type = kMTBlockWise; + + // prevent from moving onto a trail byte + mark_mb_adjustpos(curwin->w_buffer, &oap->end); + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) { + oap->end_vcol = end; + } + } + } else if (redo_VIsual_busy) { + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + } + + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + +// Handle an operator after Visual mode or when the movement is finished. +// "gui_yank" is true when yanking text for the clipboard. +void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + bool empty_region_error; + int restart_edit_save; + int lbr_saved = curwin->w_p_lbr; + + + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument + bool include_line_break = false; + + old_cursor = curwin->w_cursor; + + // If an operation is pending, handle it... + if ((finish_op + || VIsual_active) + && oap->op_type != OP_NOP) { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + + // Avoid a problem with unwanted linebreaks in block mode + if (curwin->w_p_lbr) { + curwin->w_valid &= ~VALID_VIRTCOL; + } + curwin->w_p_lbr = false; + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { + oap->inclusive = false; + } else if (oap->motion_type == kMTCharWise) { + // If the motion already was charwise, toggle "inclusive" + oap->inclusive = !oap->inclusive; + } + oap->motion_type = kMTCharWise; + } else if (oap->motion_force == Ctrl_V) { + // Change line- or charwise motion into Visual block mode. + if (!VIsual_active) { + VIsual_active = true; + VIsual = oap->start; + } + VIsual_mode = Ctrl_V; + VIsual_select = false; + VIsual_reselect = false; + } + + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + // Also redo Operator-pending Visual mode mappings. + || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search + // If 'cpoptions' does not contain 'r', insert the search + // pattern to really repeat the same command. + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { + AppendToRedobuffLit(cap->searchbuf, -1); + } + AppendToRedobuff(NL_STR); + } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) { + ResetRedobuff(); + } else { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + XFREE_CLEAR(repeat_cmdline); + } + } + } + + if (redo_VIsual_busy) { + // Redo of an operation on a Visual area. Use the same size from + // redo_VIsual_line_count and redo_VIsual_vcol. + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { + if (VIsual_mode == 'v') { + if (redo_VIsual_line_count <= 1) { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } else { + curwin->w_curswant = redo_VIsual_vcol; + } + } else { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); + } else if (VIsual_active) { + if (!gui_yank) { + // Save the current VIsual area for '< and '> marks, and "gv" + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + restore_visual_mode(); + curbuf->b_visual.vi_curswant = curwin->w_curswant; + curbuf->b_visual_mode_eval = VIsual_mode; + } + + // In Select mode, a linewise selection is operated upon like a + // charwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { + if (lt(VIsual, curwin->w_cursor)) { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } else { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } else if (VIsual_mode == 'v') { + // If 'selection' is "exclusive", backup one character for + // charwise selections. + include_line_break = + unadjust_for_sel(); + } + + oap->start = VIsual; + if (VIsual_mode == 'V') { + oap->start.col = 0; + oap->start.coladd = 0; + } + } + + // Set oap->start to the first position of the operated text, oap->end + // to the end of the operated text. w_cursor is equal to oap->start. + if (lt(oap->start, curwin->w_cursor)) { + // Include folded lines completely. + if (!VIsual_active) { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + oap->start.col = 0; + } + if ((curwin->w_cursor.col > 0 + || oap->inclusive + || oap->motion_type == kMTLineWise) + && hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) { + curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } + } + oap->end = curwin->w_cursor; + curwin->w_cursor = oap->start; + + // w_virtcol may have been updated; if the cursor goes back to its + // previous position w_virtcol becomes invalid and isn't updated + // automatically. + curwin->w_valid &= ~VALID_VIRTCOL; + } else { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) { + curwin->w_cursor.col = 0; + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } + } + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + // Set "virtual_op" before resetting VIsual_active. + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) { + get_op_vcol(oap, redo_VIsual_vcol, true); + + if (!redo_VIsual_busy && !gui_yank) { + // Prepare to reselect and redo Visual: this is based on the + // size of the Visual text + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) { + resel_VIsual_vcol = MAXCOL; + } else { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + } + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + } + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } else { + resel_VIsual_vcol = oap->end_vcol; + } + } + resel_VIsual_line_count = oap->line_count; + } + + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + && oap->motion_force == NUL) { + // Prepare for redoing. Only use the nchar field for "r", + // otherwise it might be the second char of the operator. + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) { + nchar = CAR; + } else if (nchar == REPLACE_NL_NCHAR) { + nchar = NL; + } + prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), nchar); + } + if (!redo_VIsual_busy) { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; + } + } + + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { + oap->inclusive = true; + } + if (VIsual_mode == 'V') { + oap->motion_type = kMTLineWise; + } else if (VIsual_mode == 'v') { + oap->motion_type = kMTCharWise; + if (*ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op)) { + oap->inclusive = false; + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; + } + } + } + + redo_VIsual_busy = false; + + // Switch Visual off now, so screen updating does + // not show inverted text when the screen is redrawn. + // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + // no screen redraw, so it is done here to remove the inverted + // part. + if (!gui_yank) { + VIsual_active = false; + setmouse(); + mouse_dragging = 0; + may_clear_cmdline(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) { + // Make sure redrawing is correct. + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + } + } + + // Include the trailing byte of a multi-byte char. + if (oap->inclusive) { + const int l = utfc_ptr2len(ml_get_pos(&oap->end)); + if (l > 1) { + oap->end.col += l - 1; + } + } + curwin->w_set_curswant = true; + + // oap->empty is set when start and end are the same. The inclusive + // flag affects this too, unless yanking and the end is on a NUL. + oap->empty = (oap->motion_type != kMTLineWise + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && equalpos(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd)); + // For delete, change and yank, it's an error to operate on an + // empty region, when 'E' included in 'cpoptions' (Vi compatible). + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + // Force a redraw when operating on an empty Visual region, when + // 'modifiable is off or creating a fold. + if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) + || oap->op_type == OP_FOLD)) { + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + + // If the end of an operator is in column one while oap->motion_type + // is kMTCharWise and oap->inclusive is false, we put op_end after the last + // character in the previous line. If op_start is on or before the + // first non-blank in the line, the operator becomes linewise + // (strange, but that's the way vi does it). + if (oap->motion_type == kMTCharWise + && oap->inclusive == false + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && oap->line_count > 1) { + oap->end_adjusted = true; // remember that we did this + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) { + oap->end.col--; + oap->inclusive = true; + } + } + } else { + oap->end_adjusted = false; + } + + switch (oap->op_type) { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, true, + oap->is_VIsual ? (int)cap->count1 : + 1); + auto_format(false, true); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) { + oap->line_count = 2; + } + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) { + beep_flush(); + } else { + do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, + true, true, true); + auto_format(false, true); + } + break; + + case OP_DELETE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + (void)op_delete(oap); + // save cursor line for undo if it wasn't saved yet + if (oap->motion_type == kMTLineWise + && has_format_option(FO_AUTO) + && u_save_cursor() == OK) { + auto_format(false, true); + } + } + break; + + case OP_YANK: + if (empty_region_error) { + if (!gui_yank) { + vim_beep(BO_OPER); + CancelRedo(); + } + } else { + curwin->w_p_lbr = lbr_saved; + oap->excl_tr_ws = cap->cmdchar == 'z'; + (void)op_yank(oap, !gui_yank, false); + } + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once and not when typed and + // 'insertmode' isn't set. + if (p_im || !KeyTyped) { + restart_edit_save = restart_edit; + } else { + restart_edit_save = 0; + } + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + // Reset finish_op now, don't want it set inside edit(). + finish_op = false; + if (op_change(oap)) { // will call edit() + cap->retval |= CA_COMMAND_BUSY; + } + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } + FALLTHROUGH; + + case OP_INDENT: + case OP_COLON: + + // If 'equalprg' is empty, do the indenting internally. + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { + if (curbuf->b_p_lisp) { + op_reindent(oap, get_lisp_indent); + break; + } + op_reindent(oap, + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); + break; + } + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + op_tilde(oap); + } + check_cursor_col(); + break; + + case OP_FORMAT: + if (*curbuf->b_p_fex != NUL) { + op_formatexpr(oap); // use expression + } else { + if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { + op_colon(oap); // use external command + } else { + op_format(oap, false); // use internal function + } + } + break; + + case OP_FORMAT2: + op_format(oap, true); // use internal function + break; + + case OP_FUNCTION: + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; + op_function(oap); // call 'operatorfunc' + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once. + restart_edit_save = restart_edit; + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_insert(oap, cap->count1); + + // Reset linebreak, so that formatting works correctly. + curwin->w_p_lbr = false; + + // TODO(brammool): when inserting in several lines, should format all + // the lines. + auto_format(false, true); + + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } else { + cap->retval |= CA_COMMAND_BUSY; + } + } + break; + + case OP_REPLACE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_replace(oap, cap->nchar); + } + break; + + case OP_FOLD: + VIsual_reselect = false; // don't reselect now + foldCreate(curwin, oap->start, oap->end); + break; + + case OP_FOLDOPEN: + case OP_FOLDOPENREC: + case OP_FOLDCLOSE: + case OP_FOLDCLOSEREC: + VIsual_reselect = false; // don't reselect now + opFoldRange(oap->start, oap->end, + oap->op_type == OP_FOLDOPEN + || oap->op_type == OP_FOLDOPENREC, + oap->op_type == OP_FOLDOPENREC + || oap->op_type == OP_FOLDCLOSEREC, + oap->is_VIsual); + break; + + case OP_FOLDDEL: + case OP_FOLDDELREC: + VIsual_reselect = false; // don't reselect now + deleteFold(curwin, oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDDELREC, oap->is_VIsual); + break; + + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; + default: + clearopbeep(oap); + } + virtual_op = kNone; + if (!gui_yank) { + // if 'sol' not set, go back to old column for some commands + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted + && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT + || oap->op_type == OP_DELETE)) { + curwin->w_p_lbr = false; + coladvance(curwin->w_curswant = old_col); + } + } else { + curwin->w_cursor = old_cursor; + } + clearop(oap); + motion_force = NUL; + } + curwin->w_p_lbr = lbr_saved; +} + /// Check if the default register (used in an unnamed paste) should be a /// clipboard register. This happens when `clipboard=unnamed[plus]` is set /// and a provider is available. diff --git a/src/nvim/option.c b/src/nvim/option.c index 45e2032b35..1fe2e1d04c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1301,7 +1301,11 @@ int do_set(char_u *arg, int opt_flags) char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; + char_u *origval_l = NULL; + char_u *origval_g = NULL; char *saved_origval = NULL; + char *saved_origval_l = NULL; + char *saved_origval_g = NULL; char *saved_newval = NULL; unsigned newlen; int comma; @@ -1319,10 +1323,21 @@ int do_set(char_u *arg, int opt_flags) // new value is valid. oldval = *(char_u **)varp; + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + + // A global-local string option might have an empty + // option as value to indicate that the global + // value should be used. + if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) { + origval_l = origval_g; + } + } + // When setting the local value of a global // option, the old value may be the global value. - if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags - & OPT_LOCAL)) { + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) { origval = *(char_u **)get_varp(&options[opt_idx]); } else { origval = oldval; @@ -1388,6 +1403,12 @@ int do_set(char_u *arg, int opt_flags) if (origval == oldval) { origval = *(char_u **)varp; } + if (origval_l == oldval) { + origval_l = *(char_u **)varp; + } + if (origval_g == oldval) { + origval_g = *(char_u **)varp; + } oldval = *(char_u **)varp; } /* @@ -1596,6 +1617,8 @@ int do_set(char_u *arg, int opt_flags) // origval may be freed by // did_set_string_option(), make a copy. saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0; + saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0; + saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0; // newval (and varp) may become invalid if the // buffer is closed by autocommands. @@ -1630,8 +1653,8 @@ int do_set(char_u *arg, int opt_flags) if (errmsg == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_origval, - saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l, + saved_origval_g, saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -1639,6 +1662,8 @@ int do_set(char_u *arg, int opt_flags) } } xfree(saved_origval); + xfree(saved_origval_l); + xfree(saved_origval_g); xfree(saved_newval); // If error detected, print the error message. @@ -2233,9 +2258,19 @@ static char *set_string_option(const int opt_idx, const char *const value, const ? OPT_GLOBAL : OPT_LOCAL) : opt_flags)); char *const oldval = *varp; + char *oldval_l = NULL; + char *oldval_g = NULL; + + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *varp = s; char *const saved_oldval = xstrdup(oldval); + char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0; + char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0; char *const saved_newval = xstrdup(s); int value_checked = false; @@ -2249,7 +2284,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const // call autocommand after handling side effects if (r == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g, + saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -2257,6 +2293,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const } } xfree(saved_oldval); + xfree(saved_oldval_l); + xfree(saved_oldval_g); xfree(saved_newval); return r; @@ -3851,6 +3889,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va const int opt_flags) { int old_value = *(int *)varp; + int old_global_value = 0; // Disallow changing some options from secure mode if ((secure || sandbox != 0) @@ -3858,6 +3897,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va return (char *)e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" in fact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *(int *)varp = value; // set the new value // Remember where the option was set. set_option_sctx_idx(opt_idx, opt_flags, current_sctx); @@ -4134,20 +4180,35 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va // Don't do this while starting up or recursively. if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[2]; + char buf_old_global[2]; char buf_new[2]; char buf_type[7]; - vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", - old_value ? true: false); - vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", - value ? true: false); + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4181,7 +4242,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, { char *errmsg = NULL; long old_value = *(long *)varp; - long old_Rows = Rows; // remember old Rows + long old_global_value = 0; // only used when setting a local and global option + long old_Rows = Rows; // remember old Rows long *pp = (long *)varp; // Disallow changing some options from secure mode. @@ -4190,6 +4252,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" infact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { return e_invarg; @@ -4534,19 +4603,36 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Don't do this while starting up, failure or recursively. if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[NUMBUFLEN]; + char buf_old_global[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4565,7 +4651,15 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return (char *)errmsg; } -static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval) +/// Trigger the OptionSet autocommand. +/// "opt_idx" is the index of the option being set. +/// "opt_flags" can be OPT_LOCAL etc. +/// "oldval" the old value +/// "oldval_l" the old local value (only non-NULL if global and local value are set) +/// "oldval_g" the old global value (only non-NULL if global and local value are set) +/// "newval" the new value +static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l, + char *oldval_g, char *newval) { // Don't do this recursively. if (oldval != NULL @@ -4578,8 +4672,24 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, set_vim_var_string(VV_OPTION_OLD, oldval, -1); set_vim_var_string(VV_OPTION_NEW, newval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } } @@ -4771,7 +4881,8 @@ static int findoption(const char *const arg) /// @param stringval NULL when only checking existence /// /// @returns: -/// Number or Toggle option: 1, *numval gets value. +/// Toggle option: 2, *numval gets value. +/// Number option: 1, *numval gets value. /// String option: 0, *stringval gets allocated string. /// Hidden Number or Toggle option: -1. /// hidden String option: -2. @@ -4804,16 +4915,18 @@ int get_option_value(const char *name, long *numval, char_u **stringval, int opt } if (options[opt_idx].flags & P_NUM) { *numval = *(long *)varp; + return 1; + } + + // Special case: 'modified' is b_changed, but we also want to consider + // it set when 'ff' or 'fenc' changed. + if ((int *)varp == &curbuf->b_changed) { + *numval = curbufIsChanged(); } else { - // Special case: 'modified' is b_changed, but we also want to consider - // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); - } else { - *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) - } + *numval = (long)*(int *)varp; // NOLINT(whitespace/cast) } - return 1; + + return 2; } // Returns the option attributes and its value. Unlike the above function it @@ -4909,7 +5022,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o // only getting a pointer, no need to use aucmd_prepbuf() curbuf = (buf_T *)from; curwin->w_buffer = curbuf; - varp = get_varp(p); + varp = get_varp_scope(p, OPT_LOCAL); curbuf = save_curbuf; curwin->w_buffer = curbuf; } @@ -4917,7 +5030,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o win_T *save_curwin = curwin; curwin = (win_T *)from; curbuf = curwin->w_buffer; - varp = get_varp(p); + varp = get_varp_scope(p, OPT_LOCAL); curwin = save_curwin; curbuf = curwin->w_buffer; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index bec3bc9648..e67efb8ea0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1760,13 +1760,14 @@ static char_u *regpiece(int *flagp) break; } if (re_multi_type(peekchr()) != NOT_MULTI) { - /* Can't have a multi follow a multi. */ - if (peekchr() == Magic('*')) - sprintf((char *)IObuff, _("E61: Nested %s*"), - reg_magic >= MAGIC_ON ? "" : "\\"); - else - sprintf((char *)IObuff, _("E62: Nested %s%c"), - reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + // Can't have a multi follow a multi. + if (peekchr() == Magic('*')) { + snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"), + reg_magic >= MAGIC_ON ? "" : "\\"); + } else { + snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"), + reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + } EMSG_RET_NULL((char *)IObuff); } @@ -1926,11 +1927,11 @@ static char_u *regatom(int *flagp) case Magic('{'): case Magic('*'): c = no_Magic(c); - sprintf((char *)IObuff, _("E64: %s%c follows nothing"), - (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) - ? "" : "\\", c); + snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) + ? "" : "\\", c); EMSG_RET_NULL((char *)IObuff); - /* NOTREACHED */ + // NOTREACHED case Magic('~'): /* previous substitute pattern */ if (reg_prev_sub != NULL) { @@ -3152,8 +3153,8 @@ static int read_limits(long *minval, long *maxval) regparse++; // Allow either \{...} or \{...\} } if (*regparse != '}') { - sprintf((char *)IObuff, _("E554: Syntax error in %s{...}"), - reg_magic == MAGIC_ALL ? "" : "\\"); + snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"), + reg_magic == MAGIC_ALL ? "" : "\\"); EMSG_RET_FAIL((char *)IObuff); } @@ -7263,9 +7264,10 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags) if (f) { fprintf(f, "Syntax error in \"%s\"\n", expr); fclose(f); - } else + } else { semsg("(NFA) Could not open \"%s\" to write !!!", - BT_REGEXP_DEBUG_LOG_NAME); + BT_REGEXP_DEBUG_LOG_NAME); + } } #endif // If the NFA engine failed, try the backtracking engine. The NFA engine diff --git a/src/nvim/search.c b/src/nvim/search.c index 2e45a8f509..f47315705c 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -5248,6 +5248,9 @@ search_line: if (depth == -1) { // match in current file if (l_g_do_tagpreview != 0) { + if (!win_valid(curwin_save)) { + break; + } if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, NULL, true, lnum, false))) { break; // failed to jump to file diff --git a/src/nvim/sign.c b/src/nvim/sign.c index d8eea7f942..fca73dc9f2 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -80,7 +80,7 @@ static signgroup_T *sign_group_ref(const char_u *groupname) hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group - group = xmalloc((unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + group = xmalloc(sizeof(signgroup_T) + STRLEN(groupname)); STRCPY(group->sg_name, groupname); group->sg_refcount = 1; diff --git a/src/nvim/state.c b/src/nvim/state.c index 4eb0073873..71db25664f 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -136,7 +136,7 @@ int get_real_state(void) /// @returns[allocated] mode string char *get_mode(void) { - char *buf = xcalloc(4, sizeof(char)); + char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char)); if (VIsual_active) { if (VIsual_select) { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index dd3f1b4dc9..cb243668ce 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5401,7 +5401,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis do { for (end = p; *end && !ascii_iswhite(*end) && *end != ','; end++) { } - char_u *const name = xmalloc((int)(end - p + 3)); // leave room for "^$" + char_u *const name = xmalloc(end - p + 3); // leave room for "^$" STRLCPY(name + 1, p, end - p + 1); if (STRCMP(name + 1, "ALLBUT") == 0 || STRCMP(name + 1, "ALL") == 0 @@ -7573,7 +7573,7 @@ static bool syn_list_header(const bool did_header, const int outlen, const int i // Show "xxx" with the attributes. if (!did_header) { if (endcol == Columns - 1 && endcol <= name_col) { - msg_putchar(' '); + msg_putchar(' '); } msg_puts_attr("xxx", syn_id2attr(id)); msg_putchar(' '); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 1f4d3adc92..483d2df778 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -141,12 +141,12 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc /// type == DT_LTAG: use location list for displaying tag matches /// type == DT_FREE: free cached matches /// -/// for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise +/// for cscope, returns true if we jumped to tag or aborted, false otherwise /// /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message -int do_tag(char_u *tag, int type, int count, int forceit, int verbose) +bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -163,7 +163,7 @@ int do_tag(char_u *tag, int type, int count, int forceit, int verbose) int error_cur_match = 0; int save_pos = false; fmark_T saved_fmark; - int jumped_to_tag = false; + bool jumped_to_tag = false; int new_num_matches; char_u **new_matches; int use_tagstack; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 35c68fa1f6..83ade74db1 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status) } if (buf && !is_autocmd_blocked()) { - dict_T *dict = get_vim_var_dict(VV_EVENT); + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); tv_dict_add_nr(dict, S_LEN("status"), status); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); - tv_dict_clear(dict); + restore_v_event(dict, &save_v_event); } } @@ -412,6 +413,7 @@ void terminal_enter(void) curwin->w_redr_status = true; // For mode() in statusline. #8323 ui_busy_start(); apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf); + trigger_modechanged(); s->state.execute = terminal_execute; s->state.check = terminal_check; diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 27b3eedfa1..4e1a24af61 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -33,7 +33,7 @@ if has('timers') let g:triggered = 0 au CursorHoldI * let g:triggered += 1 set updatetime=20 - call timer_start(LoadAdjust(100), 'ExitInsertMode') + call timer_start(LoadAdjust(200), 'ExitInsertMode') call feedkeys('a', 'x!') call assert_equal(1, g:triggered) unlet g:triggered @@ -579,9 +579,10 @@ func Test_empty_doau() endfunc func s:AutoCommandOptionSet(match) + let template = "Option: <%s>, OldVal: <%s>, OldValLocal: <%s>, OldValGlobal: <%s>, NewVal: <%s>, Scope: <%s>, Command: <%s>\n" let item = remove(g:options, 0) - let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3]) - let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type) + let expected = printf(template, item[0], item[1], item[2], item[3], item[4], item[5], item[6]) + let actual = printf(template, a:match, v:option_old, v:option_oldlocal, v:option_oldglobal, v:option_new, v:option_type, v:option_command) let g:opt = [expected, actual] "call assert_equal(expected, actual) endfunc @@ -595,130 +596,593 @@ func Test_OptionSet() au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) " 1: Setting number option" - let g:options = [['number', 0, 1, 'global']] + let g:options = [['number', 0, 0, 0, 1, 'global', 'set']] set nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 2: Setting local number option" - let g:options = [['number', 1, 0, 'local']] + let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']] setlocal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 3: Setting global number option" - let g:options = [['number', 1, 0, 'global']] + let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']] setglobal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 4: Setting local autoindent option" - let g:options = [['autoindent', 0, 1, 'local']] + let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']] setlocal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 5: Setting global autoindent option" - let g:options = [['autoindent', 0, 1, 'global']] + let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']] setglobal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 6: Setting global autoindent option" - let g:options = [['autoindent', 1, 0, 'global']] + let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']] + set ai! + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 6a: Setting global autoindent option" + let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']] + noa setlocal ai + noa setglobal noai set ai! call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " Should not print anything, use :noa " 7: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nonu - call assert_equal([['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 8: Setting several global list and number option" - let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']] set list nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 9: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nolist nonu - call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 10: Setting global acd" - let g:options = [['autochdir', 0, 1, 'local']] + let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']] setlocal acd call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 11: Setting global autoread (also sets local value)" - let g:options = [['autoread', 0, 1, 'global']] + let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']] set ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 12: Setting local autoread" - let g:options = [['autoread', 1, 1, 'local']] + let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']] setlocal ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 13: Setting global autoread" - let g:options = [['autoread', 1, 0, 'global']] + let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']] setglobal invar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 14: Setting option backspace through :let" - let g:options = [['backspace', '', 'eol,indent,start', 'global']] + let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']] let &bs = "eol,indent,start" call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 15: Setting option backspace through setbufvar()" - let g:options = [['backup', 0, 1, 'local']] + let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']] " try twice, first time, shouldn't trigger because option name is invalid, " second time, it should trigger - call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") + let bnum = bufnr('%') + call assert_fails("call setbufvar(bnum, '&l:bk', 1)", 'E355:') " should trigger, use correct option name - call setbufvar(1, '&backup', 1) + call setbufvar(bnum, '&backup', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 16: Setting number option using setwinvar" - let g:options = [['number', 0, 1, 'local']] + let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']] call setwinvar(0, '&number', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 17: Setting key option, shouldn't trigger" - let g:options = [['key', 'invalid', 'invalid1', 'invalid']] + let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']] setlocal key=blah setlocal key= - call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) + call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 18a: Setting string global option" + let oldval = &backupext + let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']] + set backupext=foo + call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 18: Setting string option" + " 18b: Resetting string global option" + let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set backupext& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18c: Setting global string global option" + let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal backupext=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18d: Setting local string global option" + " As this is a global option this sets the global value even though + " :setlocal is used! + noa set backupext& " Reset global and local value (without triggering autocmd) + let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal backupext=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18e: Setting again string global option" + noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd) + noa setlocal backupext=ext_local " Sets the global(!) value! + let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']] + set backupext=fuu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 19a: Setting string global-local (to buffer) option" let oldval = &tags - let g:options = [['tags', oldval, 'tagpath', 'global']] + let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']] set tags=tagpath call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 1l: Resetting string option" - let g:options = [['tags', 'tagpath', oldval, 'global']] + " 19b: Resetting string global-local (to buffer) option" + let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']] set tags& call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) + " 19c: Setting global string global-local (to buffer) option " + let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']] + setglobal tags=tagpath1 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19d: Setting local string global-local (to buffer) option" + let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']] + setlocal tags=tagpath2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19e: Setting again string global-local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags=tag_local + let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19f: Setting string global-local (to buffer) option to an empty string" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa set tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags= " empty string + let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 20a: Setting string local (to buffer) option" + let oldval = &spelllang + let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']] + set spelllang=elvish,klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20b: Resetting string local (to buffer) option" + let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']] + set spelllang& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20c: Setting global string local (to buffer) option" + let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']] + setglobal spelllang=elvish + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20d: Setting local string local (to buffer) option" + noa set spelllang& " Reset global and local value (without triggering autocmd) + let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']] + setlocal spelllang=klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20e: Setting again string local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd) + noa setlocal spelllang=spelllocal + let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']] + set spelllang=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 21a: Setting string global-local (to window) option" + let oldval = &statusline + let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21b: Resetting string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set statusline& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21c: Setting global string global-local (to window) option" + let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal statusline=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21d: Setting local string global-local (to window) option" + noa set statusline& " Reset global and local value (without triggering autocmd) + let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal statusline=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21e: Setting again string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal statusline=bar " Reset global and local value (without triggering autocmd) + noa setlocal statusline=baz + let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 22a: Setting string local (to window) option" + let oldval = &foldignore + let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22b: Resetting string local (to window) option" + let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']] + set foldignore& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22c: Setting global string local (to window) option" + let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal foldignore=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22d: Setting local string local (to window) option" + noa set foldignore& " Reset global and local value (without triggering autocmd) + let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal foldignore=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22e: Setting again string local (to window) option" + noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd) + noa setlocal foldignore=loc + let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 23a: Setting global number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']] + setglobal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23b: Setting local number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']] + setlocal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23c: Setting again number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23d: Setting again number global option" + noa set cmdheight=8 " Reset global and local value (without triggering autocmd) + let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 24a: Setting global number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']] + setglobal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24b: Setting local number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']] + setlocal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24c: Setting again number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24d: Setting again global number global-local (to buffer) option" + noa set undolevels=8 " Reset global and local value (without triggering autocmd) + let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 25a: Setting global number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']] + setglobal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25b: Setting local number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']] + setlocal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25c: Setting again number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25d: Setting again global number local (to buffer) option" + noa set wrapmargin=8 " Reset global and local value (without triggering autocmd) + let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 26: Setting number global-local (to window) option. + " Such option does currently not exist. + + + " 27a: Setting global number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']] + setglobal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27b: Setting local number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']] + setlocal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27c: Setting again number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27d: Setting again global number local (to window) option" + noa set foldcolumn=8 " Reset global and local value (without triggering autocmd) + let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 28a: Setting global boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']] + setglobal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28b: Setting local boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28c: Setting again boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']] + set nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28d: Setting again global boolean global option" + noa set nowrapscan " Reset global and local value (without triggering autocmd) + let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']] + set wrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 29a: Setting global boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']] + setglobal autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29b: Setting local boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']] + setlocal noautoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29c: Setting again boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29d: Setting again global boolean global-local (to buffer) option" + noa set noautoread " Reset global and local value (without triggering autocmd) + let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 30a: Setting global boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30b: Setting local boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30c: Setting again boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30d: Setting again global boolean local (to buffer) option" + noa set nocindent " Reset global and local value (without triggering autocmd) + let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 31: Setting boolean global-local (to window) option + " Currently no such option exists. + + + " 32a: Setting global boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32b: Setting local boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32c: Setting again boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32d: Setting again global boolean local (to window) option" + noa set nocursorcolumn " Reset global and local value (without triggering autocmd) + let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 33: Test autocommands when an option value is converted internally. + noa set backspace=1 " Reset global and local value (without triggering autocmd) + let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']] + set backspace=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Cleanup au! OptionSet " set tags& - for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'tags'] + for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'backupext', 'tags', 'spelllang', 'statusline', 'foldignore', 'cmdheight', 'undolevels', 'wrapmargin', 'foldcolumn', 'wrapscan', 'autoread', 'cindent', 'cursorcolumn'] exe printf(":set %s&vim", opt) endfor call test_override('starting', 0) @@ -1896,6 +2360,26 @@ func Test_autocmd_CmdWinEnter() call delete(filename) endfunc +func Test_autocmd_was_using_freed_memory() + pedit xx + n x + augroup winenter + au WinEnter * if winnr('$') > 2 | quit | endif + augroup END + " Nvim needs large 'winwidth' and 'nowinfixwidth' to crash + set winwidth=99999 nowinfixwidth + split + + augroup winenter + au! WinEnter + augroup END + + set winwidth& winfixwidth& + bwipe xx + bwipe x + pclose +endfunc + func Test_FileChangedShell_reload() if !has('unix') return @@ -2124,6 +2608,19 @@ func Test_autocmd_closes_window() au! BufWinLeave endfunc +func Test_autocmd_quit_psearch() + sn aa bb + augroup aucmd_win_test + au! + au BufEnter,BufLeave,BufNew,WinEnter,WinLeave,WinNew * if winnr('$') > 1 | q | endif + augroup END + ps / + + augroup aucmd_win_test + au! + augroup END +endfunc + func Test_autocmd_closing_cmdwin() au BufWinLeave * nested q call assert_fails("norm 7q?\n", 'E855:') diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim index 180524cd73..38978ef689 100644 --- a/src/nvim/testdir/test_blockedit.vim +++ b/src/nvim/testdir/test_blockedit.vim @@ -15,6 +15,58 @@ func Test_blockinsert_indent() bwipe! endfunc +func Test_blockinsert_autoindent() + new + let lines =<< trim END + var d = { + a: () => 0, + b: () => 0, + c: () => 0, + } + END + call setline(1, lines) + filetype plugin indent on + setlocal sw=2 et ft=vim + setlocal indentkeys+=: + exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>" + let expected =<< trim END + var d = { + a: (): asdf => 0, + b: (): asdf => 0, + c: (): asdf => 0, + } + END + call assert_equal(expected, getline(1, 5)) + + " insert on the next column should do exactly the same + :%dele + call setline(1, lines) + exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>" + call assert_equal(expected, getline(1, 5)) + + :%dele + call setline(1, lines) + setlocal sw=8 noet + exe "norm! 2Gf)\<c-v>2jA: asdf\<esc>" + let expected =<< trim END + var d = { + a: (): asdf => 0, + b: (): asdf => 0, + c: (): asdf => 0, + } + END + call assert_equal(expected, getline(1, 5)) + + " insert on the next column should do exactly the same + :%dele + call setline(1, lines) + exe "norm! 2Gf)l\<c-v>2jI: asdf\<esc>" + call assert_equal(expected, getline(1, 5)) + + filetype off + bwipe! +endfunc + func Test_blockinsert_delete() new let _bs = &bs diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 23ad8dbfc5..37786f3ca0 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1644,4 +1644,95 @@ func Test_read_invalid() set encoding=utf-8 endfunc +" Test for ModeChanged pattern +func Test_mode_changes() + let g:index = 0 + let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n'] + func! TestMode() + call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) + call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode")) + call assert_equal(mode(1), get(v:event, "new_mode")) + let g:index += 1 + endfunc + + au ModeChanged * :call TestMode() + let g:n_to_any = 0 + au ModeChanged n:* let g:n_to_any += 1 + call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix') + + let g:V_to_v = 0 + au ModeChanged V:v let g:V_to_v += 1 + call feedkeys("Vv\<C-G>\<esc>", 'tnix') + call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any) + call assert_equal(1, g:V_to_v) + call assert_equal(len(g:mode_seq) - 1, g:index) + + let g:n_to_i = 0 + au ModeChanged n:i let g:n_to_i += 1 + let g:n_to_niI = 0 + au ModeChanged i:niI let g:n_to_niI += 1 + let g:niI_to_i = 0 + au ModeChanged niI:i let g:niI_to_i += 1 + let g:nany_to_i = 0 + au ModeChanged n*:i let g:nany_to_i += 1 + let g:i_to_n = 0 + au ModeChanged i:n let g:i_to_n += 1 + let g:nori_to_any = 0 + au ModeChanged [ni]:* let g:nori_to_any += 1 + let g:i_to_any = 0 + au ModeChanged i:* let g:i_to_any += 1 + let g:index = 0 + let g:mode_seq = ['n', 'i', 'niI', 'i', 'n'] + call feedkeys("a\<C-O>l\<esc>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(3, g:nori_to_any) + + if has('terminal') + let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n'] + call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix') + call assert_equal(len(g:mode_seq) - 1, g:index) + call assert_equal(1, g:n_to_i) + call assert_equal(1, g:n_to_niI) + call assert_equal(1, g:niI_to_i) + call assert_equal(2, g:nany_to_i) + call assert_equal(1, g:i_to_n) + call assert_equal(2, g:i_to_any) + call assert_equal(5, g:nori_to_any) + endif + + au! ModeChanged + delfunc TestMode + unlet! g:mode_seq + unlet! g:index + unlet! g:n_to_any + unlet! g:V_to_v + unlet! g:n_to_i + unlet! g:n_to_niI + unlet! g:niI_to_i + unlet! g:nany_to_i + unlet! g:i_to_n + unlet! g:nori_to_any + unlet! g:i_to_any +endfunc + +func Test_recursive_ModeChanged() + au! ModeChanged * norm 0u + sil! norm + au! +endfunc + +func Test_ModeChanged_starts_visual() + " This was triggering ModeChanged before setting VIsual, causing a crash. + au! ModeChanged * norm 0u + sil! norm + + au! ModeChanged +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 7560303e22..69edbc227d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -59,7 +59,7 @@ let s:filename_checks = { \ 'aml': ['file.aml'], \ 'ampl': ['file.run'], \ 'ant': ['build.xml'], - \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file'], + \ 'apache': ['.htaccess', '/etc/httpd/file.conf', '/etc/apache2/sites-2/file.com', '/etc/apache2/some.config', '/etc/apache2/conf.file/conf', '/etc/apache2/mods-some/file', '/etc/apache2/sites-some/file', '/etc/httpd/conf.d/file.config', '/etc/apache2/conf.file/file', '/etc/apache2/file.conf', '/etc/apache2/file.conf-file', '/etc/apache2/mods-file/file', '/etc/apache2/sites-file/file', '/etc/apache2/sites-file/file.com', '/etc/httpd/conf.d/file.conf', '/etc/httpd/conf.d/file.conf-file', 'access.conf', 'access.conf-file', 'any/etc/apache2/conf.file/file', 'any/etc/apache2/file.conf', 'any/etc/apache2/file.conf-file', 'any/etc/apache2/mods-file/file', 'any/etc/apache2/sites-file/file', 'any/etc/apache2/sites-file/file.com', 'any/etc/httpd/conf.d/file.conf', 'any/etc/httpd/conf.d/file.conf-file', 'any/etc/httpd/file.conf', 'apache.conf', 'apache.conf-file', 'apache2.conf', 'apache2.conf-file', 'httpd.conf', 'httpd.conf-file', 'srm.conf', 'srm.conf-file', '/etc/httpd/mods-some/file', '/etc/httpd/sites-some/file', '/etc/httpd/conf.file/conf'], \ 'apachestyle': ['/etc/proftpd/file.config,/etc/proftpd/conf.file/file', '/etc/proftpd/conf.file/file', '/etc/proftpd/file.conf', '/etc/proftpd/file.conf-file', 'any/etc/proftpd/conf.file/file', 'any/etc/proftpd/file.conf', 'any/etc/proftpd/file.conf-file', 'proftpd.conf', 'proftpd.conf-file'], \ 'applescript': ['file.scpt'], \ 'aptconf': ['apt.conf', '/.aptitude/config', 'any/.aptitude/config'], @@ -142,7 +142,7 @@ let s:filename_checks = { \ 'desc': ['file.desc'], \ 'desktop': ['file.desktop', '.directory', 'file.directory'], \ 'dictconf': ['dict.conf', '.dictrc'], - \ 'dictdconf': ['dictd.conf'], + \ 'dictdconf': ['dictd.conf', 'dictdfile.conf', 'dictd-file.conf'], \ 'diff': ['file.diff', 'file.rej'], \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'], \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], @@ -182,11 +182,12 @@ let s:filename_checks = { \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], \ 'fish': ['file.fish'], \ 'focexec': ['file.fex', 'file.focexec'], - \ 'forth': ['file.fs', 'file.ft', 'file.fth'], + \ 'forth': ['file.ft', 'file.fth'], \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'], \ 'fpcmake': ['file.fpc'], \ 'framescript': ['file.fsl'], \ 'freebasic': ['file.fb', 'file.bi'], + \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'], \ 'fstab': ['fstab', 'mtab'], \ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'], \ 'gdb': ['.gdbinit', 'gdbinit'], @@ -338,7 +339,7 @@ let s:filename_checks = { \ 'msql': ['file.msql'], \ 'mupad': ['file.mu'], \ 'mush': ['file.mush'], - \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], + \ 'muttrc': ['Muttngrc', 'Muttrc', '.muttngrc', '.muttngrc-file', '.muttrc', '.muttrc-file', '/.mutt/muttngrc', '/.mutt/muttngrc-file', '/.mutt/muttrc', '/.mutt/muttrc-file', '/.muttng/muttngrc', '/.muttng/muttngrc-file', '/.muttng/muttrc', '/.muttng/muttrc-file', '/etc/Muttrc.d/file', '/etc/Muttrc.d/file.rc', 'Muttngrc-file', 'Muttrc-file', 'any/.mutt/muttngrc', 'any/.mutt/muttngrc-file', 'any/.mutt/muttrc', 'any/.mutt/muttrc-file', 'any/.muttng/muttngrc', 'any/.muttng/muttngrc-file', 'any/.muttng/muttrc', 'any/.muttng/muttrc-file', 'any/etc/Muttrc.d/file', 'muttngrc', 'muttngrc-file', 'muttrc', 'muttrc-file'], \ 'mysql': ['file.mysql'], \ 'n1ql': ['file.n1ql', 'file.nql'], \ 'named': ['namedfile.conf', 'rndcfile.conf', 'named-file.conf', 'named.conf', 'rndc-file.conf', 'rndc-file.key', 'rndc.conf', 'rndc.key'], @@ -475,6 +476,7 @@ let s:filename_checks = { \ 'sqlj': ['file.sqlj'], \ 'sqr': ['file.sqr', 'file.sqi'], \ 'squid': ['squid.conf'], + \ 'squirrel': ['file.nut'], \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], \ 'sshconfig': ['ssh_config', '/.ssh/config', '/etc/ssh/ssh_config.d/file.conf', 'any/etc/ssh/ssh_config.d/file.conf', 'any/.ssh/config'], \ 'sshdconfig': ['sshd_config', '/etc/ssh/sshd_config.d/file.conf', 'any/etc/ssh/sshd_config.d/file.conf'], @@ -500,7 +502,7 @@ let s:filename_checks = { \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], - \ 'text': ['file.text', 'README', '/usr/share/doc/bash-completion/AUTHORS'], + \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], \ 'tf': ['file.tf', '.tfrc', 'tfrc'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], @@ -553,7 +555,7 @@ let s:filename_checks = { \ 'xhtml': ['file.xhtml', 'file.xht'], \ 'xinetd': ['/etc/xinetd.conf', '/etc/xinetd.d/file', 'any/etc/xinetd.conf', 'any/etc/xinetd.d/file'], \ 'xmath': ['file.msc', 'file.msf'], - \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu'], + \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd'], \ 'xmodmap': ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'], \ 'xf86conf': ['xorg.conf', 'xorg.conf-4'], \ 'xpm': ['file.xpm'], @@ -663,6 +665,7 @@ let s:script_checks = { \ 'fennel': [['#!/path/fennel']], \ 'routeros': [['#!/path/rsc']], \ 'fish': [['#!/path/fish']], + \ 'forth': [['#!/path/gforth']], \ } " Various forms of "env" optional arguments. @@ -945,4 +948,57 @@ func Test_xpm_file() filetype off endfunc +func Test_fs_file() + filetype on + + call writefile(['looks like F#'], 'Xfile.fs') + split Xfile.fs + call assert_equal('fsharp', &filetype) + bwipe! + + let g:filetype_fs = 'forth' + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + unlet g:filetype_fs + + " Test dist#ft#FTfs() + + " Forth (Gforth) + + call writefile(['( Forth inline comment )'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['.( Forth displayed inline comment )'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['\ Forth line comment'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + " empty line comment - no space required + call writefile(['\'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile(['\G Forth documentation comment '], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call writefile([': squared ( n -- n^2 )', 'dup * ;'], 'Xfile.fs') + split Xfile.fs + call assert_equal('forth', &filetype) + bwipe! + + call delete('Xfile.fs') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 726670f082..e3539c1a57 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -72,6 +72,8 @@ enum { NUMBUFLEN = 65, }; #define TERM_FOCUS 0x2000 // Terminal focus mode #define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview. +#define MODE_MAX_LENGTH 4 // max mode length returned in mode() + // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 800ecf10db..ba6cfab98b 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -204,40 +204,40 @@ LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) } \ } while (0) switch (schar) { - // Paired brackets. + // Paired brackets. #define BRACKET(typ, opning, clsing) \ -case opning: \ -case clsing: { \ - ret.type = typ; \ - ret.data.brc.closing = (schar == clsing); \ - break; \ -} - BRACKET(kExprLexParenthesis, '(', ')') - BRACKET(kExprLexBracket, '[', ']') - BRACKET(kExprLexFigureBrace, '{', '}') + case opning: \ + case clsing: { \ + ret.type = typ; \ + ret.data.brc.closing = (schar == clsing); \ + break; \ + } + BRACKET(kExprLexParenthesis, '(', ')') + BRACKET(kExprLexBracket, '[', ']') + BRACKET(kExprLexFigureBrace, '{', '}') #undef BRACKET - // Single character tokens without data. + // Single character tokens without data. #define CHAR(typ, ch) \ -case ch: { \ - ret.type = typ; \ - break; \ -} - CHAR(kExprLexQuestion, '?') - CHAR(kExprLexColon, ':') - CHAR(kExprLexComma, ',') + case ch: { \ + ret.type = typ; \ + break; \ + } + CHAR(kExprLexQuestion, '?') + CHAR(kExprLexColon, ':') + CHAR(kExprLexComma, ',') #undef CHAR - // Multiplication/division/modulo. + // Multiplication/division/modulo. #define MUL(mul_type, ch) \ -case ch: { \ - ret.type = kExprLexMultiplication; \ - ret.data.mul.type = mul_type; \ - break; \ -} - MUL(kExprLexMulMul, '*') - MUL(kExprLexMulDiv, '/') - MUL(kExprLexMulMod, '%') + case ch: { \ + ret.type = kExprLexMultiplication; \ + ret.data.mul.type = mul_type; \ + break; \ + } + MUL(kExprLexMulMul, '*') + MUL(kExprLexMulDiv, '/') + MUL(kExprLexMulMod, '%') #undef MUL #define CHARREG(typ, cond) \ @@ -653,16 +653,16 @@ case ch: { \ // Sign or augmented assignment. #define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \ -case ch: { \ - if (pline.size > 1 && pline.data[1] == '=') { \ - ret.len++; \ - ret.type = kExprLexAssignment; \ - ret.data.ass.type = ass_type; \ - } else { \ - ret.type = ch_type; \ - } \ - break; \ -} + case ch: { \ + if (pline.size > 1 && pline.data[1] == '=') { \ + ret.len++; \ + ret.type = kExprLexAssignment; \ + ret.data.ass.type = ass_type; \ + } else { \ + ret.type = ch_type; \ + } \ + break; \ + } CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd) CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat) #undef CHAR_OR_ASSIGN @@ -811,19 +811,19 @@ const char *viml_pexpr_repr_token(const ParserState *const pstate, const LexExpr eltkn_type_tab[token.type]); switch (token.type) { #define TKNARGS(tkn_type, ...) \ -case tkn_type: { \ - ADDSTR(__VA_ARGS__); \ - break; \ -} - TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", - eltkn_cmp_type_tab[token.data.cmp.type], - ccs_tab[token.data.cmp.ccs], - (int)token.data.cmp.inv) - TKNARGS(kExprLexMultiplication, "(type=%s)", - eltkn_mul_type_tab[token.data.mul.type]) - TKNARGS(kExprLexAssignment, "(type=%s)", - expr_asgn_type_tab[token.data.ass.type]) - TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) + case tkn_type: { \ + ADDSTR(__VA_ARGS__); \ + break; \ + } + TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", + eltkn_cmp_type_tab[token.data.cmp.type], + ccs_tab[token.data.cmp.ccs], + (int)token.data.cmp.inv) + TKNARGS(kExprLexMultiplication, "(type=%s)", + eltkn_mul_type_tab[token.data.mul.type]) + TKNARGS(kExprLexAssignment, "(type=%s)", + expr_asgn_type_tab[token.data.ass.type]) + TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) case kExprLexDoubleQuotedString: TKNARGS(kExprLexSingleQuotedString, "(closed=%i)", (int)token.data.str.closed) @@ -1540,21 +1540,21 @@ static inline void east_set_error(const ParserState *const pstate, ExprASTError case kExprNodeComplexIdentifier: \ case kExprNodePlainIdentifier: \ case kExprNodeCurlyBracesIdentifier: { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \ - cur_node->len = 0; \ - cur_node->children = *top_node_p; \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children->next); \ - ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ - assert(*new_top_node_p == NULL); \ - new_ident_node_code; \ - *new_top_node_p = cur_node; \ - HL_CUR_TOKEN(hl); \ - break; \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \ + cur_node->len = 0; \ + cur_node->children = *top_node_p; \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children->next); \ + ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ + assert(*new_top_node_p == NULL); \ + new_ident_node_code; \ + *new_top_node_p = cur_node; \ + HL_CUR_TOKEN(hl); \ + break; \ } \ default: { \ - OP_MISSING; \ - break; \ + OP_MISSING; \ + break; \ } \ } \ } while (0) @@ -1747,19 +1747,19 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no const char *const v_p_start = v_p; switch (*p) { #define SINGLE_CHAR_ESC(ch, real_ch) \ -case ch: { \ - *v_p++ = real_ch; \ - p++; \ - break; \ -} - SINGLE_CHAR_ESC('b', BS) - SINGLE_CHAR_ESC('e', ESC) - SINGLE_CHAR_ESC('f', FF) - SINGLE_CHAR_ESC('n', NL) - SINGLE_CHAR_ESC('r', CAR) - SINGLE_CHAR_ESC('t', TAB) - SINGLE_CHAR_ESC('"', '"') - SINGLE_CHAR_ESC('\\', '\\') + case ch: { \ + *v_p++ = real_ch; \ + p++; \ + break; \ + } + SINGLE_CHAR_ESC('b', BS) + SINGLE_CHAR_ESC('e', ESC) + SINGLE_CHAR_ESC('f', FF) + SINGLE_CHAR_ESC('n', NL) + SINGLE_CHAR_ESC('r', CAR) + SINGLE_CHAR_ESC('t', TAB) + SINGLE_CHAR_ESC('"', '"') + SINGLE_CHAR_ESC('\\', '\\') #undef SINGLE_CHAR_ESC // Hexadecimal or unicode. @@ -2141,32 +2141,32 @@ viml_pexpr_parse_process_token: break; } #define SIMPLE_UB_OP(op) \ -case kExprLex##op: { \ - if (want_node == kENodeValue) { \ - /* Value level: assume unary operator. */ \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ - *top_node_p = cur_node; \ - kvi_push(ast_stack, &cur_node->children); \ - HL_CUR_TOKEN(Unary##op); \ - } else { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ - ADD_OP_NODE(cur_node); \ - HL_CUR_TOKEN(Binary##op); \ - } \ - want_node = kENodeValue; \ - break; \ -} + case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ + } SIMPLE_UB_OP(Plus) SIMPLE_UB_OP(Minus) #undef SIMPLE_UB_OP #define SIMPLE_B_OP(op, msg) \ -case kExprLex##op: { \ - ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ - HL_CUR_TOKEN(op); \ - ADD_OP_NODE(cur_node); \ - break; \ -} + case kExprLex##op: { \ + ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ + HL_CUR_TOKEN(op); \ + ADD_OP_NODE(cur_node); \ + break; \ + } SIMPLE_B_OP(Or, "or operator") SIMPLE_B_OP(And, "and operator") #undef SIMPLE_B_OP @@ -2174,14 +2174,14 @@ case kExprLex##op: { \ ADD_VALUE_IF_MISSING(_("E15: Unexpected multiplication-like operator: %.*s")); switch (cur_token.data.mul.type) { #define MUL_OP(lex_op_tail, node_op_tail) \ -case kExprLexMul##lex_op_tail: { \ - NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ - HL_CUR_TOKEN(node_op_tail); \ - break; \ -} - MUL_OP(Mul, Multiplication) - MUL_OP(Div, Division) - MUL_OP(Mod, Mod) + case kExprLexMul##lex_op_tail: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ + HL_CUR_TOKEN(node_op_tail); \ + break; \ + } + MUL_OP(Mul, Multiplication) + MUL_OP(Div, Division) + MUL_OP(Mod, Mod) #undef MUL_OP } ADD_OP_NODE(cur_node); @@ -2929,11 +2929,11 @@ viml_pexpr_parse_no_paren_closing_error: {} cur_node->data.ass.type = cur_token.data.ass.type; switch (cur_token.data.ass.type) { #define HL_ASGN(asgn, hl) \ -case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } - HL_ASGN(Plain, PlainAssignment) - HL_ASGN(Add, AssignmentWithAddition) - HL_ASGN(Subtract, AssignmentWithSubtraction) - HL_ASGN(Concat, AssignmentWithConcatenation) + case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } + HL_ASGN(Plain, PlainAssignment) + HL_ASGN(Add, AssignmentWithAddition) + HL_ASGN(Subtract, AssignmentWithSubtraction) + HL_ASGN(Concat, AssignmentWithConcatenation) #undef HL_ASGN } ADD_OP_NODE(cur_node); diff --git a/src/nvim/window.c b/src/nvim/window.c index e328ff5467..3e6e42dec2 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4525,6 +4525,7 @@ static void win_enter_ext(win_T *const wp, const int flags) fix_current_dir(); + // Careful: autocommands may close the window and make "wp" invalid if (flags & WEE_TRIGGER_NEW_AUTOCMDS) { apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); } @@ -4558,7 +4559,7 @@ static void win_enter_ext(win_T *const wp, const int flags) } // set window width to desired minimal value - if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) { + if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !curwin->w_floating) { win_setwidth((int)p_wiw); } diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index 558fa1759f..49ce394dc9 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.73.0-199-0dfafb27 +# Uncrustify-0.74.0 # # General options @@ -214,6 +214,10 @@ sp_after_ptr_star_func = remove # ignore/add/remove/force/not_defined # function prototype or function definition. sp_after_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined +# Add or remove space between the pointer star '*' and the name of the variable +# in a function pointer definition. +sp_ptr_star_func_var = ignore # ignore/add/remove/force/not_defined + # Add or remove space after a pointer star '*', if followed by an open # parenthesis, as in 'void* (*)()'. sp_ptr_star_paren = ignore # ignore/add/remove/force/not_defined @@ -311,19 +315,33 @@ sp_permit_cpp11_shift = false # true/false # 'while', etc.). sp_before_sparen = force # ignore/add/remove/force/not_defined -# Add or remove space inside '(' and ')' of control statements. +# Add or remove space inside '(' and ')' of control statements other than +# 'for'. sp_inside_sparen = remove # ignore/add/remove/force/not_defined -# Add or remove space after '(' of control statements. +# Add or remove space after '(' of control statements other than 'for'. # # Overrides sp_inside_sparen. sp_inside_sparen_open = remove # ignore/add/remove/force/not_defined -# Add or remove space before ')' of control statements. +# Add or remove space before ')' of control statements other than 'for'. # # Overrides sp_inside_sparen. sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined +# Add or remove space inside '(' and ')' of 'for' statements. +sp_inside_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_close = ignore # ignore/add/remove/force/not_defined + # Add or remove space between '((' or '))' of control statements. sp_sparen_paren = ignore # ignore/add/remove/force/not_defined @@ -648,6 +666,11 @@ sp_func_class_paren = ignore # ignore/add/remove/force/not_defined # and '()'. sp_func_class_paren_empty = ignore # ignore/add/remove/force/not_defined +# Add or remove space after 'return'. +# +# Default: force +sp_return = force # ignore/add/remove/force/not_defined + # Add or remove space between 'return' and '('. sp_return_paren = force # ignore/add/remove/force/not_defined @@ -971,11 +994,31 @@ sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined # Overrides sp_inside_newop_paren. sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space before a trailing or embedded comment. -sp_before_tr_emb_cmt = add # ignore/add/remove/force/not_defined +# Add or remove space before a trailing comment. +sp_before_tr_cmt = add # ignore/add/remove/force/not_defined + +# Number of spaces before a trailing comment. +sp_num_before_tr_cmt = 2 # unsigned number + +# Add or remove space before an embedded comment. +# +# Default: force +sp_before_emb_cmt = force # ignore/add/remove/force/not_defined + +# Number of spaces before an embedded comment. +# +# Default: 1 +sp_num_before_emb_cmt = 1 # unsigned number + +# Add or remove space after an embedded comment. +# +# Default: force +sp_after_emb_cmt = force # ignore/add/remove/force/not_defined -# Number of spaces before a trailing or embedded comment. -sp_num_before_tr_emb_cmt = 2 # unsigned number +# Number of spaces after an embedded comment. +# +# Default: 1 +sp_num_after_emb_cmt = 1 # unsigned number # (Java) Add or remove space between an annotation and the open parenthesis. sp_annotation_paren = ignore # ignore/add/remove/force/not_defined @@ -1216,12 +1259,16 @@ indent_sparen_extra = 0 # number indent_relative_single_line_comments = true # true/false # Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. -# It might wise to choose the same value for the option indent_case_brace. +# It might be wise to choose the same value for the option indent_case_brace. indent_switch_case = 0 # unsigned number +# Spaces to indent the body of a 'switch' before any 'case'. +# Usually the same as indent_columns or indent_switch_case. +indent_switch_body = 0 # unsigned number + # Spaces to indent '{' from 'case'. By default, the brace will appear under # the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. -# It might wise to choose the same value for the option indent_switch_case. +# It might be wise to choose the same value for the option indent_switch_case. indent_case_brace = 0 # number # indent 'break' with 'case' from 'switch'. @@ -1236,13 +1283,31 @@ indent_switch_pp = true # true/false # Usually 0. indent_case_shift = 0 # unsigned number +# Whether to align comments before 'case' with the 'case'. +# +# Default: true +indent_case_comment = true # true/false + +# Whether to indent comments not found in first column. +# +# Default: true +indent_comment = true # true/false + # Whether to indent comments found in first column. indent_col1_comment = false # true/false # Whether to indent multi string literal in first column. indent_col1_multi_string_literal = false # true/false -# How to indent goto labels. +# Align comments on adjacent lines that are this many columns apart or less. +# +# Default: 3 +indent_comment_align_thresh = 3 # unsigned number + +# Whether to ignore indent for goto labels. +indent_ignore_label = false # true/false + +# How to indent goto labels. Requires indent_ignore_label=false. # # >0: Absolute column where 1 is the leftmost column # <=0: Subtract from brace indent @@ -1414,7 +1479,7 @@ indent_using_block = true # true/false # 0: Off (default) # 1: When the `if_false` is a continuation, indent it under `if_false` # 2: When the `:` is a continuation, indent it under `?` -indent_ternary_operator = 2 # unsigned number +indent_ternary_operator = 0 # unsigned number # Whether to indent the statements inside ternary operator. indent_inside_ternary_operator = false # true/false @@ -2622,6 +2687,22 @@ align_right_cmt_at_col = 0 # unsigned number # 0: Don't align (default). align_func_proto_span = 0 # unsigned number +# How to consider (or treat) the '*' in the alignment of function prototypes. +# +# 0: Part of the type 'void * foo();' (default) +# 1: Part of the function 'void *foo();' +# 2: Dangling 'void *foo();' +# Dangling: the '*' will not be taken into account when aligning. +align_func_proto_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of function prototypes. +# +# 0: Part of the type 'long & foo();' (default) +# 1: Part of the function 'long &foo();' +# 2: Dangling 'long &foo();' +# Dangling: the '&' will not be taken into account when aligning. +align_func_proto_amp_style = 0 # unsigned number + # The threshold for aligning function prototypes. # Use a negative number for absolute thresholds. # @@ -3101,6 +3182,9 @@ pp_indent_in_guard = false # true/false # indented from column 1. pp_define_at_level = false # true/false +# Whether to indent '#include' at the brace level. +pp_include_at_level = false # true/false + # Whether to ignore the '#define' body while formatting. pp_ignore_define_body = false # true/false @@ -3307,5 +3391,5 @@ set QUESTION REAL_FATTR_CONST set QUESTION REAL_FATTR_NONNULL_ALL set QUESTION REAL_FATTR_PURE set QUESTION REAL_FATTR_WARN_UNUSED_RESULT -# option(s) with 'not default' value: 87 +# option(s) with 'not default' value: 86 # diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index a0c97804b7..688f3abba5 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -629,6 +629,13 @@ describe('api/buf', function() -- Doesn't change the global value eq([[^\s*#\s*define]], nvim('get_option', 'define')) end) + + it('returns values for unset local options', function() + -- 'undolevels' is only set to its "unset" value when a new buffer is + -- created + command('enew') + eq(-123456, curbuf('get_option', 'undolevels')) + end) end) describe('nvim_buf_get_name, nvim_buf_set_name', function() diff --git a/test/functional/api/server_notifications_spec.lua b/test/functional/api/server_notifications_spec.lua index 6367cc5caa..1c00f001ff 100644 --- a/test/functional/api/server_notifications_spec.lua +++ b/test/functional/api/server_notifications_spec.lua @@ -81,6 +81,8 @@ describe('notify', function() if isCI() then pending('hangs on CI #14083 #15251') return + elseif helpers.skip_fragile(pending) then + return end if helpers.pending_win32(pending) then return end local catchan = eval("jobstart(['cat'], {'rpc': v:true})") diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 21de4925b5..d53208a915 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -949,6 +949,33 @@ describe('API', function() end) end) + describe('nvim_get_option_value, nvim_set_option_value', function() + it('works', function() + ok(nvim('get_option_value', 'equalalways', {})) + nvim('set_option_value', 'equalalways', false, {}) + ok(not nvim('get_option_value', 'equalalways', {})) + end) + + it('can get local values when global value is set', function() + eq(0, nvim('get_option_value', 'scrolloff', {})) + eq(-1, nvim('get_option_value', 'scrolloff', {scope = 'local'})) + end) + + it('can set global and local values', function() + nvim('set_option_value', 'makeprg', 'hello', {}) + eq('hello', nvim('get_option_value', 'makeprg', {})) + eq('', nvim('get_option_value', 'makeprg', {scope = 'local'})) + nvim('set_option_value', 'makeprg', 'world', {scope = 'local'}) + eq('world', nvim('get_option_value', 'makeprg', {scope = 'local'})) + nvim('set_option_value', 'makeprg', 'goodbye', {scope = 'global'}) + eq('goodbye', nvim('get_option_value', 'makeprg', {scope = 'global'})) + nvim('set_option_value', 'makeprg', 'hello', {}) + eq('hello', nvim('get_option_value', 'makeprg', {scope = 'global'})) + eq('hello', nvim('get_option_value', 'makeprg', {})) + eq('', nvim('get_option_value', 'makeprg', {scope = 'local'})) + end) + end) + describe('nvim_{get,set}_current_buf, nvim_list_bufs', function() it('works', function() eq(1, #nvim('list_bufs')) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index 11755a9d97..4d2ffa316a 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -222,9 +222,9 @@ describe('API/win', function() eq('', nvim('get_option', 'statusline')) command("set modified") command("enew") -- global-local: not preserved in new buffer - eq("Failed to get value for option 'statusline'", - pcall_err(curwin, 'get_option', 'statusline')) - eq('', eval('&l:statusline')) -- confirm local value was not copied + -- confirm local value was not copied + eq('', curwin('get_option', 'statusline')) + eq('', eval('&l:statusline')) end) it('after switching windows #15390', function() @@ -238,6 +238,10 @@ describe('API/win', function() eq('window-status', window('get_option', win1, 'statusline')) assert_alive() end) + + it('returns values for unset local options', function() + eq(-1, curwin('get_option', 'scrolloff')) + end) end) describe('get_position', function() diff --git a/test/functional/autocmd/modechanged_spec.lua b/test/functional/autocmd/modechanged_spec.lua new file mode 100644 index 0000000000..be5a291ac9 --- /dev/null +++ b/test/functional/autocmd/modechanged_spec.lua @@ -0,0 +1,31 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq +local feed, command = helpers.feed, helpers.command + +describe('ModeChanged', function() + before_each(function() + clear() + command('let g:count = 0') + command('au ModeChanged * let g:event = copy(v:event)') + command('au ModeChanged * let g:count += 1') + end) + + it('picks up terminal mode changes', function() + command("term") + feed('i') + eq({ + old_mode = 'nt', + new_mode = 't' + }, eval('g:event')) + feed('<c-\\><c-n>') + eq({ + old_mode = 't', + new_mode = 'nt' + }, eval('g:event')) + eq(3, eval('g:count')) + command("bd!") + + -- v:event is cleared after the autocommand is done + eq({}, eval('v:event')) + end) +end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index d1dce0f8da..4220d68ee1 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -560,7 +560,7 @@ describe('user config init', function() it('loads default lua config, but shows an error', function() clear{ args_rm={'-u'}, env=xenv } - feed('<cr>') -- TODO check this, test execution is blocked without it + feed('<cr>') -- confirm "Conflicting config ..." message eq(1, eval('g:lua_rc')) matches('^E5422: Conflicting configs', meths.exec('messages', true)) end) diff --git a/test/functional/fixtures/api_level_8.mpack b/test/functional/fixtures/api_level_8.mpack Binary files differnew file mode 100644 index 0000000000..0447fce3ed --- /dev/null +++ b/test/functional/fixtures/api_level_8.mpack diff --git a/test/functional/legacy/059_utf8_spell_checking_spec.lua b/test/functional/legacy/059_utf8_spell_checking_spec.lua deleted file mode 100644 index 8630ac58ef..0000000000 --- a/test/functional/legacy/059_utf8_spell_checking_spec.lua +++ /dev/null @@ -1,1010 +0,0 @@ --- Tests for spell checking with 'encoding' set to "utf-8". - -local helpers = require('test.functional.helpers')(after_each) -local feed, insert, source = helpers.feed, helpers.insert, helpers.source -local clear, feed_command, expect = helpers.clear, helpers.feed_command, helpers.expect -local write_file, call = helpers.write_file, helpers.call - -local function write_latin1(name, text) - text = call('iconv', text, 'utf-8', 'latin-1') - write_file(name, text) -end - -describe("spell checking with 'encoding' set to utf-8", function() - setup(function() - clear() - feed_command("syntax off") - write_latin1('Xtest1.aff',[[ - SET ISO8859-1 - TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ¿ - SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep? - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out . - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - ]]) - write_latin1('Xtest1.dic', [[ - 123456 - test/NO - # comment - wrong - Comment - OK - uk - put/ISO - the end - deol - déôr - ]]) - write_latin1('Xtest2.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - PFXPOSTPONE - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out [a-z] - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - ]]) - write_latin1('Xtest3.aff', [[ - SET ISO8859-1 - - COMPOUNDMIN 3 - COMPOUNDRULE m* - NEEDCOMPOUND x - ]]) - write_latin1('Xtest3.dic', [[ - 1234 - foo/m - bar/mx - mï/m - la/mx - ]]) - write_latin1('Xtest4.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - COMPOUNDRULE m+ - COMPOUNDRULE sm*e - COMPOUNDRULE sm+ - COMPOUNDMIN 3 - COMPOUNDWORDMAX 3 - COMPOUNDFORBIDFLAG t - - COMPOUNDSYLMAX 5 - SYLLABLE aáeéiíoóöõuúüûy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - - NEEDAFFIX x - - PFXPOSTPONE - - MIDWORD '- - - SFX q N 1 - SFX q 0 -ok . - - SFX a Y 2 - SFX a 0 s . - SFX a 0 ize/t . - - PFX p N 1 - PFX p 0 pre . - - PFX P N 1 - PFX P 0 nou . - ]]) - write_latin1('Xtest4.dic', [[ - 1234 - word/mP - util/am - pro/xq - tomato/m - bork/mp - start/s - end/e - ]]) - write_latin1('Xtest5.aff', [[ - SET ISO8859-1 - - FLAG long - - NEEDAFFIX !! - - COMPOUNDRULE ssmm*ee - - NEEDCOMPOUND xx - COMPOUNDPERMITFLAG pp - - SFX 13 Y 1 - SFX 13 0 bork . - - SFX a1 Y 1 - SFX a1 0 a1 . - - SFX aé Y 1 - SFX aé 0 aé . - - PFX zz Y 1 - PFX zz 0 pre/pp . - - PFX yy Y 1 - PFX yy 0 nou . - ]]) - write_latin1('Xtest5.dic', [[ - 1234 - foo/a1aé!! - bar/zz13ee - start/ss - end/eeyy - middle/mmxx - ]]) - write_latin1('Xtest6.aff', [[ - SET ISO8859-1 - - FLAG caplong - - NEEDAFFIX A! - - COMPOUNDRULE sMm*Ee - - NEEDCOMPOUND Xx - - COMPOUNDPERMITFLAG p - - SFX N3 Y 1 - SFX N3 0 bork . - - SFX A1 Y 1 - SFX A1 0 a1 . - - SFX Aé Y 1 - SFX Aé 0 aé . - - PFX Zz Y 1 - PFX Zz 0 pre/p . - ]]) - write_latin1('Xtest6.dic', [[ - 1234 - mee/A1AéA! - bar/ZzN3Ee - lead/s - end/Ee - middle/MmXx - ]]) - write_latin1('Xtest7.aff', [[ - SET ISO8859-1 - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - FLAG num - - NEEDAFFIX 9999 - - COMPOUNDRULE 2,77*123 - - NEEDCOMPOUND 1 - COMPOUNDPERMITFLAG 432 - - SFX 61003 Y 1 - SFX 61003 0 meat . - - SFX 391 Y 1 - SFX 391 0 a1 . - - SFX 111 Y 1 - SFX 111 0 aé . - - PFX 17 Y 1 - PFX 17 0 pre/432 . - ]]) - write_latin1('Xtest7.dic', [[ - 1234 - mee/391,111,9999 - bar/17,61003,123 - lead/2 - tail/123 - middle/77,1 - ]]) - write_latin1('Xtest8.aff', [[ - SET ISO8859-1 - - NOSPLITSUGS - ]]) - write_latin1('Xtest8.dic', [[ - 1234 - foo - bar - faabar - ]]) - write_latin1('Xtest9.aff', [[ - ]]) - write_latin1('Xtest9.dic', [[ - 1234 - foo - bar - ]]) - write_latin1('Xtest-sal.aff', [[ - SET ISO8859-1 - TRY esianrtolcdugmphbyfvkwjkqxz-ëéèêïîäàâöüû'ESIANRTOLCDUGMPHBYFVKWJKQXZ - - FOL àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - LOW àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþßÿ - UPP ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßÿ - - MIDWORD '- - - KEP = - RAR ? - BAD ! - - PFX I N 1 - PFX I 0 in . - - PFX O Y 1 - PFX O 0 out . - - SFX S Y 2 - SFX S 0 s [^s] - SFX S 0 es s - - SFX N N 3 - SFX N 0 en [^n] - SFX N 0 nen n - SFX N 0 n . - - REP 3 - REP g ch - REP ch g - REP svp s.v.p. - - MAP 9 - MAP aàáâãäå - MAP eèéêë - MAP iìíîï - MAP oòóôõö - MAP uùúûü - MAP nñ - MAP cç - MAP yÿý - MAP sß - - SAL AH(AEIOUY)-^ *H - SAL AR(AEIOUY)-^ *R - SAL A(HR)^ * - SAL A^ * - SAL AH(AEIOUY)- H - SAL AR(AEIOUY)- R - SAL A(HR) _ - SAL À^ * - SAL Å^ * - SAL BB- _ - SAL B B - SAL CQ- _ - SAL CIA X - SAL CH X - SAL C(EIY)- S - SAL CK K - SAL COUGH^ KF - SAL CC< C - SAL C K - SAL DG(EIY) K - SAL DD- _ - SAL D T - SAL É< E - SAL EH(AEIOUY)-^ *H - SAL ER(AEIOUY)-^ *R - SAL E(HR)^ * - SAL ENOUGH^$ *NF - SAL E^ * - SAL EH(AEIOUY)- H - SAL ER(AEIOUY)- R - SAL E(HR) _ - SAL FF- _ - SAL F F - SAL GN^ N - SAL GN$ N - SAL GNS$ NS - SAL GNED$ N - SAL GH(AEIOUY)- K - SAL GH _ - SAL GG9 K - SAL G K - SAL H H - SAL IH(AEIOUY)-^ *H - SAL IR(AEIOUY)-^ *R - SAL I(HR)^ * - SAL I^ * - SAL ING6 N - SAL IH(AEIOUY)- H - SAL IR(AEIOUY)- R - SAL I(HR) _ - SAL J K - SAL KN^ N - SAL KK- _ - SAL K K - SAL LAUGH^ LF - SAL LL- _ - SAL L L - SAL MB$ M - SAL MM M - SAL M M - SAL NN- _ - SAL N N - SAL OH(AEIOUY)-^ *H - SAL OR(AEIOUY)-^ *R - SAL O(HR)^ * - SAL O^ * - SAL OH(AEIOUY)- H - SAL OR(AEIOUY)- R - SAL O(HR) _ - SAL PH F - SAL PN^ N - SAL PP- _ - SAL P P - SAL Q K - SAL RH^ R - SAL ROUGH^ RF - SAL RR- _ - SAL R R - SAL SCH(EOU)- SK - SAL SC(IEY)- S - SAL SH X - SAL SI(AO)- X - SAL SS- _ - SAL S S - SAL TI(AO)- X - SAL TH @ - SAL TCH-- _ - SAL TOUGH^ TF - SAL TT- _ - SAL T T - SAL UH(AEIOUY)-^ *H - SAL UR(AEIOUY)-^ *R - SAL U(HR)^ * - SAL U^ * - SAL UH(AEIOUY)- H - SAL UR(AEIOUY)- R - SAL U(HR) _ - SAL V^ W - SAL V F - SAL WR^ R - SAL WH^ W - SAL W(AEIOU)- W - SAL X^ S - SAL X KS - SAL Y(AEIOU)- Y - SAL ZZ- _ - SAL Z S - ]]) - write_file('Xtest.utf-8.add', [[ - /regions=usgbnz - elequint/2 - elekwint/3 - ]]) - end) - - teardown(function() - os.remove('Xtest-sal.aff') - os.remove('Xtest.aff') - os.remove('Xtest.dic') - os.remove('Xtest.utf-8.add') - os.remove('Xtest.utf-8.add.spl') - os.remove('Xtest.utf-8.spl') - os.remove('Xtest.utf-8.sug') - os.remove('Xtest1.aff') - os.remove('Xtest1.dic') - os.remove('Xtest2.aff') - os.remove('Xtest3.aff') - os.remove('Xtest3.dic') - os.remove('Xtest4.aff') - os.remove('Xtest4.dic') - os.remove('Xtest5.aff') - os.remove('Xtest5.dic') - os.remove('Xtest6.aff') - os.remove('Xtest6.dic') - os.remove('Xtest7.aff') - os.remove('Xtest7.dic') - os.remove('Xtest8.aff') - os.remove('Xtest8.dic') - os.remove('Xtest9.aff') - os.remove('Xtest9.dic') - end) - - -- Function to test .aff/.dic with list of good and bad words. This was a - -- Vim function in the original legacy test. - local function test_one(aff, dic) - -- Generate a .spl file from a .dic and .aff file. - if helpers.iswin() then - os.execute('copy /y Xtest'..aff..'.aff Xtest.aff') - os.execute('copy /y Xtest'..dic..'.dic Xtest.dic') - else - os.execute('cp -f Xtest'..aff..'.aff Xtest.aff') - os.execute('cp -f Xtest'..dic..'.dic Xtest.dic') - end - source([[ - set spellfile= - function! SpellDumpNoShow() - " spelling scores depend on what happens to be drawn on screen - spelldump - %yank - quit - endfunction - $put ='' - $put ='test ]]..aff..'-'..dic..[[' - mkspell! Xtest Xtest - " Use that spell file. - set spl=Xtest.utf-8.spl spell - " List all valid words. - call SpellDumpNoShow() - $put - $put ='-------' - " Find all bad words and suggestions for them. - 1;/^]]..aff..[[good: - normal 0f:]s - let prevbad = '' - while 1 - let [bad, a] = spellbadword() - if bad == '' || bad == prevbad || bad == 'badend' - break - endif - let prevbad = bad - let lst = spellsuggest(bad, 3) - normal mm - $put =bad - $put =string(lst) - normal `m]s - endwhile - ]]) - end - - it('part 1-1', function() - insert([[ - 1good: wrong OK puts. Test the end - bad: inputs comment ok Ok. test déôl end the - badend - - test2: - elequint test elekwint test elekwent asdf - ]]) - test_one(1, 1) - feed_command([[$put =soundfold('goobledygoook')]]) - feed_command([[$put =soundfold('kóopërÿnôven')]]) - feed_command([[$put =soundfold('oeverloos gezwets edale')]]) - -- And now with SAL instead of SOFO items; test automatic reloading. - if helpers.iswin() then - os.execute('copy /y Xtest-sal.aff Xtest.aff') - else - os.execute('cp -f Xtest-sal.aff Xtest.aff') - end - feed_command('mkspell! Xtest Xtest') - feed_command([[$put =soundfold('goobledygoook')]]) - feed_command([[$put =soundfold('kóopërÿnôven')]]) - feed_command([[$put =soundfold('oeverloos gezwets edale')]]) - -- Also use an addition file. - feed_command('mkspell! Xtest.utf-8.add.spl Xtest.utf-8.add') - feed_command('set spellfile=Xtest.utf-8.add') - feed_command('/^test2:') - feed(']s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_us.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_gb.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_nz.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('set spl=Xtest_ca.utf-8.spl') - feed_command('/^test2:') - feed(']smm') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed('`m]s') - feed_command('let [str, a] = spellbadword()') - feed_command('$put =str') - feed_command('1,/^test 1-1/-1d') - expect([[ - test 1-1 - # file: Xtest.utf-8.spl - Comment - deol - déôr - input - OK - output - outputs - outtest - put - puts - test - testen - testn - the end - uk - wrong - ------- - bad - ['put', 'uk', 'OK'] - inputs - ['input', 'puts', 'outputs'] - comment - ['Comment', 'outtest', 'the end'] - ok - ['OK', 'uk', 'put'] - Ok - ['OK', 'Uk', 'Put'] - test - ['Test', 'testn', 'testen'] - déôl - ['deol', 'déôr', 'test'] - end - ['put', 'uk', 'test'] - the - ['put', 'uk', 'test'] - gebletegek - kepereneven - everles gesvets etele - kbltykk - kprnfn - *fls kswts tl - elekwent - elequint - elekwint - elekwint - elekwent - elequint - elekwent - elequint - elekwint]]) - end) - - it('part 2-1', function() - insert([[ - 2good: puts - bad: inputs comment ok Ok end the. test déôl - badend - ]]) - -- Postponed prefixes. - test_one(2, 1) - feed_command('1,/^test 2-1/-1d') - expect([=[ - test 2-1 - # file: Xtest.utf-8.spl - Comment - deol - déôr - OK - put - input - output - puts - outputs - test - outtest - testen - testn - the end - uk - wrong - ------- - bad - ['put', 'uk', 'OK'] - inputs - ['input', 'puts', 'outputs'] - comment - ['Comment'] - ok - ['OK', 'uk', 'put'] - Ok - ['OK', 'Uk', 'Put'] - end - ['put', 'uk', 'deol'] - the - ['put', 'uk', 'test'] - test - ['Test', 'testn', 'testen'] - déôl - ['deol', 'déôr', 'test']]=]) - end) - - it('part 3-3', function() - insert([[ - Test rules for compounding. - - 3good: foo mï foobar foofoobar barfoo barbarfoo - bad: bar la foomï barmï mïfoo mïbar mïmï lala mïla lamï foola labar - badend - ]]) - test_one(3, 3) - feed_command('1,/^test 3-3/-1d') - expect([=[ - test 3-3 - # file: Xtest.utf-8.spl - foo - mï - ------- - bad - ['foo', 'mï'] - bar - ['barfoo', 'foobar', 'foo'] - la - ['mï', 'foo'] - foomï - ['foo mï', 'foo', 'foofoo'] - barmï - ['barfoo', 'mï', 'barbar'] - mïfoo - ['mï foo', 'foo', 'foofoo'] - mïbar - ['foobar', 'barbar', 'mï'] - mïmï - ['mï mï', 'mï'] - lala - [] - mïla - ['mï', 'mï mï'] - lamï - ['mï', 'mï mï'] - foola - ['foo', 'foobar', 'foofoo'] - labar - ['barbar', 'foobar']]=]) - end) - - it('part 4-4', function() - insert([[ - Tests for compounding. - - 4good: word util bork prebork start end wordutil wordutils pro-ok - bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork - tomato tomatotomato startend startword startwordword startwordend - startwordwordend startwordwordwordend prebork preborkbork - preborkborkbork - nouword - bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato - endstart endend startstart wordend wordstart - preborkprebork preborkpreborkbork - startwordwordwordwordend borkpreborkpreborkbork - utilsbork startnouword - badend - ]]) - test_one(4, 4) - feed_command('1,/^test 4-4/-1d') - expect([=[ - test 4-4 - # file: Xtest.utf-8.spl - bork - prebork - end - pro-ok - start - tomato - util - utilize - utils - word - nouword - ------- - bad - ['end', 'bork', 'word'] - wordutilize - ['word utilize', 'wordutils', 'wordutil'] - pro - ['bork', 'word', 'end'] - borkborkborkborkborkbork - ['bork borkborkborkborkbork', 'borkbork borkborkborkbork', 'borkborkbork borkborkbork'] - tomatotomatotomato - ['tomato tomatotomato', 'tomatotomato tomato', 'tomato tomato tomato'] - endstart - ['end start', 'start'] - endend - ['end end', 'end'] - startstart - ['start start'] - wordend - ['word end', 'word', 'wordword'] - wordstart - ['word start', 'bork start'] - preborkprebork - ['prebork prebork', 'preborkbork', 'preborkborkbork'] - preborkpreborkbork - ['prebork preborkbork', 'preborkborkbork', 'preborkborkborkbork'] - startwordwordwordwordend - ['startwordwordwordword end', 'startwordwordwordword', 'start wordwordwordword end'] - borkpreborkpreborkbork - ['bork preborkpreborkbork', 'bork prebork preborkbork', 'bork preborkprebork bork'] - utilsbork - ['utilbork', 'utils bork', 'util bork'] - startnouword - ['start nouword', 'startword', 'startborkword']]=]) - end) - - it('part 5-5', function() - insert([[ - Test affix flags with two characters - - 5good: fooa1 fooaé bar prebar barbork prebarbork startprebar - start end startend startmiddleend nouend - bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart - startprobar startnouend - badend - ]]) - test_one(5, 5) - feed_command('1,/^test 5-5/-1d') - expect([=[ - test 5-5 - # file: Xtest.utf-8.spl - bar - barbork - end - fooa1 - fooaé - nouend - prebar - prebarbork - start - ------- - bad - ['bar', 'end', 'fooa1'] - foo - ['fooa1', 'fooaé', 'bar'] - fooa2 - ['fooa1', 'fooaé', 'bar'] - prabar - ['prebar', 'bar', 'bar bar'] - probarbirk - ['prebarbork'] - middle - [] - startmiddle - ['startmiddleend', 'startmiddlebar'] - middleend - [] - endstart - ['end start', 'start'] - startprobar - ['startprebar', 'start prebar', 'startbar'] - startnouend - ['start nouend', 'startend']]=]) - end) - - it('part 6-6', function() - insert([[ - 6good: meea1 meeaé bar prebar barbork prebarbork leadprebar - lead end leadend leadmiddleend - bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead - leadprobar - badend - ]]) - test_one(6, 6) - feed_command('1,/^test 6-6/-1d') - expect([=[ - test 6-6 - # file: Xtest.utf-8.spl - bar - barbork - end - lead - meea1 - meeaé - prebar - prebarbork - ------- - bad - ['bar', 'end', 'lead'] - mee - ['meea1', 'meeaé', 'bar'] - meea2 - ['meea1', 'meeaé', 'lead'] - prabar - ['prebar', 'bar', 'leadbar'] - probarbirk - ['prebarbork'] - middle - [] - leadmiddle - ['leadmiddleend', 'leadmiddlebar'] - middleend - [] - endlead - ['end lead', 'lead', 'end end'] - leadprobar - ['leadprebar', 'lead prebar', 'leadbar']]=]) - end) - - it('part 7-7', function() - insert([[ - 7good: meea1 meeaé bar prebar barmeat prebarmeat leadprebar - lead tail leadtail leadmiddletail - bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead - leadprobar - badend - ]]) - -- Compound words. - test_one(7, 7) - -- Assert buffer contents. - feed_command('1,/^test 7-7/-1d') - expect([=[ - test 7-7 - # file: Xtest.utf-8.spl - bar - barmeat - lead - meea1 - meeaé - prebar - prebarmeat - tail - ------- - bad - ['bar', 'lead', 'tail'] - mee - ['meea1', 'meeaé', 'bar'] - meea2 - ['meea1', 'meeaé', 'lead'] - prabar - ['prebar', 'bar', 'leadbar'] - probarmaat - ['prebarmeat'] - middle - [] - leadmiddle - ['leadmiddlebar'] - middletail - [] - taillead - ['tail lead', 'tail'] - leadprobar - ['leadprebar', 'lead prebar', 'leadbar']]=]) - end) - - it('part 8-8', function() - insert([[ - 8good: foo bar faabar - bad: foobar barfoo - badend - ]]) - -- NOSPLITSUGS - test_one(8, 8) - -- Assert buffer contents. - feed_command('1,/^test 8-8/-1d') - expect([=[ - test 8-8 - # file: Xtest.utf-8.spl - bar - faabar - foo - ------- - bad - ['bar', 'foo'] - foobar - ['faabar', 'foo bar', 'bar'] - barfoo - ['bar foo', 'bar', 'foo']]=]) - end) - - it('part 9-9', function() - insert([[ - 9good: 0b1011 0777 1234 0x01ff - badend - ]]) - -- NOSPLITSUGS - test_one(9, 9) - -- Assert buffer contents. - feed_command('1,/^test 9-9/-1d') - expect([=[ - test 9-9 - # file: Xtest.utf-8.spl - bar - foo - -------]=]) - end) -end) diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 1914818215..5e586d3a6a 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local nvim = helpers.meths -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq +local clear, eq, neq, eval = helpers.clear, helpers.eq, helpers.neq, helpers.eval local curbuf, buf = helpers.curbuf, helpers.bufmeths local curwin = helpers.curwin local exec_capture = helpers.exec_capture @@ -10,11 +10,14 @@ local function declare_hook_function() source([[ fu! AutoCommand(match, bufnr, winnr) let l:acc = { - \ 'option' : a:match, - \ 'oldval' : v:option_old, - \ 'newval' : v:option_new, - \ 'scope' : v:option_type, - \ 'attr' : { + \ 'option' : a:match, + \ 'oldval' : v:option_old, + \ 'oldval_l' : v:option_oldlocal, + \ 'oldval_g' : v:option_oldglobal, + \ 'newval' : v:option_new, + \ 'scope' : v:option_type, + \ 'cmd' : v:option_command, + \ 'attr' : { \ 'bufnr' : a:bufnr, \ 'winnr' : a:winnr, \ } @@ -42,13 +45,16 @@ local function get_result() return ret end -local function expected_table(option, oldval, newval, scope, attr) +local function expected_table(option, oldval, oldval_l, oldval_g, newval, scope, cmd, attr) return { - option = option, - oldval = tostring(oldval), - newval = tostring(newval), - scope = scope, - attr = attr, + option = option, + oldval = tostring(oldval), + oldval_l = tostring(oldval_l), + oldval_g = tostring(oldval_g), + newval = tostring(newval), + scope = scope, + cmd = cmd, + attr = attr, } end @@ -66,7 +72,7 @@ local function expected_combination(...) end for i, v in ipairs(args) do - local attr = v[5] + local attr = v[8] if not attr then -- remove attr entries ret[i].attr = nil @@ -112,7 +118,7 @@ local function get_new_window_number() end describe('au OptionSet', function() - describe('with any opton (*)', function() + describe('with any option (*)', function() before_each(function() clear() @@ -123,44 +129,44 @@ describe('au OptionSet', function() it('should be called in setting number option', function() command('set nu') - expected_combination({'number', 0, 1, 'global'}) + expected_combination({'number', 0, 0, 0, 1, 'global', 'set'}) command('setlocal nonu') - expected_combination({'number', 1, 0, 'local'}) + expected_combination({'number', 1, 1, '', 0, 'local', 'setlocal'}) command('setglobal nonu') - expected_combination({'number', 1, 0, 'global'}) + expected_combination({'number', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ai') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, '', 0, 1, 'global', 'setglobal'}) command('set noai') - expected_combination({'autoindent', 1, 0, 'global'}) + expected_combination({'autoindent', 1, 1, 1, 0, 'global', 'set'}) end) it('should be called in inverting global autoindent option',function() command('set ai!') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, 0, 0, 1, 'global', 'set'}) end) it('should be called in being unset local autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setlocal ai<') - expected_combination({'autoindent', 1, 0, 'local'}) + expected_combination({'autoindent', 1, 1, '', 0, 'local', 'setlocal'}) end) it('should be called in setting global list and number option at the same time',function() command('set list nu') expected_combination( - {'list', 0, 1, 'global'}, - {'number', 0, 1, 'global'} + {'list', 0, 0, 0, 1, 'global', 'set'}, + {'number', 0, 0, 0, 1, 'global', 'set'} ) end) @@ -171,25 +177,27 @@ describe('au OptionSet', function() it('should be called in setting local acd', function() command('setlocal acd') - expected_combination({'autochdir', 0, 1, 'local'}) + expected_combination({'autochdir', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in setting autoread', function() command('set noar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, 1, 1, 0, 'global', 'set'}) command('setlocal ar') - expected_combination({'autoread', 0, 1, 'local'}) + expected_combination({'autoread', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in inverting global autoread', function() command('setglobal invar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting backspace option through :let', function() + local oldval = eval('&backspace') + command('let &bs=""') - expected_combination({'backspace', 'indent,eol,start', '', 'global'}) + expected_combination({'backspace', oldval, oldval, oldval, '', 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -200,7 +208,7 @@ describe('au OptionSet', function() it('should trigger using correct option name', function() command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -208,9 +216,339 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) + + it('with string global option', function() + local oldval = eval('&backupext') + + command('set backupext=foo') + expected_combination({'backupext', oldval, oldval, oldval, 'foo', 'global', 'set'}) + + command('set backupext&') + expected_combination({'backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal backupext=bar') + expected_combination({'backupext', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set backupext&') + -- As this is a global option this sets the global value even though :setlocal is used! + command('setlocal backupext=baz') + expected_combination({'backupext', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal backupext=ext_global') + command('noa setlocal backupext=ext_local') -- Sets the global(!) value + command('set backupext=foo') + expected_combination({ + 'backupext', 'ext_local', 'ext_local', 'ext_local', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to buffer) option', function() + local oldval = eval('&tags') + + command('set tags=tagpath') + expected_combination({'tags', oldval, oldval, oldval, 'tagpath', 'global', 'set'}) + + command('set tags&') + expected_combination({'tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set'}) + + command('setglobal tags=tagpath1') + expected_combination({'tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal'}) + + command('setlocal tags=tagpath2') + expected_combination({'tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal tags=tag_global') + command('noa setlocal tags=tag_local') + command('set tags=tagpath') + expected_combination({ + 'tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set' + }) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa set tags=tag_global') + command('noa setlocal tags=') + command('set tags=tagpath') + expected_combination({'tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set'}) + end) + + it('with string local (to buffer) option', function() + local oldval = eval('&spelllang') + + command('set spelllang=elvish,klingon') + expected_combination({'spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set'}) + + command('set spelllang&') + expected_combination({ + 'spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set' + }) + + command('setglobal spelllang=elvish') + expected_combination({'spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal'}) + + command('noa set spelllang&') + command('setlocal spelllang=klingon') + expected_combination({'spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal spelllang=spellglobal') + command('noa setlocal spelllang=spelllocal') + command('set spelllang=foo') + expected_combination({ + 'spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to window) option', function() + local oldval = eval('&statusline') + + command('set statusline=foo') + expected_combination({'statusline', oldval, oldval, '', 'foo', 'global', 'set'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('set statusline&') + expected_combination({'statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal statusline=bar') + expected_combination({'statusline', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set statusline&') + command('setlocal statusline=baz') + expected_combination({'statusline', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal statusline=bar') + command('noa setlocal statusline=baz') + command('set statusline=foo') + expected_combination({'statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set'}) + end) + + it('with string local (to window) option', function() + local oldval = eval('&foldignore') + + command('set foldignore=fo') + expected_combination({'foldignore', oldval, oldval, oldval, 'fo', 'global', 'set'}) + + command('set foldignore&') + expected_combination({'foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set'}) + + command('setglobal foldignore=bar') + expected_combination({'foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set foldignore&') + command('setlocal foldignore=baz') + expected_combination({'foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal foldignore=glob') + command('noa setlocal foldignore=loc') + command('set foldignore=fo') + expected_combination({'foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set'}) + end) + + it('with number global option', function() + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setglobal cmdheight=2') + expected_combination({'cmdheight', 1, '', 1, 2, 'global', 'setglobal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setlocal cmdheight=2') + expected_combination({'cmdheight', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('set cmdheight=2') + expected_combination({'cmdheight', 1, 1, 1, 2, 'global', 'set'}) + + command('noa set cmdheight=8') + command('set cmdheight=2') + expected_combination({'cmdheight', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to buffer) option', function() + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setglobal undolevels=2') + expected_combination({'undolevels', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setlocal undolevels=2') + expected_combination({'undolevels', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('set undolevels=2') + expected_combination({'undolevels', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set undolevels=8') + command('set undolevels=2') + expected_combination({'undolevels', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to buffer) option', function() + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setglobal wrapmargin=2') + expected_combination({'wrapmargin', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setlocal wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set wrapmargin=8') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to window) option', function() + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setglobal scrolloff=2') + expected_combination({'scrolloff', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setlocal scrolloff=2') + expected_combination({'scrolloff', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('set scrolloff=2') + expected_combination({'scrolloff', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set scrolloff=8') + command('set scrolloff=2') + expected_combination({'scrolloff', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to window) option', function() + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setglobal foldcolumn=2') + expected_combination({'foldcolumn', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setlocal foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set foldcolumn=8') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with boolean global option', function() + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setglobal nowrapscan') + expected_combination({'wrapscan', 1, '', 1, 0, 'global', 'setglobal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setlocal nowrapscan') + expected_combination({'wrapscan', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('set nowrapscan') + expected_combination({'wrapscan', 1, 1, 1, 0, 'global', 'set'}) + + command('noa set nowrapscan') + command('set wrapscan') + expected_combination({'wrapscan', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean global-local (to buffer) option', function() + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setglobal autoread') + expected_combination({'autoread', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setlocal noautoread') + expected_combination({'autoread', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('set autoread') + expected_combination({'autoread', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set noautoread') + command('set autoread') + expected_combination({'autoread', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to buffer) option', function() + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setglobal cindent') + expected_combination({'cindent', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setlocal nocindent') + expected_combination({'cindent', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('set cindent') + expected_combination({'cindent', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocindent') + command('set cindent') + expected_combination({'cindent', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to window) option', function() + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setglobal cursorcolumn') + expected_combination({'cursorcolumn', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setlocal nocursorcolumn') + expected_combination({'cursorcolumn', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with option value converted internally', function() + command('noa set backspace=1') + command('set backspace=2') + expected_combination(({ + 'backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set' + })) + end) end) describe('with specific option', function() @@ -228,13 +566,13 @@ describe('au OptionSet', function() expected_empty() command('setlocal ro') - expected_combination({'readonly', 0, 1, 'local'}) + expected_combination({'readonly', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ro') - expected_combination({'readonly', 0, 1, 'global'}) + expected_combination({'readonly', 0, '', 0, 1, 'global', 'setglobal'}) command('set noro') - expected_combination({'readonly', 1, 0, 'global'}) + expected_combination({'readonly', 1, 1, 1, 0, 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -249,7 +587,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -259,7 +597,7 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) @@ -275,7 +613,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setwinvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should not trigger if the current window is different from the targetted window', function() @@ -295,7 +633,7 @@ describe('au OptionSet', function() nvim.set_option('autochdir', true) eq(true, nvim.get_option('autochdir')) - expected_combination({'autochdir', '0', '1', 'global'}) + expected_combination({'autochdir', 0, '', 0, 1, 'global', 'setglobal'}) end) it('should trigger if a number option be set globally', function() @@ -303,7 +641,7 @@ describe('au OptionSet', function() nvim.set_option('cmdheight', 5) eq(5, nvim.get_option('cmdheight')) - expected_combination({'cmdheight', 1, 5, 'global'}) + expected_combination({'cmdheight', 1, '', 1, 5, 'global', 'setglobal'}) end) it('should trigger if a string option be set globally', function() @@ -311,7 +649,7 @@ describe('au OptionSet', function() nvim.set_option('ambiwidth', 'double') eq('double', nvim.get_option('ambiwidth')) - expected_combination({'ambiwidth', 'single', 'double', 'global'}) + expected_combination({'ambiwidth', 'single', '', 'single', 'double', 'global', 'setglobal'}) end) end) end) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index e1439bfdb4..eeb9316b06 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -357,7 +357,7 @@ describe('vim.diagnostic', function() it("doesn't error after bwipeout on buffer", function() exec_lua [[ - vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { lnum = 0, end_lnum = 0, col = 0, end_col = 0 }) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) vim.cmd("bwipeout! " .. diagnostic_bufnr) vim.diagnostic.show(diagnostic_ns) @@ -653,7 +653,7 @@ describe('vim.diagnostic', function() it("doesn't error after bwipeout called on buffer", function() exec_lua [[ - vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { lnum = 0, end_lnum = 0, col = 0, end_col = 0 }) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, {{ lnum = 0, end_lnum = 0, col = 0, end_col = 0 }}) vim.cmd("bwipeout! " .. diagnostic_bufnr) vim.diagnostic.reset(diagnostic_ns) @@ -1370,7 +1370,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope="buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1387,7 +1387,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="line"}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1402,7 +1402,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="line", pos=1}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, pos=1}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1466,7 +1466,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {scope="line"}) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines @@ -1533,7 +1533,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false, scope = "line", pos = 5}) + local float_bufnr, winnr = vim.diagnostic.open_float(diagnostic_bufnr, {header = false, pos = 5}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return #lines @@ -1665,7 +1665,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1678,7 +1678,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, prefix = ""}) + local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer", prefix = ""}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1687,7 +1687,7 @@ describe('vim.diagnostic', function() eq({'1. Syntax error', '2. Some warning'}, exec_lua [[ local diagnostics = { make_error("Syntax error", 0, 1, 0, 3), - make_warning("Some warning", 1, 1, 1, 3), + make_warning("Some warning", 0, 1, 0, 3), } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) @@ -1918,5 +1918,27 @@ describe('vim.diagnostic', function() return {show_called, hide_called} ]]) end) + + it('triggers the autocommand when diagnostics are set', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic', 0, 0, 0, 0) + }) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) + + it('triggers the autocommand when diagnostics are cleared', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) end) end) diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 28471bdd46..2515b37beb 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1240,7 +1240,7 @@ describe('lua stdlib', function() vim.opt.makeprg = "global-local" table.insert(result, vim.api.nvim_get_option('makeprg')) - table.insert(result, (pcall(vim.api.nvim_buf_get_option, 0, 'makeprg'))) + table.insert(result, vim.api.nvim_buf_get_option(0, 'makeprg')) vim.opt_local.mp = "only-local" table.insert(result, vim.api.nvim_get_option('makeprg')) @@ -1258,7 +1258,7 @@ describe('lua stdlib', function() -- Set -> global & local eq("global-local", result[1]) - eq(false, result[2]) + eq("", result[2]) -- Setlocal -> only local eq("global-local", result[3]) @@ -1268,9 +1268,9 @@ describe('lua stdlib', function() eq("only-global", result[5]) eq("only-local", result[6]) - -- set -> doesn't override previously set value + -- Set -> sets global value and resets local value eq("global-local", result[7]) - eq("only-local", result[8]) + eq("", result[8]) end) it('should allow you to retrieve window opts even if they have not been set', function() diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index cfbcbdd0f2..52839d8efa 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -97,7 +97,7 @@ endif() if(MINGW AND CMAKE_GENERATOR MATCHES "Ninja") find_program(MAKE_PRG NAMES mingw32-make) if(NOT MAKE_PRG) - message(FATAL_ERROR "GNU Make for mingw32 is required to build the dependecies.") + message(FATAL_ERROR "GNU Make for mingw32 is required to build the dependencies.") else() message(STATUS "Found GNU Make for mingw32: ${MAKE_PRG}") endif() |