diff options
116 files changed, 2191 insertions, 772 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8596eb2a7f..2d06b1e685 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -1,4 +1,4 @@ -image: freebsd/12.x +image: freebsd/latest packages: - cmake @@ -12,6 +12,7 @@ packages: - gettext - python - libffi +- gdb sources: - https://github.com/neovim/neovim @@ -34,10 +35,6 @@ tasks: - unittest: | cd neovim gmake unittest - -# Unfortunately, oldtest is tanking hard on sourcehut's FreeBSD instance -# and not producing any logs as a result. So don't do this task for now. -# Ref: https://github.com/neovim/neovim/pull/11477#discussion_r352095005. -# - test-oldtest: | -# cd neovim -# gmake oldtest +- oldtest: | + cd neovim + gmake oldtest diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 422fa366b6..0aaa003820 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -1,6 +1,6 @@ # sourcehut CI: https://builds.sr.ht/~jmk/neovim -image: openbsd/6.9 +image: openbsd/latest packages: - autoconf-2.71 @@ -12,6 +12,7 @@ packages: - libtool - ninja-1.10.2p0 - unzip-6.0p14 +- gdb sources: - https://github.com/neovim/neovim diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3565b20bfc..41a22af538 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,11 @@ concurrency: jobs: lint: + # This job tests two things: it lints the code but also builds neovim using + # system dependencies instead of bundled dependencies. This is to make sure + # we are able to build neovim without pigeonholing ourselves into specifics + # of the bundled dependencies. + if: (github.event_name == 'pull_request' && github.base_ref == 'master' && !github.event.pull_request.draft) || (github.event_name == 'push' && github.ref == 'refs/heads/master') runs-on: ubuntu-20.04 timeout-minutes: 10 @@ -100,7 +105,7 @@ jobs: - name: Cache dependencies run: ./ci/before_cache.sh - unixish: + posix: name: ${{ matrix.runner }} ${{ matrix.flavor }} (cc=${{ matrix.cc }}) strategy: fail-fast: false @@ -120,6 +125,12 @@ jobs: - cc: clang runner: macos-11.0 os: osx + + # The functionaltest-lua test two things simultaneously: + # 1. Check that the tests pass with PUC Lua instead of LuaJIT. + # 2. Use as oldest/minimum versions of dependencies/build tools we + # still explicitly support so we don't accidentally rely on + # features that is only available on later versions. - flavor: functionaltest-lua cc: gcc runner: ubuntu-20.04 @@ -172,8 +183,8 @@ jobs: - name: Install brew packages if: matrix.os == 'osx' run: | - brew update >/dev/null - brew install automake ccache perl cpanminus ninja + brew update --quiet + brew install automake ccache cpanminus ninja - name: Setup interpreter packages run: ./ci/install.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e954b57175..8e0b13d59c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,7 +85,7 @@ jobs: fetch-depth: 0 - name: Install brew packages run: | - brew update >/dev/null + brew update --quiet brew install automake ninja - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.tag_name != 'nightly') run: printf 'NVIM_BUILD_TYPE=Release\n' >> $GITHUB_ENV diff --git a/CMakeLists.txt b/CMakeLists.txt index eae2d75e3f..767bd797bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,9 @@ endif() if(POLICY CMP0060) cmake_policy(SET CMP0060 NEW) endif() +if(POLICY CMP0075) + cmake_policy(SET CMP0075 NEW) +endif() # Point CMake at any custom modules we may ship list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") diff --git a/contrib/flake.lock b/contrib/flake.lock index c4d7f120ba..b72e1f8d5f 100644 --- a/contrib/flake.lock +++ b/contrib/flake.lock @@ -2,11 +2,11 @@ "nodes": { "flake-utils": { "locked": { - "lastModified": 1629481132, - "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", "owner": "numtide", "repo": "flake-utils", - "rev": "997f7efcb746a9c140ce1f13c72263189225f482", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", "type": "github" }, "original": { @@ -17,11 +17,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1630074300, - "narHash": "sha256-BFM7OiXRs0RvSUZd6NCGAKWVPn3VodgYQ4TUQXxbMBU=", + "lastModified": 1646254136, + "narHash": "sha256-8nQx02tTzgYO21BP/dy5BCRopE8OwE8Drsw98j+Qoaw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "21c937f8cb1e6adcfeb36dfd6c90d9d9bfab1d28", + "rev": "3e072546ea98db00c2364b81491b893673267827", "type": "github" }, "original": { diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 54cb6ce70f..c52def1051 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: 2022 Feb 22 +" Last Change: 2022 Mar 05 " These functions are moved here from runtime/filetype.vim to make startup " faster. diff --git a/runtime/compiler/jest.vim b/runtime/compiler/jest.vim index fee70b7c55..a4bb549de1 100644 --- a/runtime/compiler/jest.vim +++ b/runtime/compiler/jest.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Jest " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2018 May 15 +" Last Change: 2021 Nov 20 if exists("current_compiler") finish @@ -15,12 +15,14 @@ endif let s:cpo_save = &cpo set cpo&vim -" CompilerSet makeprg=npx\ jest\ --no-colors +" CompilerSet makeprg=npx\ --no-install\ jest\ --no-colors CompilerSet makeprg=jest\ --no-colors -CompilerSet errorformat=%E\ \ ●\ %m, +CompilerSet errorformat=%-A\ \ ●\ Console, + \%E\ \ ●\ %m, \%Z\ %\\{4}%.%#Error:\ %f:\ %m\ (%l:%c):%\\=, \%Z\ %\\{6}at\ %\\S%#\ (%f:%l:%c), + \%Z\ %\\{6}at\ %\\S%#\ %f:%l:%c, \%+C\ %\\{4}%\\w%.%#, \%+C\ %\\{4}%[-+]%.%#, \%-C%.%#, diff --git a/runtime/compiler/sml.vim b/runtime/compiler/sml.vim index c7e1b1bf16..a0b13b6c8a 100644 --- a/runtime/compiler/sml.vim +++ b/runtime/compiler/sml.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: SML/NJ Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2020 Feb 10 +" Last Change: 2022 Feb 09 if exists("current_compiler") finish @@ -16,10 +16,10 @@ let s:cpo_save = &cpo set cpo&vim CompilerSet makeprg=sml -CompilerSet errorformat=%f:%l.%c-%\\d%\\+.%\\d%\\+\ %trror:\ %m, +CompilerSet errorformat=%f:%l.%c-%e.%k\ %trror:\ %m, \%f:%l.%c\ %trror:\ %m, - \%trror:\ %m - \%f:%l.%c-%\\d%\\+.%\\d%\\+\ %tarning:\ %m, + \%trror:\ %m, + \%f:%l.%c-%e.%k\ %tarning:\ %m, \%f:%l.%c\ %tarning:\ %m, \%tarning:\ %m, \%-G%.%# diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index fe5f9eaf35..eefe6e5a47 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3165,7 +3165,7 @@ nvim_create_autocmd({event}, {*opts}) *nvim_create_autocmd()* • create a |autocmd-buflocal| autocmd. • NOTE: Cannot be used with {pattern} - • group: (string) The augroup name + • group: (string|int) The augroup name or id • once: (boolean) - See |autocmd-once| • nested: (boolean) - See |autocmd-nested| • desc: (string) - Description of the autocmd @@ -3213,7 +3213,7 @@ nvim_do_autocmd({event}, {*opts}) *nvim_do_autocmd()* "*". • NOTE: Cannot be used with {buffer} - • group (string) - autocmd group name + • group (string|int) - autocmd group name or id • modeline (boolean) - Default true, see |<nomodeline>| @@ -3224,9 +3224,14 @@ nvim_get_autocmds({*opts}) *nvim_get_autocmds()* {opts} Optional Parameters: • event : Name or list of name of events to match against - • group (string): Name of group to match against - • pattern: Pattern or list of patterns to match + • group (string|int): Name or id of group to match against + • pattern: Pattern or list of patterns to match + against. Cannot be used with {buffer} + • buffer: Buffer number or list of buffer numbers + for buffer local autocommands + |autocmd-buflocal|. Cannot be used with + {pattern} Return: ~ A list of autocmds that match diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 03a5f98c6d..5b0c7918e0 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -4900,7 +4900,7 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()* empty list is returned. If length of {str} is greater than 256, then returns an empty list. - Refer to |fuzzy-match| for more information about fuzzy + Refer to |fuzzy-matching| for more information about fuzzy matching strings. Example: > @@ -7937,8 +7937,11 @@ synIDattr({synID}, {what} [, {mode}]) *synIDattr()* "inverse" "1" if inverse (= reverse) "standout" "1" if standout "underline" "1" if underlined + "underlineline" "1" if double underlined "undercurl" "1" if undercurled - "strikethrough" "1" if struckthrough + "underdot" "1" if dotted underlined + "underdash" "1" if dashed underlined + "strikethrough" "1" if struckthrough Example (echoes the color of the syntax item under the cursor): > diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index e14427494d..d4bed7a5f2 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -229,12 +229,12 @@ prompt. > call chansend(g:shell_job, [a:text, '']) endfunc - " Function handling output from the shell: Added above the prompt. + " Function handling output from the shell: Add it above the prompt. func GotOutput(channel, msg, name) call append(line("$") - 1, a:msg) endfunc - " Function handling the shell exit: close the window. + " Function handling the shell exits: close the window. func JobExit(job, status, event) quit! endfunc diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index 178b0dc62b..cc146fcf6e 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -204,7 +204,7 @@ Docstring format: - Use `<pre>` for code samples. Example: the help for |vim.paste()| is generated from a docstring decorating -vim.paste in src/nvim/lua/vim.lua like this: > +vim.paste in runtime/lua/vim/_editor.lua like this: > --- Paste handler, invoked by |nvim_paste()| when a conforming UI --- (such as the |TUI|) pastes text into the editor. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 3015e232a7..9fcdbe4cc3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1651,6 +1651,7 @@ Some variables can be set by the user, but the type cannot be changed. *v:argv* *argv-variable* v:argv The command line arguments Vim was invoked with. This is a list of strings. The first item is the Vim command. + See |v:progpath| for the command with full path. *v:beval_col* *beval_col-variable* v:beval_col The number of the column, over which the mouse pointer is. @@ -2027,6 +2028,9 @@ v:null Special value used to put "null" in JSON and NIL in msgpack. used as a String (e.g. in |expr5| with string concatenation operator) and to zero when used as a Number (e.g. in |expr5| or |expr7| when used with numeric operators). Read-only. + In some places `v:null` can be used for a List, Dict, etc. + that is not set. That is slightly different than an empty + List, Dict, etc. *v:numbermax* *numbermax-variable* v:numbermax Maximum value of a number. @@ -3062,16 +3066,17 @@ text... opposite of |:lockvar|. :if {expr1} *:if* *:end* *:endif* *:en* *E171* *E579* *E580* -:en[dif] Execute the commands until the next matching ":else" - or ":endif" if {expr1} evaluates to non-zero. +:en[dif] Execute the commands until the next matching `:else` + or `:endif` if {expr1} evaluates to non-zero. Although the short forms work, it is recommended to - always use `:endif` to avoid confusion. + always use `:endif` to avoid confusion and to make + auto-indenting work properly. From Vim version 4.5 until 5.0, every Ex command in - between the ":if" and ":endif" is ignored. These two + between the `:if` and `:endif` is ignored. These two commands were just to allow for future expansions in a backward compatible way. Nesting was allowed. Note - that any ":else" or ":elseif" was ignored, the "else" + that any `:else` or `:elseif` was ignored, the `else` part was not executed either. You can use this to remain compatible with older @@ -3080,32 +3085,32 @@ text... : version-5-specific-commands :endif < The commands still need to be parsed to find the - "endif". Sometimes an older Vim has a problem with a - new command. For example, ":silent" is recognized as - a ":substitute" command. In that case ":execute" can + `endif`. Sometimes an older Vim has a problem with a + new command. For example, `:silent` is recognized as + a `:substitute` command. In that case `:execute` can avoid problems: > :if version >= 600 : execute "silent 1,$delete" :endif < - NOTE: The ":append" and ":insert" commands don't work - properly in between ":if" and ":endif". + NOTE: The `:append` and `:insert` commands don't work + properly in between `:if` and `:endif`. *:else* *:el* *E581* *E583* -:el[se] Execute the commands until the next matching ":else" - or ":endif" if they previously were not being +:el[se] Execute the commands until the next matching `:else` + or `:endif` if they previously were not being executed. *:elseif* *:elsei* *E582* *E584* -:elsei[f] {expr1} Short for ":else" ":if", with the addition that there - is no extra ":endif". +:elsei[f] {expr1} Short for `:else` `:if`, with the addition that there + is no extra `:endif`. :wh[ile] {expr1} *:while* *:endwhile* *:wh* *:endw* *E170* *E585* *E588* *E733* -:endw[hile] Repeat the commands between ":while" and ":endwhile", +:endw[hile] Repeat the commands between `:while` and `:endwhile`, as long as {expr1} evaluates to non-zero. When an error is detected from a command inside the - loop, execution continues after the "endwhile". + loop, execution continues after the `endwhile`. Example: > :let lnum = 1 :while lnum <= line("$") @@ -3113,16 +3118,16 @@ text... :let lnum = lnum + 1 :endwhile < - NOTE: The ":append" and ":insert" commands don't work - properly inside a ":while" and ":for" loop. + NOTE: The `:append` and `:insert` commands don't work + properly inside a `:while` and `:for` loop. :for {var} in {object} *:for* *E690* *E732* :endfo[r] *:endfo* *:endfor* - Repeat the commands between ":for" and ":endfor" for + Repeat the commands between `:for` and `:endfor` for each item in {object}. {object} can be a |List| or a |Blob|. Variable {var} is set to the value of each item. When an error is detected for a command inside - the loop, execution continues after the "endfor". + the loop, execution continues after the `endfor`. Changing {object} inside the loop affects what items are used. Make a copy if this is unwanted: > :for item in copy(mylist) @@ -3146,7 +3151,7 @@ text... :for [{var1}, {var2}, ...] in {listlist} :endfo[r] - Like ":for" above, but each item in {listlist} must be + Like `:for` above, but each item in {listlist} must be a list, of which each item is assigned to {var1}, {var2}, etc. Example: > :for [lnum, col] in [[1, 3], [2, 5], [3, 8]] @@ -3154,38 +3159,39 @@ text... :endfor < *:continue* *:con* *E586* -:con[tinue] When used inside a ":while" or ":for" loop, jumps back +:con[tinue] When used inside a `:while` or `:for` loop, jumps back to the start of the loop. - If it is used after a |:try| inside the loop but - before the matching |:finally| (if present), the - commands following the ":finally" up to the matching - |:endtry| are executed first. This process applies to - all nested ":try"s inside the loop. The outermost - ":endtry" then jumps back to the start of the loop. + + If it is used after a `:try` inside the loop but + before the matching `:finally` (if present), the + commands following the `:finally` up to the matching + `:endtry` are executed first. This process applies to + all nested `:try`s inside the loop. The outermost + `:endtry` then jumps back to the start of the loop. *:break* *:brea* *E587* -:brea[k] When used inside a ":while" or ":for" loop, skips to - the command after the matching ":endwhile" or - ":endfor". - If it is used after a |:try| inside the loop but - before the matching |:finally| (if present), the - commands following the ":finally" up to the matching - |:endtry| are executed first. This process applies to - all nested ":try"s inside the loop. The outermost - ":endtry" then jumps to the command after the loop. +:brea[k] When used inside a `:while` or `:for` loop, skips to + the command after the matching `:endwhile` or + `:endfor`. + If it is used after a `:try` inside the loop but + before the matching `:finally` (if present), the + commands following the `:finally` up to the matching + `:endtry` are executed first. This process applies to + all nested `:try`s inside the loop. The outermost + `:endtry` then jumps to the command after the loop. :try *:try* *:endt* *:endtry* *E600* *E601* *E602* :endt[ry] Change the error handling for the commands between - ":try" and ":endtry" including everything being - executed across ":source" commands, function calls, + `:try` and `:endtry` including everything being + executed across `:source` commands, function calls, or autocommand invocations. When an error or interrupt is detected and there is - a |:finally| command following, execution continues - after the ":finally". Otherwise, or when the - ":endtry" is reached thereafter, the next - (dynamically) surrounding ":try" is checked for - a corresponding ":finally" etc. Then the script + a `:finally` command following, execution continues + after the `:finally`. Otherwise, or when the + `:endtry` is reached thereafter, the next + (dynamically) surrounding `:try` is checked for + a corresponding `:finally` etc. Then the script processing is terminated. Whether a function definition has an "abort" argument does not matter. Example: > @@ -3193,9 +3199,9 @@ text... echomsg "not reached" < Moreover, an error or interrupt (dynamically) inside - ":try" and ":endtry" is converted to an exception. It - can be caught as if it were thrown by a |:throw| - command (see |:catch|). In this case, the script + `:try` and `:endtry` is converted to an exception. It + can be caught as if it were thrown by a `:throw` + command (see `:catch`). In this case, the script processing is not terminated. The value "Vim:Interrupt" is used for an interrupt @@ -3211,11 +3217,11 @@ text... try | edit | catch /^Vim(edit):E\d\+/ | echo "error" | endtry < *:cat* *:catch* *E603* *E604* *E605* -:cat[ch] /{pattern}/ The following commands until the next |:catch|, - |:finally|, or |:endtry| that belongs to the same - |:try| as the ":catch" are executed when an exception +:cat[ch] /{pattern}/ The following commands until the next `:catch`, + `:finally`, or `:endtry` that belongs to the same + `:try` as the `:catch` are executed when an exception matching {pattern} is being thrown and has not yet - been caught by a previous ":catch". Otherwise, these + been caught by a previous `:catch`. Otherwise, these commands are skipped. When {pattern} is omitted all errors are caught. Examples: > @@ -3239,27 +3245,27 @@ text... locales. *:fina* *:finally* *E606* *E607* -:fina[lly] The following commands until the matching |:endtry| +:fina[lly] The following commands until the matching `:endtry` are executed whenever the part between the matching - |:try| and the ":finally" is left: either by falling - through to the ":finally" or by a |:continue|, - |:break|, |:finish|, or |:return|, or by an error or - interrupt or exception (see |:throw|). + `:try` and the `:finally` is left: either by falling + through to the `:finally` or by a `:continue`, + `:break`, `:finish`, or `:return`, or by an error or + interrupt or exception (see `:throw`). *:th* *:throw* *E608* :th[row] {expr1} The {expr1} is evaluated and thrown as an exception. - If the ":throw" is used after a |:try| but before the - first corresponding |:catch|, commands are skipped - until the first ":catch" matching {expr1} is reached. - If there is no such ":catch" or if the ":throw" is - used after a ":catch" but before the |:finally|, the - commands following the ":finally" (if present) up to - the matching |:endtry| are executed. If the ":throw" - is after the ":finally", commands up to the ":endtry" - are skipped. At the ":endtry", this process applies - again for the next dynamically surrounding ":try" + If the `:throw` is used after a `:try` but before the + first corresponding `:catch`, commands are skipped + until the first `:catch` matching {expr1} is reached. + If there is no such `:catch` or if the `:throw` is + used after a `:catch` but before the `:finally`, the + commands following the `:finally` (if present) up to + the matching `:endtry` are executed. If the `:throw` + is after the `:finally`, commands up to the `:endtry` + are skipped. At the `:endtry`, this process applies + again for the next dynamically surrounding `:try` (which may be found in a calling function or sourcing - script), until a matching ":catch" has been found. + script), until a matching `:catch` has been found. If the exception is not caught, the command processing is terminated. Example: > @@ -3274,7 +3280,7 @@ text... Also see |:comment|. Use "\n" to start a new line. Use "\r" to move the cursor to the first column. - Uses the highlighting set by the |:echohl| command. + Uses the highlighting set by the `:echohl` command. Cannot be followed by a comment. Example: > :echo "the value of 'shell' is" &shell @@ -3283,9 +3289,9 @@ text... And since Vim mostly postpones redrawing until it's finished with a sequence of commands this happens quite often. To avoid that a command from before the - ":echo" causes a redraw afterwards (redraws are often + `:echo` causes a redraw afterwards (redraws are often postponed until you type something), force a redraw - with the |:redraw| command. Example: > + with the `:redraw` command. Example: > :new | redraw | echo "there is a new window" < *:echo-self-refer* When printing nested containers echo prints second @@ -3304,13 +3310,13 @@ text... *:echon* :echon {expr1} .. Echoes each {expr1}, without anything added. Also see |:comment|. - Uses the highlighting set by the |:echohl| command. + Uses the highlighting set by the `:echohl` command. Cannot be followed by a comment. Example: > :echon "the value of 'shell' is " &shell < - Note the difference between using ":echo", which is a - Vim command, and ":!echo", which is an external shell + Note the difference between using `:echo`, which is a + Vim command, and `:!echo`, which is an external shell command: > :!echo % --> filename < The arguments of ":!" are expanded, see |:_%|. > @@ -3326,8 +3332,8 @@ text... *:echoh* *:echohl* :echoh[l] {name} Use the highlight group {name} for the following - |:echo|, |:echon| and |:echomsg| commands. Also used - for the |input()| prompt. Example: > + `:echo`, `:echon` and `:echomsg` commands. Also used + for the `input()` prompt. Example: > :echohl WarningMsg | echo "Don't panic!" | echohl None < Don't forget to set the group back to "None", otherwise all following echo's will be highlighted. @@ -3336,14 +3342,14 @@ text... :echom[sg] {expr1} .. Echo the expression(s) as a true message, saving the message in the |message-history|. Spaces are placed between the arguments as with the - |:echo| command. But unprintable characters are + `:echo` command. But unprintable characters are displayed, not interpreted. - The parsing works slightly different from |:echo|, - more like |:execute|. All the expressions are first + The parsing works slightly different from `:echo`, + more like `:execute`. All the expressions are first evaluated and concatenated before echoing anything. If expressions does not evaluate to a Number or String, string() is used to turn it into a string. - Uses the highlighting set by the |:echohl| command. + Uses the highlighting set by the `:echohl` command. Example: > :echomsg "It's a Zizzer Zazzer Zuzz, as you can plainly see." < See |:echo-redraw| to avoid the message disappearing @@ -3353,12 +3359,12 @@ text... message in the |message-history|. When used in a script or function the line number will be added. Spaces are placed between the arguments as with the - |:echomsg| command. When used inside a try conditional, + `:echomsg` command. When used inside a try conditional, the message is raised as an error exception instead (see |try-echoerr|). Example: > :echoerr "This script just failed!" -< If you just want a highlighted message use |:echohl|. +< If you just want a highlighted message use `:echohl`. And to get a beep: > :exe "normal \<Esc>" < diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 39682a2ab2..d9aecfe4fd 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -269,8 +269,8 @@ start allow backspacing over the start position of insert; CTRL-W and When 'backspace' is empty, Vi compatible backspacing is used. You cannot backspace over autoindent, before column 1 or before where insert started. -For backwards compatibility the values "0", "1" and "2" are also allowed, see -|'backspace'|. +For backwards compatibility the values "0", "1", "2" and "3" are also allowed, +see |'backspace'|. If the 'backspace' option does contain "eol" and the cursor is in column 1 when one of the three keys is used, the current line is joined with the @@ -798,6 +798,7 @@ CTRL-X CTRL-K Search the files given with the 'dictionary' option the 'dictionary' option is empty. For suggestions where to find a list of words, see the 'dictionary' option. + 'ignorecase', 'smartcase' and 'infercase' apply. CTRL-K or CTRL-N Search forward for next matching keyword. This diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index c3bd5baff2..680b853dab 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -153,9 +153,10 @@ index, on which the cursor is. This can look like this: > Note: the count does not take offset into account. When no match is found you get the error: *E486* Pattern not found -Note that for the |:global| command this behaves like a normal message, for Vi -compatibility. For the |:s| command the "e" flag can be used to avoid the -error message |:s_flags|. +Note that for the `:global` command, you get a normal message "Pattern not +found", for Vi compatibility. +For the |:s| command the "e" flag can be used to avoid the error message +|:s_flags|. *search-offset* *{offset}* These commands search for the specified pattern. With "/" and "?" an @@ -1422,7 +1423,7 @@ Finally, these constructs are unique to Perl: ":2match" for another plugin. ============================================================================== -11. Fuzzy matching *fuzzy-match* +11. Fuzzy matching *fuzzy-matching* Fuzzy matching refers to matching strings using a non-exact search string. Fuzzy matching will match a string, if all the characters in the search string diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 601384a71f..0345b14b7a 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -642,22 +642,25 @@ instead. If the buffer in the used window has changed, and the error is in another file, jumping to the error will fail. You will first have to make sure the window contains a buffer which can be abandoned. -The following steps are used to find a window to open the file selected from -the quickfix window: -1. If 'switchbuf' contains "usetab", then find a window in any tabpage - (starting with the first tabpage) that has the selected file and jump to - it. -2. Otherwise find a window displaying the selected file in the current tab - page (starting with the window before the quickfix window) and use it. -3. Otherwise find a window displaying a normal buffer ('buftype' is empty) - starting with the window before the quickfix window. If a window is found, - open the file in that window. -4. If a usable window is not found and 'switchbuf' contains "uselast", then - open the file in the last used window. -5. Otherwise open the file in the window before the quickfix window. If there - is no previous window, then open the file in the next window. -6. If a usable window is not found in the above steps, then create a new - horizontally split window above the quickfix window and open the file. +When you select a file from the quickfix window, the following steps are used +to find a window to edit the file: + +1. If a window displaying the selected file is present in the current tabpage + (starting with the window before the quickfix window), then that window is + used. +2. If the above step fails and if 'switchbuf' contains "usetab" and a window + displaying the selected file is present in any one of the tabpages + (starting with the first tabpage) then that window is used. +3. If the above step fails then a window in the current tabpage displaying a + buffer with 'buftype' not set (starting with the window before the quickfix + window) is used. +4. If the above step fails and if 'switchbuf' contains "uselast", then the + previously accessed window is used. +5. If the above step fails then the window before the quickfix window is used. + If there is no previous window, then the window after the quickfix window + is used. +6. If the above step fails, then a new horizontally split window above the + quickfix window is used. *CTRL-W_<Enter>* *CTRL-W_<CR>* You can use CTRL-W <Enter> to open a new window and jump to the error there. @@ -697,13 +700,15 @@ this window, the displayed location list is used. When you select a file from the location list window, the following steps are used to find a window to edit the file: -1. If a window with the location list displayed in the location list window is - present, then the file is opened in that window. -2. If the above step fails and if the file is already opened in another - window, then that window is used. -3. If the above step fails then an existing window showing a buffer with - 'buftype' not set is used. -4. If the above step fails, then the file is edited in a new window. +1. If a non-quickfix window associated with the location list is present in + the current tabpage, then that window is used. +2. If the above step fails and if the file is already opened in another window + in the current tabpage, then that window is used. +3. If the above step fails and 'switchbuf' contains "usetab" and if the file + is opened in a window in any one of the tabpages, then that window is used. +4. If the above step fails then a window in the current tabpage showing a + buffer with 'buftype' not set is used. +5. If the above step fails, then the file is edited in a new window. In all of the above cases, if the location list for the selected window is not yet set, then it is set to the location list displayed in the location list @@ -1036,7 +1041,7 @@ commands can be combined to create a NewGrep command: > matching is used to find matching lines. In this case, {pattern} is treated as a literal string instead of a regular expression. See - |fuzzy-match| for more information about fuzzy + |fuzzy-matching| for more information about fuzzy matching strings. |QuickFixCmdPre| and |QuickFixCmdPost| are triggered. diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 05529dc90a..994f97bba0 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -161,6 +161,11 @@ Q Repeat the last recorded register [count] times. result of evaluating the expression is executed as an Ex command. Mappings are not recognized in these commands. + When the |line-continuation| character (\) is present + at the beginning of a line in a linewise register, + then it is combined with the previous line. This is + useful for yanking and executing parts of a Vim + script. *:@:* :[addr]@: Repeat last command-line. First set cursor at line diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 778f829a4e..c8280173fb 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4388,7 +4388,7 @@ Leading context *:syn-lc* *:syn-leading* *:syn-context* Note: This is an obsolete feature, only included for backwards compatibility with previous Vim versions. It's now recommended to use the |/\@<=| construct -in the pattern. +in the pattern. You can also often use |/\zs|. The "lc" offset specifies leading context -- a part of the pattern that must be present, but is not considered part of the match. An offset of "lc=n" will @@ -4875,7 +4875,8 @@ the same syntax file on all UIs. 1. TUI highlight arguments - *bold* *underline* *undercurl* + *bold* *underline* *underlineline* + *undercurl* *underdot* *underdash* *inverse* *italic* *standout* *nocombine* *strikethrough* cterm={attr-list} *attr-list* *highlight-cterm* *E418* @@ -4883,7 +4884,10 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418* following items (in any order): bold underline + underlineline double underline undercurl curly underline + underdot dotted underline + underdash dashed underline strikethrough reverse inverse same as reverse @@ -4894,8 +4898,9 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418* Note that "bold" can be used here and by using a bold font. They have the same effect. - "undercurl" falls back to "underline" in a terminal that does not - support it. The color is set using |highlight-guisp|. + "underlineline", "undercurl", "underdot", and "underdash" fall back + to "underline" in a terminal that does not support them. The color is + set using |highlight-guisp|. start={term-list} *highlight-start* *E422* stop={term-list} *term-list* *highlight-stop* @@ -5028,8 +5033,8 @@ guifg={color-name} *highlight-guifg* guibg={color-name} *highlight-guibg* guisp={color-name} *highlight-guisp* These give the foreground (guifg), background (guibg) and special - (guisp) color to use in the GUI. "guisp" is used for undercurl - and underline. + (guisp) color to use in the GUI. "guisp" is used for various + underlines. There are a few special names: NONE no color (transparent) bg use normal background color diff --git a/runtime/doc/uganda.txt b/runtime/doc/uganda.txt index 79519da51e..23dfa082a0 100644 --- a/runtime/doc/uganda.txt +++ b/runtime/doc/uganda.txt @@ -129,11 +129,12 @@ Kibaale Children's Centre *kcc* *Kibaale* *charity* Kibaale Children's Centre (KCC) is located in Kibaale, a small town in the south of Uganda, near Tanzania, in East Africa. The area is known as Rakai District. The population is mostly farmers. Although people are poor, there -is enough food. But this district is suffering from AIDS more than any other -part of the world. Some say that it started there. Estimations are that 10 -to 30% of the Ugandans are infected with HIV. Because parents die, there are -many orphans. In this district about 60,000 children have lost one or both -parents, out of a population of 350,000. And this is still continuing. +usually is enough food. But this district is suffering from AIDS more than +any other part of the world. Some say that it started there. Estimations are +that in the past 10 to 30% of the Ugandans are infected with HIV. Because +parents die, there are many orphans. In this district about 60,000 children +have lost one or both parents, out of a population of 350,000. Although AIDS +is now mostly under control, the problems are still continuing. The children need a lot of help. The KCC is working hard to provide the needy with food, medical care and education. Food and medical care to keep them diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index e7be14e732..c5e3b60079 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -283,19 +283,24 @@ numerical highlight ids to the actual attributes. attributes specified by the `rgb_attr` and `cterm_attr` dicts, with the following (all optional) keys. - `foreground`: foreground color. - `background`: background color. - `special`: color to use for underline and undercurl, when present. - `reverse`: reverse video. Foreground and background colors are - switched. - `italic`: italic text. - `bold`: bold text. - `strikethrough`: struckthrough text. - `underline`: underlined text. The line has `special` color. - `undercurl`: undercurled text. The curl has `special` color. - `blend`: Blend level (0-100). Could be used by UIs to support - blending floating windows to the background or to - signal a transparent cursor. + `foreground`: foreground color. + `background`: background color. + `special`: color to use for various underlines, when + present. + `reverse`: reverse video. Foreground and background colors + are switched. + `italic`: italic text. + `bold`: bold text. + `strikethrough`: struckthrough text. + `underline`: underlined text. The line has `special` color. + `underlineline`: double underlined text. The lines have `special` + color. + `undercurl`: undercurled text. The curl has `special` color. + `underdot`: underdotted text. The dots have `special` color. + `underdash`: underdashed text. The dashes have `special` color. + `blend`: Blend level (0-100). Could be used by UIs to + support blending floating windows to the + background or to signal a transparent cursor. For absent color keys the default color should be used. Don't store the default value in the table, rather a sentinel value, so that @@ -444,14 +449,17 @@ is not active. New UIs should implement |ui-linegrid| instead. `foreground`: foreground color. `background`: background color. - `special`: color to use for underline and undercurl, when present. + `special`: color to use for various underlines, when present. `reverse`: reverse video. Foreground and background colors are switched. `italic`: italic text. `bold`: bold text. `strikethrough`: struckthrough text. `underline`: underlined text. The line has `special` color. + `underlineline`: double underlined text. The lines have `special` color. `undercurl`: undercurled text. The curl has `special` color. + `underdot`: underdotted text. The dots have `special` color. + `underdash`: underdashed text. The dashes have `special` color. ["put", text] The (utf-8 encoded) string `text` is put at the cursor position diff --git a/runtime/ftplugin/ant.vim b/runtime/ftplugin/ant.vim index 5905858896..aee07ca4b9 100644 --- a/runtime/ftplugin/ant.vim +++ b/runtime/ftplugin/ant.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: ant -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/aspvbs.vim b/runtime/ftplugin/aspvbs.vim index 660dab4685..70a130d287 100644 --- a/runtime/ftplugin/aspvbs.vim +++ b/runtime/ftplugin/aspvbs.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: aspvbs -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/config.vim b/runtime/ftplugin/config.vim index 7fde42ebf5..73136cbc66 100644 --- a/runtime/ftplugin/config.vim +++ b/runtime/ftplugin/config.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: config -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/csc.vim b/runtime/ftplugin/csc.vim index 3a09c3bf8b..7b4126a503 100644 --- a/runtime/ftplugin/csc.vim +++ b/runtime/ftplugin/csc.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: csc -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 diff --git a/runtime/ftplugin/csh.vim b/runtime/ftplugin/csh.vim index 929823219c..ca5da5a8b9 100644 --- a/runtime/ftplugin/csh.vim +++ b/runtime/ftplugin/csh.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: csh " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" Previous Maintainer: Dan Sharp " Contributor: Johannes Zellner <johannes@zellner.org> " Last Change: 2021 Oct 15 diff --git a/runtime/ftplugin/dtd.vim b/runtime/ftplugin/dtd.vim index 6c08f6691d..a046118c70 100644 --- a/runtime/ftplugin/dtd.vim +++ b/runtime/ftplugin/dtd.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: dtd -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 diff --git a/runtime/ftplugin/html.vim b/runtime/ftplugin/html.vim index 7579080ea5..3179aa2e88 100644 --- a/runtime/ftplugin/html.vim +++ b/runtime/ftplugin/html.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: html -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim index 292cb6b166..74c8e8d1c1 100644 --- a/runtime/ftplugin/java.vim +++ b/runtime/ftplugin/java.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: Java -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Change: 2012 Mar 11 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 diff --git a/runtime/ftplugin/jsp.vim b/runtime/ftplugin/jsp.vim index fbba863b32..18136ccc24 100644 --- a/runtime/ftplugin/jsp.vim +++ b/runtime/ftplugin/jsp.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: jsp -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/pascal.vim b/runtime/ftplugin/pascal.vim index 2de92563ae..aba1e54f27 100644 --- a/runtime/ftplugin/pascal.vim +++ b/runtime/ftplugin/pascal.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Pascal " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" Previous Maintainer: Dan Sharp " Last Change: 2021 Apr 23 if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/php.vim b/runtime/ftplugin/php.vim index a2f8b4d8d3..3ff0828ffe 100644 --- a/runtime/ftplugin/php.vim +++ b/runtime/ftplugin/php.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: php -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/sgml.vim b/runtime/ftplugin/sgml.vim index bf63efbf1f..ef52125c68 100644 --- a/runtime/ftplugin/sgml.vim +++ b/runtime/ftplugin/sgml.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: sgml -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim index 593fcec927..93a46f63e2 100644 --- a/runtime/ftplugin/sh.vim +++ b/runtime/ftplugin/sh.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: sh -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 diff --git a/runtime/ftplugin/svg.vim b/runtime/ftplugin/svg.vim index 8fff6ea32c..6f16b1a0f4 100644 --- a/runtime/ftplugin/svg.vim +++ b/runtime/ftplugin/svg.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: svg -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/tcsh.vim b/runtime/ftplugin/tcsh.vim index 33f1aabf68..85d3873b33 100644 --- a/runtime/ftplugin/tcsh.vim +++ b/runtime/ftplugin/tcsh.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: tcsh " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" Previous Maintainer: Dan Sharp " Last Change: 2021 Oct 15 if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/xhtml.vim b/runtime/ftplugin/xhtml.vim index 21ed3e1100..d2a1c0b566 100644 --- a/runtime/ftplugin/xhtml.vim +++ b/runtime/ftplugin/xhtml.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: xhtml -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/xml.vim b/runtime/ftplugin/xml.vim index 1d43521155..9aa188cecc 100644 --- a/runtime/ftplugin/xml.vim +++ b/runtime/ftplugin/xml.vim @@ -3,7 +3,7 @@ " Maintainer: Christian Brabandt <cb@256bit.org> " Last Changed: Dec 07th, 2018 " Repository: https://github.com/chrisbra/vim-xml-ftplugin -" Previous Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" Previous Maintainer: Dan Sharp " URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/xsd.vim b/runtime/ftplugin/xsd.vim index 6a4a193656..7d3efbb390 100644 --- a/runtime/ftplugin/xsd.vim +++ b/runtime/ftplugin/xsd.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: xsd -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/ftplugin/xslt.vim b/runtime/ftplugin/xslt.vim index 1a5ee62865..9d2def107b 100644 --- a/runtime/ftplugin/xslt.vim +++ b/runtime/ftplugin/xslt.vim @@ -1,8 +1,10 @@ " Vim filetype plugin file " Language: xslt -" Maintainer: Dan Sharp <dwsharp at users dot sourceforge dot net> +" +" This runtime file is looking for a new maintainer. +" +" Former maintainer: Dan Sharp " Last Changed: 20 Jan 2009 -" URL: http://dwsharp.users.sourceforge.net/vim/ftplugin if exists("b:did_ftplugin") | finish | endif diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index f5a94940bd..cd2d4982d8 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: 2022 Feb 23 +" Last Change: 2022 Mar 01 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -105,7 +105,7 @@ function GetVimIndentIntern() " terminated dict. " ":function" starts a block but "function(" doesn't. if prev_text !~ '^\s*au\%[tocmd]' && prev_text !~ '^\s*{.*}' - let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|def\|el\%[seif]\)\>\|fu\%[nction]\s\)') + let i = match(prev_text, '\(^\||\)\s*\(export\s\+\)\?\({\|\(if\|wh\%[ile]\|for\|try\|cat\%[ch]\|fina\|finall\%[y]\|def\|el\%[seif]\)\>\|fu\%[nction][! ]\)') if i >= 0 let ind += shiftwidth() if strpart(prev_text, i, 1) == '|' && has('syntax_items') diff --git a/src/nvim/lua/vim.lua b/runtime/lua/vim/_editor.lua index f5f293939b..2251aca004 100644 --- a/src/nvim/lua/vim.lua +++ b/runtime/lua/vim/_editor.lua @@ -3,9 +3,11 @@ -- Lua code lives in one of three places: -- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the -- `inspect` and `lpeg` modules. --- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. --- (This will go away if we migrate to nvim as the test-runner.) --- 3. src/nvim/lua/: Compiled-into Nvim itself. +-- 2. runtime/lua/vim/shared.lua: pure lua functions which always +-- are available. Used in the test runner, as well as worker threads +-- and processes launched from Nvim. +-- 3. runtime/lua/vim/_editor.lua: Code which directly interacts with +-- the Nvim editor state. Only available in the main thread. -- -- Guideline: "If in doubt, put it in the runtime". -- @@ -34,45 +36,20 @@ -- - https://github.com/bakpakin/Fennel (pretty print, repl) -- - https://github.com/howl-editor/howl/tree/master/lib/howl/util -local vim = vim -assert(vim) -assert(vim.inspect) +local vim = assert(vim) -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c -setmetatable(vim, { - __index = function(t, key) - if key == 'treesitter' then - t.treesitter = require('vim.treesitter') - return t.treesitter - elseif key == 'filetype' then - t.filetype = require('vim.filetype') - return t.filetype - elseif key == 'F' then - t.F = require('vim.F') - return t.F - elseif require('vim.uri')[key] ~= nil then - -- Expose all `vim.uri` functions on the `vim` module. - t[key] = require('vim.uri')[key] - return t[key] - elseif key == 'lsp' then - t.lsp = require('vim.lsp') - return t.lsp - elseif key == 'highlight' then - t.highlight = require('vim.highlight') - return t.highlight - elseif key == 'diagnostic' then - t.diagnostic = require('vim.diagnostic') - return t.diagnostic - elseif key == 'keymap' then - t.keymap = require('vim.keymap') - return t.keymap - elseif key == 'ui' then - t.ui = require('vim.ui') - return t.ui - end - end -}) +for k,v in pairs { + treesitter=true; + filetype = true; + F=true; + lsp=true; + highlight=true; + diagnostic=true; + keymap=true; + ui=true; +} do vim._submodules[k] = v end vim.log = { levels = { @@ -244,12 +221,6 @@ function vim.schedule_wrap(cb) end) end ---- <Docs described in |vim.empty_dict()| > ----@private -function vim.empty_dict() - return setmetatable({}, vim._empty_dict_mt) -end - -- vim.fn.{func}(...) vim.fn = setmetatable({}, { __index = function(t, key) diff --git a/runtime/lua/vim/_load_package.lua b/runtime/lua/vim/_init_packages.lua index 59bca9b148..7d27741f1b 100644 --- a/runtime/lua/vim/_load_package.lua +++ b/runtime/lua/vim/_init_packages.lua @@ -48,5 +48,36 @@ end -- Insert vim._load_package after the preloader at position 2 table.insert(package.loaders, 2, vim._load_package) --- should always be available -vim.inspect = require'vim.inspect' +-- builtin functions which always should be available +require'vim.shared' + +vim._submodules = {inspect=true} + +-- These are for loading runtime modules in the vim namespace lazily. +setmetatable(vim, { + __index = function(t, key) + if vim._submodules[key] then + t[key] = require('vim.'..key) + return t[key] + elseif vim.startswith(key, 'uri_') then + local val = require('vim.uri')[key] + if val ~= nil then + -- Expose all `vim.uri` functions on the `vim` module. + t[key] = val + return t[key] + end + end + end +}) + +--- <Docs described in |vim.empty_dict()| > +---@private +--- TODO: should be in vim.shared when vim.shared always uses nvim-lua +function vim.empty_dict() + return setmetatable({}, vim._empty_dict_mt) +end + +-- only on main thread: functions for interacting with editor state +if not vim.is_thread() then + require'vim._editor' +end diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 0f4e5b572b..9955fff3e2 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -131,13 +131,30 @@ end ---@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@returns table<int, int> line_idx, byte_idx, and char_idx of first change position local function compute_start_range(prev_lines, curr_lines, firstline, lastline, new_lastline, offset_encoding) + local char_idx + local byte_idx -- If firstline == lastline, no existing text is changed. All edit operations -- 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 == lastline then + local line_idx + local line = prev_lines[firstline - 1] + if line then + line_idx = firstline - 1 + byte_idx = #line + 1 + char_idx = compute_line_length(line, offset_encoding) + 1 + else + line_idx = firstline + byte_idx = 1 + char_idx = 1 + end + return { line_idx = line_idx, byte_idx = byte_idx, char_idx = char_idx } + end + -- 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 + if firstline == new_lastline then return { line_idx = firstline, byte_idx = 1, char_idx = 1 } end @@ -158,8 +175,6 @@ local function compute_start_range(prev_lines, curr_lines, firstline, lastline, end -- Convert byte to codepoint if applicable - local char_idx - local byte_idx if start_byte_idx == 1 or (#prev_line == 0 and start_byte_idx == 1)then byte_idx = start_byte_idx char_idx = 1 diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index e170befa4c..8124b23eb1 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -1,8 +1,10 @@ -- Functions shared by Nvim and its test-suite. -- --- The singular purpose of this module is to share code with the Nvim --- test-suite. If, in the future, Nvim itself is used to run the test-suite --- instead of "vanilla Lua", these functions could move to src/nvim/lua/vim.lua +-- These are "pure" lua functions not depending of the state of the editor. +-- Thus they should always be available whenever nvim-related lua code is run, +-- regardless if it is code in the editor itself, or in worker threads/processes, +-- or the test suite. (Eventually the test suite will be run in a worker process, +-- so this wouldn't be a separate case to consider) local vim = vim or {} diff --git a/runtime/menu.vim b/runtime/menu.vim index 701dd33cdd..956b941971 100644 --- a/runtime/menu.vim +++ b/runtime/menu.vim @@ -569,9 +569,9 @@ func! s:XxdConv() %!mc vim:xxd else call s:XxdFind() - exe '%!' . g:xxdprogram + exe ':%!' . g:xxdprogram endif - if getline(1) =~ "^0000000:" " only if it worked + if getline(1) =~ "^00000000:" " only if it worked set ft=xxd endif let &mod = mod @@ -583,7 +583,7 @@ func! s:XxdBack() %!mc vim:xxd -r else call s:XxdFind() - exe '%!' . g:xxdprogram . ' -r' + exe ':%!' . g:xxdprogram . ' -r' endif set ft= doautocmd filetypedetect BufReadPost diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 41993b65b0..0bc233fc2a 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -46,14 +46,14 @@ syn match vimTermOption contained "t_%i" syn match vimTermOption contained "t_k;" " unsupported settings: these are supported by vi but don't do anything in vim {{{2 -syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600 +syn keyword vimErrSetting contained hardtabs ht w1200 w300 w9600 "}}}2 syn case ignore " Highlight commonly used Groupnames {{{2 -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 +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 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 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 @@ -88,10 +88,10 @@ if exists("g:vimsyn_folding") && g:vimsyn_folding =~# '[afhlmpPrt]' else com! -nargs=* VimFoldm <args> endif - if g:vimsyn_folding =~# 'p' - com! -nargs=* VimFoldp <args> fold - else - com! -nargs=* VimFoldp <args> + if g:vimsyn_folding =~# 'p' + com! -nargs=* VimFoldp <args> fold + else + com! -nargs=* VimFoldp <args> endif if g:vimsyn_folding =~# 'P' com! -nargs=* VimFoldP <args> fold @@ -148,8 +148,8 @@ syn match vimNumber '-\d\+\%(\.\d\+\%([eE][+-]\=\d\+\)\=\)\=' skipwhite nextgro syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment -syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment -syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment +syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment +syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment " All vimCommands are contained by vimIsCommand. {{{2 syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAddress,vimAutoCmd,vimEcho,vimIsCommand,vimExtCmd,vimFilter,vimLet,vimMap,vimMark,vimSet,vimSyntax,vimUserCmd @@ -574,7 +574,7 @@ syn match vimHiBang contained "!" skipwhite nextgroup=@vimHighlightCluster syn match vimHiGroup contained "\i\+" syn case ignore -syn keyword vimHiAttrib contained none bold inverse italic nocombine reverse standout strikethrough underline undercurl +syn keyword vimHiAttrib contained none bold inverse italic nocombine reverse standout strikethrough underline underlineline undercurl underdot underdash syn keyword vimFgBgAttrib contained none bg background fg foreground syn case match syn match vimHiAttribList contained "\i\+" contains=vimHiAttrib diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 7b6d974181..af49d57492 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -124,7 +124,7 @@ CONFIG = { 'filename': 'lua.txt', 'section_start_token': '*lua-vim*', 'section_order': [ - 'vim.lua', + '_editor.lua', 'shared.lua', 'uri.lua', 'ui.lua', @@ -132,7 +132,7 @@ CONFIG = { 'keymap.lua', ], 'files': ' '.join([ - os.path.join(base_dir, 'src/nvim/lua/vim.lua'), + os.path.join(base_dir, 'runtime/lua/vim/_editor.lua'), os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), @@ -144,9 +144,18 @@ CONFIG = { 'section_name': { 'lsp.lua': 'core', }, - 'section_fmt': lambda name: f'Lua module: {name.lower()}', - 'helptag_fmt': lambda name: f'*lua-{name.lower()}*', - 'fn_helptag_fmt': lambda fstem, name: f'*{fstem}.{name}()*', + 'section_fmt': lambda name: ( + 'Lua module: vim' + if name.lower() == '_editor' + else f'Lua module: {name.lower()}'), + 'helptag_fmt': lambda name: ( + '*lua-vim*' + if name.lower() == '_editor' + else f'*lua-{name.lower()}*'), + 'fn_helptag_fmt': lambda fstem, name: ( + f'*vim.{name}()*' + if fstem.lower() == '_editor' + else f'*{fstem}.{name}()*'), 'module_override': { # `shared` functions are exposed on the `vim` module. 'shared': 'vim', diff --git a/scripts/stripdecls.py b/scripts/stripdecls.py deleted file mode 100755 index b4ac34dcfe..0000000000 --- a/scripts/stripdecls.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/python -# vim: set fileencoding=utf-8: - -from __future__ import print_function, unicode_literals, division - -from clang.cindex import Index, CursorKind -from collections import namedtuple, OrderedDict, defaultdict -import sys -import os - - -DECL_KINDS = { - CursorKind.FUNCTION_DECL, -} - - -Strip = namedtuple('Strip', 'start_line start_column end_line end_column') - - -def main(progname, cfname, only_static, move_all): - cfname = os.path.abspath(os.path.normpath(cfname)) - - hfname1 = os.path.splitext(cfname)[0] + os.extsep + 'h' - hfname2 = os.path.splitext(cfname)[0] + '_defs' + os.extsep + 'h' - - files_to_modify = (cfname, hfname1, hfname2) - - index = Index.create() - src_dirname = os.path.join(os.path.dirname(__file__), '..', 'src') - src_dirname = os.path.abspath(os.path.normpath(src_dirname)) - relname = os.path.join(src_dirname, 'nvim') - unit = index.parse(cfname, args=('-I' + src_dirname, - '-DUNIX', - '-DEXITFREE', - '-DFEAT_USR_CMDS', - '-DFEAT_CMDL_COMPL', - '-DFEAT_COMPL_FUNC', - '-DPROTO', - '-DUSE_MCH_ERRMSG')) - cursor = unit.cursor - - tostrip = defaultdict(OrderedDict) - definitions = set() - - for child in cursor.get_children(): - if not (child.location and child.location.file): - continue - fname = os.path.abspath(os.path.normpath(child.location.file.name)) - if fname not in files_to_modify: - continue - if child.kind not in DECL_KINDS: - continue - if only_static and next(child.get_tokens()).spelling == 'static': - continue - - if child.is_definition() and fname == cfname: - definitions.add(child.spelling) - else: - stripdict = tostrip[fname] - assert(child.spelling not in stripdict) - stripdict[child.spelling] = Strip( - child.extent.start.line, - child.extent.start.column, - child.extent.end.line, - child.extent.end.column, - ) - - for (fname, stripdict) in tostrip.items(): - if not move_all: - for name in set(stripdict) - definitions: - stripdict.pop(name) - - if not stripdict: - continue - - if fname.endswith('.h'): - is_h_file = True - include_line = next(reversed(stripdict.values())).start_line + 1 - else: - is_h_file = False - include_line = next(iter(stripdict.values())).start_line - - lines = None - generated_existed = os.path.exists(fname + '.generated.h') - with open(fname, 'rb') as F: - lines = list(F) - - stripped = [] - - for name, position in reversed(stripdict.items()): - sl = slice(position.start_line - 1, position.end_line) - if is_h_file: - include_line -= sl.stop - sl.start - stripped += lines[sl] - lines[sl] = () - - if not generated_existed: - lines[include_line:include_line] = [ - '#ifdef INCLUDE_GENERATED_DECLARATIONS\n', - '# include "{0}.generated.h"\n'.format( - os.path.relpath(fname, relname)), - '#endif\n', - ] - - with open(fname, 'wb') as F: - F.writelines(lines) - - -if __name__ == '__main__': - progname = sys.argv[0] - args = sys.argv[1:] - if not args or '--help' in args: - print('Usage:') - print('') - print(' {0} [--static [--all]] file.c...'.format(progname)) - print('') - print('Stripts all declarations from file.c, file.h and file_defs.h.') - print('If --static argument is given then only static declarations are') - print('stripped. Declarations are stripped only if corresponding') - print('definition is found unless --all argument was given.') - print('') - print('Note: it is assumed that static declarations starts with "static"') - print(' keyword.') - sys.exit(0 if args else 1) - - if args[0] == '--static': - only_static = True - args = args[1:] - else: - only_static = False - - if args[0] == '--all': - move_all = True - args = args[1:] - else: - move_all = False - - for cfname in args: - print('Processing {0}'.format(cfname)) - main(progname, cfname, only_static, move_all) diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 67a2cc96fd..591c658e6b 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -36,7 +36,7 @@ usage() { echo " can be a Vim version (8.0.xxx) or a Git hash." echo " -P {vim-revision} Download, generate and apply a Vim patch." echo " -g {vim-revision} Download a Vim patch." - echo " -s Create a vim-patch pull request." + echo " -s [pr args] Create a vim-patch pull request." echo " -r {pr-number} Review a vim-patch pull request." echo " -V Clone the Vim source code to \$VIM_SOURCE_DIR." echo @@ -329,7 +329,8 @@ stage_patch() { * Do this only for _related_ patches (otherwise it increases the size of the pull request, making it harder to review) - When you are done, try "%s -s" to create the pull request. + When you are done, try "%s -s" to create the pull request, + or "%s -s --draft" to create a draft pull request. See the wiki for more information: * https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-vim @@ -338,13 +339,19 @@ stage_patch() { } gh_pr() { - gh pr create --title "$1" --body "$2" + local pr_title + local pr_body + pr_title="$1" + pr_body="$2" + shift 2 + gh pr create --title "${pr_title}" --body "${pr_body}" "$@" } git_hub_pr() { local pr_message pr_message="$(printf '%s\n\n%s\n' "$1" "$2")" - git hub pull new -m "${pr_message}" + shift 2 + git hub pull new -m "${pr_message}" "$@" } submit_pr() { @@ -408,7 +415,7 @@ submit_pr() { fi echo "Creating pull request." - if output="$($submit_fn "$pr_title" "$pr_body" 2>&1)"; then + if output="$($submit_fn "$pr_title" "$pr_body" "$@" 2>&1)"; then msg_ok "$output" else msg_err "$output" @@ -799,7 +806,8 @@ while getopts "hlLmMVp:P:g:r:s" opt; do exit 0 ;; s) - submit_pr + shift # remove opt + submit_pr "$@" exit 0 ;; V) diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index b5f97bc485..c243f93c05 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -1110,7 +1110,7 @@ static int json_is_invalid_number(json_parse_t *json) /* Reject numbers starting with 0x, or leading zeros */ if (*p == '0') { - int ch2 = *(p + 1); + char ch2 = *(p + 1); if ((ch2 | 0x20) == 'x' || /* Hex */ ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ diff --git a/src/clint.py b/src/clint.py index 4b7bf002e6..37b44920af 100755 --- a/src/clint.py +++ b/src/clint.py @@ -191,7 +191,6 @@ _ERROR_CATEGORIES = [ 'readability/fn_size', 'readability/multiline_comment', 'readability/multiline_string', - 'readability/nolint', 'readability/nul', 'readability/todo', 'readability/utf8', @@ -298,9 +297,6 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error): if category in _ERROR_CATEGORIES: _error_suppressions.setdefault( category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) def ParseKnownErrorSuppressions(filename, raw_lines, linenum): diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 21150965f9..9f29cae29d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -57,13 +57,13 @@ set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) -set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) +set(LUA_EDITOR_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_editor.lua) set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua) set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua) set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua) -set(LUA_LOAD_PACKAGE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_load_package.lua) +set(LUA_INIT_PACKAGES_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_init_packages.lua) set(LUA_KEYMAP_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/keymap.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) @@ -332,9 +332,9 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env "LUAC_PRG=${LUAC_PRG}" ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE} - ${LUA_LOAD_PACKAGE_MODULE_SOURCE} "vim._load_package" + ${LUA_INIT_PACKAGES_MODULE_SOURCE} "vim._init_packages" ${LUA_INSPECT_MODULE_SOURCE} "vim.inspect" - ${LUA_VIM_MODULE_SOURCE} "vim" + ${LUA_EDITOR_MODULE_SOURCE} "vim._editor" ${LUA_SHARED_MODULE_SOURCE} "vim.shared" ${LUA_F_MODULE_SOURCE} "vim.F" ${LUA_META_MODULE_SOURCE} "vim._meta" @@ -342,7 +342,8 @@ add_custom_command( ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap" DEPENDS ${CHAR_BLOB_GENERATOR} - ${LUA_VIM_MODULE_SOURCE} + ${LUA_INIT_PACKAGES_MODULE_SOURCE} + ${LUA_EDITOR_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} ${LUA_F_MODULE_SOURCE} diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 0ad7b320d0..5ede0e5265 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -40,8 +40,10 @@ static int64_t next_autocmd_id = 1; /// /// @param opts Optional Parameters: /// - event : Name or list of name of events to match against -/// - group (string): Name of group to match against -/// - pattern: Pattern or list of patterns to match against +/// - group (string|int): Name or id of group to match against +/// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} +/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands +/// |autocmd-buflocal|. Cannot be used with {pattern} /// /// @return A list of autocmds that match Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) @@ -53,24 +55,34 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) char_u *pattern_filters[AUCMD_MAX_PATTERNS]; char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + Array buffers = ARRAY_DICT_INIT; + bool event_set[NUM_EVENTS] = { false }; bool check_event = false; int group = 0; - if (opts->group.type != kObjectTypeNil) { - Object v = opts->group; - if (v.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "group must be a string."); - goto cleanup; - } - - group = augroup_find(v.data.string.data); - - if (group < 0) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + group = augroup_find(opts->group.data.string.data); + if (group < 0) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + case kObjectTypeInteger: + group = (int)opts->group.data.integer; + char *name = augroup_name(group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); goto cleanup; - } } if (opts->event.type != kObjectTypeNil) { @@ -100,6 +112,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } } + if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Cannot use both 'pattern' and 'buffer'"); + goto cleanup; + } + int pattern_filter_count = 0; if (opts->pattern.type != kObjectTypeNil) { Object v = opts->pattern; @@ -107,25 +125,70 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) pattern_filters[pattern_filter_count] = (char_u *)v.data.string.data; pattern_filter_count += 1; } else if (v.type == kObjectTypeArray) { + if (v.data.array.size > AUCMD_MAX_PATTERNS) { + api_set_error(err, kErrorTypeValidation, + "Too many patterns. Please limit yourself to %d or fewer", + AUCMD_MAX_PATTERNS); + goto cleanup; + } + FOREACH_ITEM(v.data.array, item, { + if (item.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string"); + goto cleanup; + } + pattern_filters[pattern_filter_count] = (char_u *)item.data.string.data; pattern_filter_count += 1; }); } else { - api_set_error(err, - kErrorTypeValidation, + api_set_error(err, kErrorTypeValidation, "Not a valid 'pattern' value. Must be a string or an array"); goto cleanup; } + } + + if (opts->buffer.type == kObjectTypeInteger || opts->buffer.type == kObjectTypeBuffer) { + buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err); + if (ERROR_SET(err)) { + goto cleanup; + } - if (pattern_filter_count >= AUCMD_MAX_PATTERNS) { + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + } else if (opts->buffer.type == kObjectTypeArray) { + if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { api_set_error(err, kErrorTypeValidation, - "Too many patterns. Please limit yourself to less"); + "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS); goto cleanup; } + + FOREACH_ITEM(opts->buffer.data.array, bufnr, { + if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer"); + goto cleanup; + } + + buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err); + if (ERROR_SET(err)) { + goto cleanup; + } + + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "<buffer=%d>", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + }); + } else if (opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Invalid value for 'buffer': must be an integer or array of integers"); + goto cleanup; } + FOREACH_ITEM(buffers, bufnr, { + pattern_filters[pattern_filter_count] = (char_u *)bufnr.data.string.data; + pattern_filter_count += 1; + }); + FOR_ALL_AUEVENTS(event) { if (check_event && !event_set[event]) { continue; @@ -234,6 +297,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } cleanup: + api_free_array(buffers); return autocmd_list; } @@ -265,7 +329,7 @@ cleanup: /// - buffer: (bufnr) /// - create a |autocmd-buflocal| autocmd. /// - NOTE: Cannot be used with {pattern} -/// - group: (string) The augroup name +/// - group: (string|int) The augroup name or id /// - once: (boolean) - See |autocmd-once| /// - nested: (boolean) - See |autocmd-nested| /// - desc: (string) - Description of the autocmd @@ -350,23 +414,29 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - // TODO(tjdevries): accept number for namespace instead - if (opts->group.type != kObjectTypeNil) { - Object *v = &opts->group; - if (v->type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(v->data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", v->data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { @@ -568,7 +638,7 @@ void nvim_del_augroup_by_name(String name) /// - NOTE: Cannot be used with {pattern} /// - pattern (string|table) - optional, defaults to "*". /// - NOTE: Cannot be used with {buffer} -/// - group (string) - autocmd group name +/// - group (string|int) - autocmd group name or id /// - modeline (boolean) - Default true, see |<nomodeline>| void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) @@ -588,21 +658,29 @@ void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) goto cleanup; } - if (opts->group.type != kObjectTypeNil) { - if (opts->group.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(opts->group.data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", opts->group.data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->buffer.type != kObjectTypeNil) { diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 3a968f07ab..0ee8134ec4 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -445,6 +445,27 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - strict: boolean that indicates extmark should not be placed /// if the line or column value is past the end of the /// buffer or end of the line respectively. Defaults to true. +/// - sign_text: string of length 1-2 used to display in the +/// sign column. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - sign_hl_group: name of the highlight group used to +/// highlight the sign column text. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - number_hl_group: name of the highlight group used to +/// highlight the number column. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - line_hl_group: name of the highlight group used to +/// highlight the whole line. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - cursorline_hl_group: name of the highlight group used to +/// highlight the line when the cursor is on the same line +/// as the mark and 'cursorline' is enabled. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -519,10 +540,25 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - if (HAS_KEY(opts->hl_group)) { - decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err); - if (ERROR_SET(err)) { - goto error; + struct { + const char *name; + Object *opt; + int *dest; + } hls[] = { + { "hl_group" , &opts->hl_group , &decor.hl_id }, + { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id }, + { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id }, + { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id }, + { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id }, + { NULL, NULL, NULL }, + }; + + for (int j = 0; hls[j].name && hls[j].dest; j++) { + if (HAS_KEY(*hls[j].opt)) { + *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err); + if (ERROR_SET(err)) { + goto error; + } } } @@ -622,6 +658,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } + if (opts->sign_text.type == kObjectTypeString) { + if (!init_sign_text(&decor.sign_text, + (char_u *)opts->sign_text.data.string.data)) { + api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value"); + goto error; + } + } else if (HAS_KEY(opts->sign_text)) { + api_set_error(err, kErrorTypeValidation, "sign_text is not a String"); + goto error; + } + bool right_gravity = true; OPTION_TO_BOOL(right_gravity, right_gravity, true); @@ -709,6 +756,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer error: clear_virttext(&decor.virt_text); + xfree(decor.sign_text); return 0; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index b05816f8ac..435e8195dd 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -22,6 +22,11 @@ return { "virt_lines_above"; "virt_lines_leftcol"; "strict"; + "sign_text"; + "sign_hl_group"; + "number_hl_group"; + "line_hl_group"; + "cursorline_hl_group"; }; keymap = { "noremap"; @@ -83,7 +88,10 @@ return { "standout"; "strikethrough"; "underline"; + "underlineline"; "undercurl"; + "underdot"; + "underdash"; "italic"; "reverse"; "nocombine"; @@ -105,7 +113,10 @@ return { "standout"; "strikethrough"; "underline"; + "underlineline"; "undercurl"; + "underdot"; + "underdash"; "italic"; "reverse"; "nocombine"; @@ -131,6 +142,7 @@ return { "event"; "group"; "pattern"; + "buffer"; }; create_augroup = { "clear"; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 35e8589255..8056950e26 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id) } return old_current_sctx; } + +// adapted from sign.c:sign_define_init_text. +// TODO(lewis6991): Consider merging +int init_sign_text(char_u **sign_text, char_u *text) +{ + char_u *s; + + char_u *endp = text + (int)STRLEN(text); + + // Count cells and check for non-printable chars + int cells = 0; + for (s = text; s < endp; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { + return FAIL; + } + if (cells < 1) { + return OK; + } + + // Allocate one byte more if we need to pad up + // with a space. + size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); + *sign_text = vim_strnsave(text, len); + + if (cells == 1) { + STRCPY(*sign_text + len - 1, " "); + } + + return OK; +} diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7cb493f57d..a850e5c1a0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -105,15 +105,24 @@ static char_u *old_termresponse = NULL; #define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT -// Map of autocmd group names. +// Map of autocmd group names and ids. // name -> ID -static Map(String, int) augroup_map = MAP_INIT; +// ID -> name +static Map(String, int) map_augroup_name_to_id = MAP_INIT; +static Map(int, String) map_augroup_id_to_name = MAP_INIT; -static void augroup_map_del(char *name) +static void augroup_map_del(int id, char *name) { - String key = map_key(String, int)(&augroup_map, cstr_as_string(name)); - map_del(String, int)(&augroup_map, key); - api_free_string(key); + if (name != NULL) { + String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name)); + map_del(String, int)(&map_augroup_name_to_id, key); + api_free_string(key); + } + if (id > 0) { + String mapped = map_get(int, String)(&map_augroup_id_to_name, id); + api_free_string(mapped); + map_del(int, String)(&map_augroup_id_to_name, id); + } } @@ -382,12 +391,14 @@ int augroup_add(char *name) } if (existing_id == AUGROUP_DELETED) { - augroup_map_del(name); + augroup_map_del(existing_id, name); } int next_id = next_augroup_id++; - String name_copy = cstr_to_string(name); - map_put(String, int)(&augroup_map, name_copy, next_id); + String name_key = cstr_to_string(name); + String name_val = cstr_to_string(name); + map_put(String, int)(&map_augroup_name_to_id, name_key, next_id); + map_put(int, String)(&map_augroup_id_to_name, next_id, name_val); return next_id; } @@ -416,7 +427,8 @@ void augroup_del(char *name, bool stupid_legacy_mode) FOR_ALL_AUPATS_IN_EVENT(event, ap) { if (ap->group == i && ap->pat != NULL) { give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true); - map_put(String, int)(&augroup_map, cstr_as_string(name), AUGROUP_DELETED); + map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED); + augroup_map_del(ap->group, NULL); return; } } @@ -432,7 +444,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) } // Remove the group because it's not currently in use. - augroup_map_del(name); + augroup_map_del(i, name); au_cleanup(); } } @@ -445,7 +457,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) int augroup_find(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int existing_id = map_get(String, int)(&augroup_map, cstr_as_string((char *)name)); + int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name)); if (existing_id == AUGROUP_DELETED) { return existing_id; } @@ -487,13 +499,10 @@ char *augroup_name(int group) return NULL; } - String key; - int value; - map_foreach(&augroup_map, key, value, { - if (value == group) { - return key.data; - } - }); + String key = map_get(int, String)(&map_augroup_id_to_name, group); + if (key.data != NULL) { + return key.data; + } // If it's not in the map anymore, then it must have been deleted. return (char *)get_deleted_augroup(); @@ -526,7 +535,7 @@ void do_augroup(char_u *arg, int del_group) String name; int value; - map_foreach(&augroup_map, name, value, { + map_foreach(&map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -556,11 +565,17 @@ void free_all_autocmds(void) // Delete the augroup_map, including free the data String name; int id; - map_foreach(&augroup_map, name, id, { + map_foreach(&map_augroup_name_to_id, name, id, { + (void)id; + api_free_string(name); + }) + map_destroy(String, int)(&map_augroup_name_to_id); + + map_foreach(&map_augroup_id_to_name, id, name, { (void)id; api_free_string(name); }) - map_destroy(String, int)(&augroup_map); + map_destroy(int, String)(&map_augroup_id_to_name); } #endif diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9e82b4e80b..2bd14e2103 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -36,6 +36,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/digraph.h" +#include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -1736,7 +1737,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl buf = xcalloc(1, sizeof(buf_T)); // init b: variables buf->b_vars = tv_dict_alloc(); - buf->b_signcols_valid = false; + buf->b_signcols.valid = false; init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -5467,15 +5468,23 @@ static int buf_signcols_inner(buf_T *buf, int maximum) int linesum = 0; linenr_T curline = 0; + buf->b_signcols.sentinel = 0; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->se_lnum > curline) { + // Counted all signs, now add extmark signs + if (curline > 0) { + linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, + maximum-linesum); + } + curline = sign->se_lnum; if (linesum > signcols) { signcols = linesum; + buf->b_signcols.sentinel = curline; if (signcols >= maximum) { return maximum; } } - curline = sign->se_lnum; linesum = 0; } if (sign->se_has_text_or_icon) { @@ -5483,8 +5492,22 @@ static int buf_signcols_inner(buf_T *buf, int maximum) } } + if (curline > 0) { + linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum); + } + if (linesum > signcols) { + signcols = linesum; + if (signcols >= maximum) { + return maximum; + } + } + + // Check extmarks between signs + linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count-1, maximum); + if (linesum > signcols) { signcols = linesum; + buf->b_signcols.sentinel = curline; if (signcols >= maximum) { return maximum; } @@ -5493,28 +5516,96 @@ static int buf_signcols_inner(buf_T *buf, int maximum) return signcols; } +/// Invalidate the signcolumn if needed after deleting +/// signs between line1 and line2 (inclusive). +/// +/// @param buf buffer to check +/// @param line1 start of region being deleted +/// @param line2 end of region being deleted +void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2) +{ + if (!buf->b_signcols.valid) { + return; + } + + if (!buf->b_signcols.sentinel) { + buf->b_signcols.valid = false; + return; + } + + linenr_T sent = buf->b_signcols.sentinel; + + if (sent >= line1 && sent <= line2) { + // Only invalidate when removing signs at the sentinel line. + buf->b_signcols.valid = false; + } +} + +/// Re-calculate the signcolumn after adding a sign. +/// +/// @param buf buffer to check +/// @param added sign being added +void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) +{ + if (!buf->b_signcols.valid) { + return; + } + + if (!added || !buf->b_signcols.sentinel) { + buf->b_signcols.valid = false; + return; + } + + if (added->se_lnum == buf->b_signcols.sentinel) { + if (buf->b_signcols.size == buf->b_signcols.max) { + buf->b_signcols.max++; + } + buf->b_signcols.size++; + return; + } + + sign_entry_T *s; + + // Get first sign for added lnum + for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {} + + // Count signs for lnum + int linesum = 1; + for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) { + linesum++; + } + linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum-1, (int)s->se_lnum-1, + SIGN_SHOW_MAX-linesum); + + if (linesum > buf->b_signcols.size) { + buf->b_signcols.size = linesum; + buf->b_signcols.max = linesum; + buf->b_signcols.sentinel = added->se_lnum; + } +} + int buf_signcols(buf_T *buf, int maximum) { // The maximum can be determined from 'signcolumn' which is window scoped so // need to invalidate signcols if the maximum is greater than the previous // maximum. - if (maximum > buf->b_signcols_max) { - buf->b_signcols_valid = false; + if (maximum > buf->b_signcols.max) { + buf->b_signcols.valid = false; } - if (!buf->b_signcols_valid) { + if (!buf->b_signcols.valid) { int signcols = buf_signcols_inner(buf, maximum); // Check if we need to redraw - if (signcols != buf->b_signcols) { - buf->b_signcols = signcols; - buf->b_signcols_max = maximum; + if (signcols != buf->b_signcols.size) { + buf->b_signcols.size = signcols; + buf->b_signcols.max = maximum; redraw_buf_later(buf, NOT_VALID); } - buf->b_signcols_valid = true; + buf->b_signcols.valid = true; } - return buf->b_signcols; + return buf->b_signcols.size; } // Get "buf->b_fname", use "[No Name]" if it is NULL. diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 1e0c837056..7acb646980 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -862,9 +862,12 @@ struct file_buffer { // may use a different synblock_T. sign_entry_T *b_signlist; // list of placed signs - int b_signcols; // last calculated number of sign columns - bool b_signcols_valid; // calculated sign columns is valid - int b_signcols_max; // Maximum value b_signcols is valid for. + struct { + int size; // last calculated number of sign columns + bool valid; // calculated sign columns is valid + linenr_T sentinel; // a line number which is holding up the signcolumn + int max; // Maximum value size is valid for. + } b_signcols; Terminal *terminal; // Terminal instance associated with the buffer @@ -875,6 +878,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces size_t b_virt_line_blocks; // number of virt_line blocks + size_t b_signs; // number of sign extmarks // array of channel_id:s which have asked to receive updates for this // buffer. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f4882e57e1..97aac67627 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1500,7 +1500,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons } else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN)) && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { - pre = ptr[1]; + pre = (char_u)ptr[1]; // Detect hexadecimal: 0x or 0X followed by hex digit. if ((what & STR2NR_HEX) && !STRING_ENDED(ptr + 2) diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index 75ebf0084e..b48a3155b6 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -201,7 +201,7 @@ void do_debug(char_u *cmd) if (last_cmd != 0) { // Check that the tail matches. p++; - while (*p != NUL && *p == *tail) { + while (*p != NUL && *p == (char_u)(*tail)) { p++; tail++; } diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 935b233752..619e8fdadc 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -1,10 +1,12 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include "nvim/buffer.h" #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" #include "nvim/lua/executor.h" +#include "nvim/move.h" #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/vim.h" @@ -65,8 +67,10 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { - if ((!decor || decor->hl_id) && row2 >= row1) { - redraw_buf_range_later(buf, row1+1, row2+1); + if (row2 >= row1) { + if (!decor || decor->hl_id || decor_has_sign(decor)) { + redraw_buf_range_later(buf, row1+1, row2+1); + } } if (decor && kv_size(decor->virt_text)) { @@ -82,9 +86,18 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { decor_redraw(buf, row, row2, decor); - if (decor && kv_size(decor->virt_lines)) { - assert(buf->b_virt_line_blocks > 0); - buf->b_virt_line_blocks--; + if (decor) { + if (kv_size(decor->virt_lines)) { + assert(buf->b_virt_line_blocks > 0); + buf->b_virt_line_blocks--; + } + if (decor_has_sign(decor)) { + assert(buf->b_signs > 0); + buf->b_signs--; + } + if (row2 >= row && decor->sign_text) { + buf_signcols_del_check(buf, row+1, row2+1); + } } decor_free(decor); } @@ -97,6 +110,7 @@ void decor_free(Decoration *decor) clear_virttext(&kv_A(decor->virt_lines, i).line); } kv_destroy(decor->virt_lines); + xfree(decor->sign_text); xfree(decor); } } @@ -136,6 +150,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) { state->row = -1; state->buf = buf; + state->has_sign_decor = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); if (item.virt_text_owned) { @@ -180,9 +195,23 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) Decoration decor = get_decor(mark); + // Exclude non-paired marks unless they contain virt_text or a sign + if (!mt_paired(mark) + && !kv_size(decor.virt_text) + && !decor_has_sign(&decor)) { + goto next_mark; + } + + // Don't add signs for end marks as the start mark has already been added. + if (mt_end(mark) && decor_has_sign(&decor)) { + goto next_mark; + } + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if ((!mt_end(mark) && altpos.row < top_row + // Exclude start marks if the end mark position is above the top row + // Exclude end marks if we have already added the start mark + if ((mt_start(mark) && altpos.row < top_row && !kv_size(decor.virt_text)) || (mt_end(mark) && altpos.row >= top_row)) { goto next_mark; @@ -241,6 +270,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r kv_A(state->active, index) = kv_A(state->active, index-1); } kv_A(state->active, index) = range; + + if (decor_has_sign(decor)) { + state->has_sign_decor = true; + } } int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state) @@ -294,7 +327,8 @@ next_mark: bool active = false, keep = true; if (item.end_row < state->row || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) { + if (!(item.start_row >= state->row && kv_size(item.decor.virt_text)) + && !decor_has_sign(&item.decor)) { keep = false; } } else { @@ -329,6 +363,131 @@ next_mark: return attr; } +void decor_redraw_signs(buf_T *buf, DecorState *state, int row, + int *num_signs, sign_attrs_T sattrs[]) +{ + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange item = kv_A(state->active, i); + Decoration *decor = &item.decor; + + if (!decor_has_sign(decor)) { + continue; + } + + if (state->row != item.start_row) { + continue; + } + + int j; + for (j = (*num_signs); j > 0; j--) { + if (sattrs[j].sat_prio <= decor->priority) { + break; + } + sattrs[j] = sattrs[j-1]; + } + if (j < SIGN_SHOW_MAX) { + memset(&sattrs[j], 0, sizeof(sign_attrs_T)); + sattrs[j].sat_text = decor->sign_text; + if (decor->sign_hl_id != 0) { + sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id); + } + if (decor->number_hl_id != 0) { + sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id); + } + if (decor->line_hl_id != 0) { + sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id); + } + if (decor->cursorline_hl_id != 0) { + sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id); + } + sattrs[j].sat_prio = decor->priority; + (*num_signs)++; + } + } +} + +// Get the maximum required amount of sign columns needed between row and +// end_row. +int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max) +{ + int count = 0; // count for the number of signs on a given row + int count_remove = 0; // how much to decrement count by when iterating marks for a new row + int signcols = 0; // highest value of count + int currow = -1; // current row + + if (max <= 1 && buf->b_signs >= (size_t)max) { + return max; + } + + if (buf->b_signs == 0) { + return 0; + } + + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, 0, -1, itr); + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > end_row) { + break; + } + + if ((mark.pos.row < row && mt_end(mark)) + || marktree_decor_level(mark) < kDecorLevelVisible + || !mark.decor_full) { + goto next_mark; + } + + Decoration decor = get_decor(mark); + + if (!decor.sign_text) { + goto next_mark; + } + + if (mark.pos.row > currow) { + count -= count_remove; + count_remove = 0; + currow = mark.pos.row; + } + + if (!mt_paired(mark)) { + if (mark.pos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + count_remove++; + } + goto next_mark; + } + + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + + if (mt_end(mark)) { + if (mark.pos.row >= row && altpos.row <= end_row) { + count_remove++; + } + } else { + if (altpos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + } + } + +next_mark: + marktree_itr_next(buf->b_marktree, itr); + } + + return signcols; +} + void decor_redraw_end(DecorState *state) { state->buf = NULL; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 02472d09e4..7e6dbdf1a7 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -47,13 +47,18 @@ struct Decoration { bool virt_text_hide; bool hl_eol; bool virt_lines_above; - // TODO(bfredl): style, signs, etc + // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col int virt_text_width; // width of virt_text + char_u *sign_text; + int sign_hl_id; + int number_hl_id; + int line_hl_id; + int cursorline_hl_id; }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, DECOR_PRIORITY_BASE, 0, 0 } + false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 } typedef struct { int start_row; @@ -75,6 +80,7 @@ typedef struct { int col_until; int current; int eol_col; + bool has_sign_decor; } DecorState; typedef struct { @@ -98,6 +104,15 @@ EXTERN bool provider_active INIT(= false); LUA_NOREF, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, -1 } +static inline bool decor_has_sign(Decoration *decor) +{ + return decor->sign_text + || decor->sign_hl_id + || decor->number_hl_id + || decor->line_hl_id + || decor->cursorline_hl_id; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "decoration.h.generated.h" #endif diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 00ffa7cba1..095e082f61 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -7776,7 +7776,7 @@ int hkmap(int c) case ';': c = 't'; break; default: { - static char str[] = "zqbcxlsjphmkwonu ydafe rig"; + static char_u str[] = "zqbcxlsjphmkwonu ydafe rig"; if (c < 'a' || c > 'z') { return c; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d95b9560c2..8cdb03c341 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4858,7 +4858,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval long numval; char_u *stringval; int opt_type; - int c; bool working = (**arg == '+'); // has("+option") int ret = OK; int opt_flags; @@ -4877,7 +4876,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return OK; } - c = *option_end; + char c = *option_end; *option_end = NUL; opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); @@ -7790,8 +7789,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - ILOG(" We tryin to call dat dang lua ref "); - nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL); + nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); return false; break; @@ -9442,7 +9440,7 @@ void new_script_vars(scid_T id) hashtab_T *ht; scriptvar_T *sv; - ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)); + ga_grow(&ga_scripts, id - ga_scripts.ga_len); { /* Re-allocating ga_data means that an ht_array pointing to * ht_smallarray becomes invalid. We can recognize this: ht_mask is diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 49dde537c3..f47c9c482b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8296,7 +8296,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) // Repeat until {skip} returns false. for (;;) { subpatnum - = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); + = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); // finding the first match again means there is no match where {skip} // evaluates to zero. if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { @@ -9339,7 +9339,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) { static char *e_invact = N_("E927: Invalid action: '%s'"); const char *title = NULL; - int action = ' '; + char action = ' '; static int recursive = 0; rettv->vval.v_number = -1; dict_T *what = NULL; @@ -9569,7 +9569,6 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c */ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int regname; bool append = false; MotionType yank_type; long block_len; @@ -9583,13 +9582,13 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strregname == NULL) { return; // Type error; errmsg already given. } - regname = (uint8_t)(*strregname); + char regname = (uint8_t)(*strregname); if (regname == 0 || regname == '@') { regname = '"'; } const typval_T *regcontents = NULL; - int pointreg = 0; + char pointreg = 0; if (argvars[1].v_type == VAR_DICT) { dict_T *const d = argvars[1].vval.v_dict; @@ -9759,7 +9758,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) static char *e_invact2 = N_("E962: Invalid action: '%s'"); win_T *wp; dict_T *d; - int action = 'r'; + char action = 'r'; rettv->vval.v_number = -1; @@ -11409,14 +11408,22 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = highlight_has_attr(id, HL_STANDOUT, modec); } break; - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + case 'u': { + const size_t len = STRLEN(what); + if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline p = highlight_has_attr(id, HL_UNDERLINE, modec); - } else { // undercurl + } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl p = highlight_has_attr(id, HL_UNDERCURL, modec); + } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline + p = highlight_has_attr(id, HL_UNDERLINELINE, modec); + } else if (len > 6 && TOLOWER_ASC(what[6]) == 'o') { // underdot + p = highlight_has_attr(id, HL_UNDERDOT, modec); + } else { // underdash + p = highlight_has_attr(id, HL_UNDERDASH, modec); } break; } + } rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 5764f9fbd4..471c4092fe 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -516,7 +516,7 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf if (llen > 0) { fname_buf[0] = K_SPECIAL; fname_buf[1] = KS_EXTRA; - fname_buf[2] = (int)KE_SNR; + fname_buf[2] = KE_SNR; int i = 3; if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:" if (current_sctx.sc_sid <= 0) { @@ -1713,7 +1713,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, // Check for hard coded <SNR>: already translated function ID (from a user // command). if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA - && (*pp)[2] == (int)KE_SNR) { + && (*pp)[2] == KE_SNR) { *pp += 3; len = get_id_len((const char **)pp) + 3; return (char_u *)xmemdupz(start, len); @@ -1821,7 +1821,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, // Change "<SNR>" to the byte sequence. name[0] = K_SPECIAL; name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; + name[2] = KE_SNR; memmove(name + 3, name + 5, strlen((char *)name + 5) + 1); } goto theend; @@ -1888,7 +1888,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, if (!skip && lead > 0) { name[0] = K_SPECIAL; name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; + name[2] = KE_SNR; if (sid_buf_len > 0) { // If it's "<SID>" memcpy(name + 3, sid_buf, sid_buf_len); } diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 8666c7e33a..5c040adc1c 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1698,7 +1698,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, return false; } if (ga->ga_len > init_growsize) { - ga_set_growsize(ga, MAX(ga->ga_len, 8000)); + ga_set_growsize(ga, MIN(ga->ga_len, 8000)); } ga_concat_len(ga, (const char *)line + 1, len - 1); return true; @@ -1796,7 +1796,7 @@ scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out) if (sid_out != NULL) { *sid_out = sid; } - ga_grow(&script_items, (int)(sid - script_items.ga_len)); + ga_grow(&script_items, sid - script_items.ga_len); while (script_items.ga_len < sid) { script_items.ga_len++; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; @@ -1852,7 +1852,7 @@ static void cmd_source_buffer(const exarg_T *const eap) for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) { // Adjust growsize to current length to speed up concatenating many lines. if (ga.ga_len > 400) { - ga_set_growsize(&ga, MAX(ga.ga_len, 8000)); + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); } ga_concat(&ga, (char *)ml_get(curr_lnum)); ga_append(&ga, NL); @@ -2188,7 +2188,6 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) } - /// ":scriptnames" void ex_scriptnames(exarg_T *eap) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d3203bcee8..a140d76858 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -80,7 +80,7 @@ static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s"); static char *e_no_such_user_defined_command_in_current_buffer_str - = N_("E1237: No such user-defined command in current buffer: %s"); + = N_("E1237: No such user-defined command in current buffer: %s"); static int quitmore = 0; static bool ex_pressedreturn = false; @@ -2835,7 +2835,7 @@ int modifier_len(char_u *cmd) for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) { int j; for (j = 0; p[j] != NUL; j++) { - if (p[j] != cmdmods[i].name[j]) { + if (p[j] != (char_u)cmdmods[i].name[j]) { break; } } @@ -8160,7 +8160,7 @@ static void ex_operators(exarg_T *eap) case CMD_yank: oa.op_type = OP_YANK; - (void)op_yank(&oa, true, false); + (void)op_yank(&oa, true); break; default: // CMD_rshift or CMD_lshift diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 851828afcf..25b6aa7d8a 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -430,7 +430,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int STRCAT(val, p); p[-2] = NUL; - sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]); + snprintf(val + STRLEN(p), strlen(" (%s)"), " (%s)", &mesg[1]); p[-2] = '"'; } break; @@ -1602,7 +1602,7 @@ void ex_endtry(exarg_T *eap) { int idx; bool rethrow = false; - int pending = CSTP_NONE; + char pending = CSTP_NONE; void *rettv = NULL; cstack_T *const cstack = eap->cstack; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 48e57e20e1..2916e3e978 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -67,7 +67,9 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col uint8_t decor_level = kDecorLevelNone; // no decor if (decor) { - if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) { + if (kv_size(decor->virt_text) + || kv_size(decor->virt_lines) + || decor_has_sign(decor)) { decor_full = true; decor = xmemdup(decor, sizeof *decor); } @@ -142,6 +144,13 @@ revised: if (kv_size(decor->virt_lines)) { buf->b_virt_line_blocks++; } + if (decor_has_sign(decor)) { + buf->b_signs++; + } + if (decor->sign_text) { + // TODO(lewis6991): smarter invalidation + buf_signcols_add_check(buf, NULL); + } decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 965aa8749d..c5a89d3371 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3911,13 +3911,13 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST { - return (long)file_info->stat.st_mtim.tv_nsec != mtime_ns + return file_info->stat.st_mtim.tv_nsec != mtime_ns #if defined(__linux__) || defined(MSWIN) // On a FAT filesystem, esp. under Linux, there are only 5 bits to store // the seconds. Since the roundoff is done when flushing the inode, the // time may change unexpectedly by one second!!! - || (long)file_info->stat.st_mtim.tv_sec - mtime > 1 - || mtime - (long)file_info->stat.st_mtim.tv_sec > 1; + || file_info->stat.st_mtim.tv_sec - mtime > 1 + || mtime - file_info->stat.st_mtim.tv_sec > 1; #else || (long)file_info->stat.st_mtim.tv_sec != mtime; #endif diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 8426cdb98c..299456f688 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1907,9 +1907,6 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // complete match if (keylen >= 0 && keylen <= typebuf.tb_len) { char_u *map_str = NULL; - 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 @@ -1946,9 +1943,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // 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 save_m_expr = mp->m_expr; + int save_m_noremap = mp->m_noremap; + char 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 LuaRef save_m_luaref = mp->m_luaref; @@ -2661,9 +2658,9 @@ int fix_input_buffer(char_u *buf, int len) /// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. -void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, - const char_u *orig_rhs, const size_t orig_rhs_len, - LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs) +void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs, + const size_t orig_rhs_len, LuaRef rhs_lua, int cpo_flags, + MapArguments *mapargs) { char_u *lhs_buf = NULL; char_u *rhs_buf = NULL; @@ -3988,7 +3985,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) /// special characters. /// /// @param c NUL or typed character for abbreviation -static char_u *eval_map_expr(mapblock_T *mp, int c) +static char_u *eval_map_expr(mapblock_T *mp, int c) { char_u *res; char_u *p = NULL; @@ -4143,7 +4140,7 @@ int makemap(FILE *fd, buf_T *buf) } for (p = mp->m_str; *p != NUL; p++) { if (p[0] == K_SPECIAL && p[1] == KS_EXTRA - && p[2] == (int)KE_SNR) { + && p[2] == KE_SNR) { break; } } diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index eb10c65be9..93c8c53e33 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -420,7 +420,10 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL); + pattr->underlineline = (highlight_has_attr(hl_id, HL_UNDERLINELINE, modec) != NULL); pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); + pattr->underdot = (highlight_has_attr(hl_id, HL_UNDERDOT, modec) != NULL); + pattr->underdash = (highlight_has_attr(hl_id, HL_UNDERDASH, modec) != NULL); uint32_t fg_color = prt_get_color(hl_id, modec); diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h index eba769d342..16119c5d2d 100644 --- a/src/nvim/hardcopy.h +++ b/src/nvim/hardcopy.h @@ -18,6 +18,9 @@ typedef struct { TriState italic; TriState underline; int undercurl; + int underlineline; + int underdot; + int underdash; } prt_text_attr_T; /* diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index e43a56086f..a1fd0d0d66 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -327,7 +327,7 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal); } - for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) { + for (int hlf = 0; hlf < HLF_COUNT; hlf++) { int attr; if (wp->w_hl_ids[hlf] != 0) { attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false); @@ -558,7 +558,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) cattrs = battrs; cattrs.rgb_fg_color = rgb_blend(ratio, battrs.rgb_fg_color, fattrs.rgb_bg_color); - if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) { + if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) { cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color, fattrs.rgb_bg_color); } else { @@ -576,7 +576,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) } cattrs.rgb_fg_color = rgb_blend(ratio/2, battrs.rgb_fg_color, fattrs.rgb_fg_color); - if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) { + if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) { cattrs.rgb_sp_color = rgb_blend(ratio/2, battrs.rgb_bg_color, fattrs.rgb_sp_color); } else { @@ -743,10 +743,23 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) PUT(hl, "underline", BOOLEAN_OBJ(true)); } + if (mask & HL_UNDERLINELINE) { + PUT(hl, "underlineline", BOOLEAN_OBJ(true)); + } + if (mask & HL_UNDERCURL) { PUT(hl, "undercurl", BOOLEAN_OBJ(true)); } + if (mask & HL_UNDERDOT) { + PUT(hl, "underdot", BOOLEAN_OBJ(true)); + } + + if (mask & HL_UNDERDASH) { + PUT(hl, "underdash", BOOLEAN_OBJ(true)); + } + + if (mask & HL_ITALIC) { PUT(hl, "italic", BOOLEAN_OBJ(true)); } @@ -813,7 +826,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, bold, , HL_BOLD); CHECK_FLAG(dict, mask, standout, , HL_STANDOUT); CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE); + CHECK_FLAG(dict, mask, underlineline, , HL_UNDERLINELINE); CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL); + CHECK_FLAG(dict, mask, underdot, , HL_UNDERDOT); + CHECK_FLAG(dict, mask, underdash, , HL_UNDERDASH); CHECK_FLAG(dict, mask, italic, , HL_ITALIC); CHECK_FLAG(dict, mask, reverse, , HL_INVERSE); CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 50a03e0c02..dcfb9358bb 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -24,6 +24,10 @@ typedef enum { HL_FG_INDEXED = 0x0200, HL_DEFAULT = 0x0400, HL_GLOBAL = 0x0800, + HL_UNDERLINELINE = 0x1000, + HL_UNDERDOT = 0x2000, + HL_UNDERDASH = 0x4000, + HL_ANY_UNDERLINE = HL_UNDERLINE | HL_UNDERLINELINE | HL_UNDERCURL | HL_UNDERDOT | HL_UNDERDASH, } HlAttrFlags; /// Stores a complete highlighting entry, including colors and attributes diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e233e0e6d0..6aaff100ca 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -525,24 +525,6 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_pop(lstate, 3); } -static void nlua_preload_modules(lua_State *lstate) -{ - lua_getglobal(lstate, "package"); // [package] - lua_getfield(lstate, -1, "preload"); // [package, preload] - for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { - ModuleDef def = builtin_modules[i]; - lua_pushinteger(lstate, (long)i); // [package, preload, i] - lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] - lua_setfield(lstate, -2, def.name); // [package, preload] - - if (nlua_disable_preload && strequal(def.name, "vim")) { - break; - } - } - - lua_pop(lstate, 2); // [] -} - static int nlua_module_preloader(lua_State *lstate) { size_t i = (size_t)lua_tointeger(lstate, lua_upvalueindex(1)); @@ -561,24 +543,30 @@ static int nlua_module_preloader(lua_State *lstate) return 1; } -static bool nlua_common_package_init(lua_State *lstate) +static bool nlua_init_packages(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - nlua_preload_modules(lstate); + // put builtin packages in preload + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "preload"); // [package, preload] + for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { + ModuleDef def = builtin_modules[i]; + lua_pushinteger(lstate, (long)i); // [package, preload, i] + lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] + lua_setfield(lstate, -2, def.name); // [package, preload] - lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim._load_package"); - if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s\n")); - return false; + if (nlua_disable_preload && strequal(def.name, "vim.inspect")) { + break; + } } - // TODO(bfredl): ideally all initialization should be done as a single require - // call. + lua_pop(lstate, 2); // [] + lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim.shared"); + lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); + mch_errmsg(lua_tostring(lstate, -1)); + mch_errmsg("\n"); return false; } @@ -654,14 +642,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); - if (!nlua_common_package_init(lstate)) { - return false; - } - - lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim"); - if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); + if (!nlua_init_packages(lstate)) { return false; } @@ -732,7 +713,7 @@ static lua_State *nlua_thread_acquire_vm(void) lua_setglobal(lstate, "vim"); - nlua_common_package_init(lstate); + nlua_init_packages(lstate); lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); diff --git a/src/nvim/main.c b/src/nvim/main.c index ae25ff63dd..7281809c06 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -823,7 +823,6 @@ static void command_line_scan(mparm_T *parmp) bool had_stdin_file = false; // found explicit "-" argument bool had_minmin = false; // found "--" argument int want_argument; // option argument with argument - int c; long n; argc--; @@ -845,7 +844,7 @@ static void command_line_scan(mparm_T *parmp) // Optional argument. } else if (argv[0][0] == '-' && !had_minmin) { want_argument = false; - c = argv[0][argv_idx++]; + char c = argv[0][argv_idx++]; switch (c) { case NUL: // "nvim -" read from stdin if (exmode_active) { diff --git a/src/nvim/map.c b/src/nvim/map.c index 091d653046..4e39eb8c07 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -178,6 +178,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) +MAP_IMPL(int, String, DEFAULT_INITIALIZER) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c9c89bf2fd..00f72386a7 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -47,6 +47,7 @@ MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) +MAP_DECLS(int, String) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 918db8b76c..d2354dbf6b 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -558,8 +558,8 @@ void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_ { // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms // once we upgrade to a non-broken version of gcc in functionaltest-lua CI - rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); - rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags + rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); + rawkey(itr).flags = (uint16_t)(rawkey(itr).flags | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET) | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)); rawkey(itr).decor_full = key.decor_full; diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 30f5aacebc..440816930b 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -87,6 +87,11 @@ static inline bool mt_end(mtkey_t key) return key.flags & MT_FLAG_END; } +static inline bool mt_start(mtkey_t key) +{ + return mt_paired(key) && !mt_end(key); +} + static inline bool mt_right(mtkey_t key) { return key.flags & MT_FLAG_RIGHT_GRAVITY; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 004ef36b36..59f57aa667 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -303,7 +303,7 @@ int ml_open(buf_T *buf) b0p->b0_id[0] = BLOCK0_ID0; b0p->b0_id[1] = BLOCK0_ID1; - b0p->b0_magic_long = (long)B0_MAGIC_LONG; + b0p->b0_magic_long = B0_MAGIC_LONG; b0p->b0_magic_int = (int)B0_MAGIC_INT; b0p->b0_magic_short = (short)B0_MAGIC_SHORT; b0p->b0_magic_char = B0_MAGIC_CHAR; @@ -3709,7 +3709,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ static int b0_magic_wrong(ZERO_BL *b0p) { - return b0p->b0_magic_long != (long)B0_MAGIC_LONG + return b0p->b0_magic_long != B0_MAGIC_LONG || b0p->b0_magic_int != (int)B0_MAGIC_INT || b0p->b0_magic_short != (short)B0_MAGIC_SHORT || b0p->b0_magic_char != B0_MAGIC_CHAR; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b5c7020dee..23db7fe5a3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1023,6 +1023,60 @@ static int stuff_yank(int regname, char_u *p) static int execreg_lastc = NUL; +/// When executing a register as a series of ex-commands, if the +/// line-continuation character is used for a line, then join it with one or +/// more previous lines. Note that lines are processed backwards starting from +/// the last line in the register. +/// +/// @param lines list of lines in the register +/// @param idx index of the line starting with \ or "\. Join this line with all the immediate +/// predecessor lines that start with a \ and the first line that doesn't start +/// with a \. Lines that start with a comment "\ character are ignored. +/// @returns the concatenated line. The index of the line that should be +/// processed next is returned in idx. +static char_u *execreg_line_continuation(char_u **lines, size_t *idx) +{ + size_t i = *idx; + assert(i > 0); + const size_t cmd_end = i; + + garray_T ga; + ga_init(&ga, (int)sizeof(char_u), 400); + + char_u *p; + + // search backwards to find the first line of this command. + // Any line not starting with \ or "\ is the start of the + // command. + while (--i > 0) { + p = skipwhite(lines[i]); + if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) { + break; + } + } + const size_t cmd_start = i; + + // join all the lines + ga_concat(&ga, (char *)lines[cmd_start]); + for (size_t j = cmd_start + 1; j <= cmd_end; j++) { + p = skipwhite(lines[j]); + if (*p == '\\') { + // Adjust the growsize to the current length to + // speed up concatenating many lines. + if (ga.ga_len > 400) { + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); + } + ga_concat(&ga, (char *)(p + 1)); + } + } + ga_append(&ga, NUL); + char_u *str = vim_strsave(ga.ga_data); + ga_clear(&ga); + + *idx = i; + return str; +} + /// Execute a yank register: copy it into the stuff buffer /// /// @param colon insert ':' before each line @@ -1111,7 +1165,21 @@ int do_execreg(int regname, int colon, int addcr, int silent) return FAIL; } } - escaped = vim_strsave_escape_ks(reg->y_array[i]); + + // Handle line-continuation for :@<register> + char_u *str = reg->y_array[i]; + bool free_str = false; + if (colon && i > 0) { + p = skipwhite(str); + if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) { + str = execreg_line_continuation(reg->y_array, &i); + free_str = true; + } + } + escaped = vim_strsave_escape_ks(str); + if (free_str) { + xfree(str); + } retval = ins_typebuf(escaped, remap, 0, true, silent); xfree(escaped); if (retval == FAIL) { @@ -1504,20 +1572,20 @@ int op_delete(oparg_T *oap) yankreg_T *reg = NULL; int did_yank = false; if (oap->regname != 0) { - // yank without message - did_yank = op_yank(oap, false, true); - if (!did_yank) { - // op_yank failed, don't do anything + // check for read-only register + if (!valid_yank_reg(oap->regname, true)) { + beep_flush(); return OK; } + reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg + op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message + did_yank = true; } - /* - * Put deleted text into register 1 and shift number registers if the - * delete contains a line break, or when a regname has been specified. - */ - if (oap->regname != 0 || oap->motion_type == kMTLineWise - || oap->line_count > 1 || oap->use_reg_one) { + // Put deleted text into register 1 and shift number registers if the + // delete contains a line break, or when using a specific operator (Vi + // compatible) + if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { shift_delete_registers(is_append_register(oap->regname)); reg = &y_regs[1]; op_yank_reg(oap, false, reg, false); @@ -2579,12 +2647,12 @@ void free_register(yankreg_T *reg) /// Yanks the text between "oap->start" and "oap->end" into a yank register. /// If we are to append (uppercase register), we first yank into a new yank /// register and then concatenate the old and the new one. +/// Do not call this from a delete operation. Use op_yank_reg() instead. /// /// @param oap operator arguments /// @param message show message when more than `&report` lines are yanked. -/// @param deleting whether the function was called from a delete operation. /// @returns whether the operation register was writable. -bool op_yank(oparg_T *oap, bool message, int deleting) +bool op_yank(oparg_T *oap, bool message) FUNC_ATTR_NONNULL_ALL { // check for read-only register @@ -2598,11 +2666,8 @@ bool op_yank(oparg_T *oap, bool message, int deleting) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); - // op_delete will set_clipboard and do_autocmd - if (!deleting) { - set_clipboard(oap->regname, reg); - do_autocmd_textyankpost(oap, reg); - } + set_clipboard(oap->regname, reg); + do_autocmd_textyankpost(oap, reg); return true; } @@ -6630,7 +6695,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { curwin->w_p_lbr = lbr_saved; oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); + (void)op_yank(oap, !gui_yank); } check_cursor_col(); break; diff --git a/src/nvim/option.c b/src/nvim/option.c index 9068c90dd1..a0706dfa14 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3851,7 +3851,7 @@ static bool parse_winhl_opt(win_T *wp) if (strncmp("Normal", p, nlen) == 0) { w_hl_id_normal = hl_id; } else { - for (hlf = 0; hlf < (int)HLF_COUNT; hlf++) { + for (hlf = 0; hlf < HLF_COUNT; hlf++) { if (strlen(hlf_names[hlf]) == nlen && strncmp(hlf_names[hlf], p, nlen) == 0) { w_hl_ids[hlf] = hl_id; @@ -5267,7 +5267,7 @@ static void showoptions(int all, int opt_flags) && Columns + GAP >= INT_MIN + 3 && (Columns + GAP - 3) / INC >= INT_MIN && (Columns + GAP - 3) / INC <= INT_MAX); - cols = (int)((Columns + GAP - 3) / INC); + cols = (Columns + GAP - 3) / INC; if (cols == 0) { cols = 1; } @@ -5666,11 +5666,11 @@ void comp_col(void) assert(sc_col >= 0 && INT_MIN + sc_col <= Columns && Columns - sc_col <= INT_MAX); - sc_col = (int)(Columns - sc_col); + sc_col = Columns - sc_col; assert(ru_col >= 0 && INT_MIN + ru_col <= Columns && Columns - ru_col <= INT_MAX); - ru_col = (int)(Columns - ru_col); + ru_col = Columns - ru_col; if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6a6c915094..b7f11b2de0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -468,6 +468,8 @@ static int toggle_Magic(int x) #define EMSG_RET_FAIL(m) return (emsg(m), rc_did_emsg = true, FAIL) #define EMSG2_RET_NULL(m, c) \ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL) +#define EMSG3_RET_NULL(m, c, a) \ + return (semsg((const char *)(m), (c) ? "" : "\\", (a)), rc_did_emsg = true, (void *)NULL) #define EMSG2_RET_FAIL(m, c) \ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL) #define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \ @@ -1762,13 +1764,9 @@ static char_u *regpiece(int *flagp) if (re_multi_type(peekchr()) != NOT_MULTI) { // 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())); + EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON); } - EMSG_RET_NULL((char *)IObuff); + EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr())); } return ret; @@ -1927,10 +1925,8 @@ static char_u *regatom(int *flagp) case Magic('{'): case Magic('*'): c = no_Magic(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); + EMSG3_RET_NULL(_("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c); // NOTREACHED case Magic('~'): /* previous substitute pattern */ @@ -2537,7 +2533,9 @@ do_multibyte: static bool re_mult_next(char *what) { if (re_multi_type(peekchr()) == MULTI_MULT) { - EMSG2_RET_FAIL(_("E888: (NFA regexp) cannot repeat %s"), what); + semsg(_("E888: (NFA regexp) cannot repeat %s"), what); + rc_did_emsg = true; + return false; } return true; } @@ -3149,9 +3147,7 @@ static int read_limits(long *minval, long *maxval) regparse++; // Allow either \{...} or \{...\} } if (*regparse != '}') { - snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"), - reg_magic == MAGIC_ALL ? "" : "\\"); - EMSG_RET_FAIL((char *)IObuff); + EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL); } /* @@ -5043,8 +5039,7 @@ static bool regmatch( } else { MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr); if (limit > 0 - && (long)(behind_pos.rs_u.ptr - - rp->rs_un.regsave.rs_u.ptr) > limit) { + && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) { no = FAIL; } } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7fafe3dd6e..1cdee7c972 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2208,6 +2208,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc buf_T *buf = wp->w_buffer; bool end_fill = (lnum == buf->b_ml.ml_line_count+1); + has_decor = decor_redraw_line(buf, lnum-1, &decor_state); + if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. @@ -2229,9 +2231,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - has_decor = decor_redraw_line(wp->w_buffer, lnum-1, - &decor_state); - for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_line != LUA_NOREF) { @@ -2460,6 +2459,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc memset(sattrs, 0, sizeof(sattrs)); num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs); + if (decor_state.has_sign_decor) { + decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs); + } // If this line has a sign with line highlighting set line_attr. // TODO(bfredl, vigoux): this should not take priority over decoration! @@ -2888,9 +2890,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } else if (wp->w_p_cul && lnum == wp->w_cursor.lnum && (wp->w_p_culopt_flags & CULOPT_NBR) - && (row == startrow - || wp->w_p_culopt_flags & CULOPT_LINE) - && filler_todo == 0) { + && (row == startrow + filler_lines + || (row > startrow + filler_lines + && wp->w_p_culopt_flags & CULOPT_LINE))) { // When 'cursorline' is set highlight the line number of // the current line differently. // When 'cursorlineopt' has "screenline" only highlight diff --git a/src/nvim/search.c b/src/nvim/search.c index 682fa417a9..e6b47e75b2 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1071,7 +1071,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, * Find out the direction of the search. */ if (dirc == 0) { - dirc = spats[0].off.dir; + dirc = (char_u)spats[0].off.dir; } else { spats[0].off.dir = dirc; set_vv_searchforward(); diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 8b41781c98..6d0ac30003 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -196,7 +196,8 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int if (next != NULL) { next->se_prev = newsign; } - buf->b_signcols_valid = false; + + buf_signcols_add_check(buf, newsign); if (prev == NULL) { // When adding first sign need to redraw the windows to create the @@ -506,6 +507,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) if (sp->sn_num_hl != 0) { sattr.sat_numhl = syn_id2attr(sp->sn_num_hl); } + // Store the priority so we can mesh in extmark signs later + sattr.sat_prio = sign->se_priority; } sattrs[nr_matches] = sattr; @@ -539,7 +542,6 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) sign_entry_T *next; // the next sign in a b_signlist linenr_T lnum; // line number whose sign was deleted - buf->b_signcols_valid = false; lastp = &buf->b_signlist; lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { @@ -552,6 +554,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) next->se_prev = sign->se_prev; } lnum = sign->se_lnum; + buf_signcols_del_check(buf, lnum, lnum); if (sign->se_group != NULL) { sign_group_unref(sign->se_group->sg_name); } @@ -673,7 +676,7 @@ void buf_delete_signs(buf_T *buf, char_u *group) lastp = &sign->se_next; } } - buf->b_signcols_valid = false; + buf_signcols_del_check(buf, 1, MAXLNUM); } /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. @@ -735,14 +738,19 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a int is_fixed = 0; int signcol = win_signcol_configured(curwin, &is_fixed); - curbuf->b_signcols_valid = false; + bool delete = amount == MAXLNUM; + + if (delete) { + buf_signcols_del_check(curbuf, line1, line2); + } + lastp = &curbuf->b_signlist; for (sign = curbuf->b_signlist; sign != NULL; sign = next) { next = sign->se_next; new_lnum = sign->se_lnum; if (sign->se_lnum >= line1 && sign->se_lnum <= line2) { - if (amount != MAXLNUM) { + if (!delete) { new_lnum += amount; } else if (!is_fixed || signcol >= 2) { *lastp = next; diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index c734502878..6d28c1849a 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -40,6 +40,7 @@ typedef struct sign_attrs_S { int sat_linehl; int sat_culhl; int sat_numhl; + int sat_prio; // Used for inserting extmark signs } sign_attrs_T; #define SIGN_SHOW_MAX 9 diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1296d410f6..fb644daa39 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3824,7 +3824,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // At end of a prefix or at start of prefixtree: check for // following word. - if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) { + if (byts[arridx] == 0 || n == STATE_NOPREFIX) { // Set su->su_badflags to the caps type at this position. // Use the caps type until here for the prefix itself. n = nofold_len(fword, sp->ts_fidx, su->su_badptr); diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 291138ef23..9d4c64e4b1 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -354,7 +354,7 @@ char *strcase_save(const char *const orig, bool upper) int l = utf_ptr2len((const char_u *)p); if (c == 0) { // overlong sequence, use only the first byte - c = *p; + c = (char_u)(*p); l = 1; } int uc = upper ? mb_toupper(c) : mb_tolower(c); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index db10b71d38..54fce3d968 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -121,11 +121,11 @@ static int include_link = 0; // when 2 include "nvim/link" and "clear" /// The "term", "cterm" and "gui" arguments can be any combination of the /// following names, separated by commas (but no spaces!). static char *(hl_name_table[]) = -{ "bold", "standout", "underline", "undercurl", - "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; +{ "bold", "standout", "underline", "underlineline", "undercurl", "underdot", + "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; static int hl_attr_table[] = -{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, - HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; +{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, + HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; static char e_illegal_arg[] = N_("E390: Illegal argument: %s"); @@ -4208,7 +4208,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha p = flagtab[fidx].name; int i; for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) { - if (arg[len] != p[i] && arg[len] != p[i + 1]) { + if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) { break; } } diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 53ed4617f7..4229095f9f 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -26,6 +26,54 @@ func Test_set_filename() call delete('samples/Xtest') endfunc +func Test_set_filename_other_window() + CheckFunction test_autochdir + let cwd = getcwd() + call test_autochdir() + call mkdir('Xa') + call mkdir('Xb') + call mkdir('Xc') + try + args Xa/aaa.txt Xb/bbb.txt + set acd + let winid = win_getid() + snext + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt') + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + finally + set noacd + call chdir(cwd) + call delete('Xa', 'rf') + call delete('Xb', 'rf') + call delete('Xc', 'rf') + bwipe! aaa.txt + bwipe! bbb.txt + bwipe! ccc.txt + endtry +endfunc + +func Test_acd_win_execute() + CheckFunction test_autochdir + let cwd = getcwd() + set acd + call test_autochdir() + + call mkdir('Xfile') + let winid = win_getid() + new Xfile/file + call assert_match('testdir.Xfile$', getcwd()) + cd .. + call assert_match('testdir$', getcwd()) + call win_execute(winid, 'echo') + call assert_match('testdir$', getcwd()) + + bwipe! + set noacd + call chdir(cwd) + call delete('Xfile', 'rf') +endfunc + func Test_verbose_pwd() CheckFunction test_autochdir let cwd = getcwd() @@ -42,20 +90,27 @@ func Test_verbose_pwd() set acd wincmd w call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) - execute 'lcd' cwd - call assert_match('\[window\].*testdir$', execute('verbose pwd')) execute 'tcd' cwd call assert_match('\[tabpage\].*testdir$', execute('verbose pwd')) execute 'cd' cwd call assert_match('\[global\].*testdir$', execute('verbose pwd')) + execute 'lcd' cwd + call assert_match('\[window\].*testdir$', execute('verbose pwd')) edit call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) + enew + wincmd w + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + wincmd w + call assert_match('\[window\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) set noacd call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) wincmd w - call assert_match('\[global\].*testdir', execute('verbose pwd')) + call assert_match('\[window\].*testdir$', execute('verbose pwd')) + execute 'cd' cwd + call assert_match('\[global\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 482d39056f..10eb979b45 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1017,6 +1017,32 @@ func Test_diff_with_cursorline() call delete('Xtest_diff_cursorline') endfunc +func Test_diff_with_cursorline_number() + CheckScreendump + + let lines =<< trim END + hi CursorLine ctermbg=red ctermfg=white + hi CursorLineNr ctermbg=white ctermfg=black cterm=underline + set cursorline number + call setline(1, ["baz", "foo", "foo", "bar"]) + 2 + vnew + call setline(1, ["foo", "foo", "bar"]) + windo diffthis + 1wincmd w + END + call writefile(lines, 'Xtest_diff_cursorline_number') + let buf = RunVimInTerminal('-S Xtest_diff_cursorline_number', {}) + + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {}) + call term_sendkeys(buf, ":set cursorlineopt=number\r") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_cursorline_number') +endfunc + func Test_diff_with_cursorline_breakindent() CheckScreendump diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 23e39eba35..f78b748d71 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -62,7 +62,6 @@ func Test_display_registers() call assert_match('^\nType Name Content\n' \ . ' c "" a\n' \ . ' c "0 ba\n' - \ . ' c "1 b\n' \ . ' c "a b\n' \ . '.*' \ . ' c "- a\n' @@ -85,6 +84,90 @@ func Test_display_registers() let g:clipboard = save_clipboard endfunc +func Test_register_one() + " delete a line goes into register one + new + call setline(1, "one") + normal dd + call assert_equal("one\n", @1) + + " delete a word does not change register one, does change "- + call setline(1, "two") + normal de + call assert_equal("one\n", @1) + call assert_equal("two", @-) + + " delete a word with a register does not change register one + call setline(1, "three") + normal "ade + call assert_equal("three", @a) + call assert_equal("one\n", @1) + + " delete a word with register DOES change register one with one of a list of + " operators + " % + call setline(1, ["(12)3"]) + normal "ad% + call assert_equal("(12)", @a) + call assert_equal("(12)", @1) + + " ( + call setline(1, ["first second"]) + normal $"ad( + call assert_equal("first secon", @a) + call assert_equal("first secon", @1) + + " ) + call setline(1, ["First Second."]) + normal gg0"ad) + call assert_equal("First Second.", @a) + call assert_equal("First Second.", @1) + + " ` + call setline(1, ["start here."]) + normal gg0fhmx0"ad`x + call assert_equal("start ", @a) + call assert_equal("start ", @1) + + " / + call setline(1, ["searchX"]) + exe "normal gg0\"ad/X\<CR>" + call assert_equal("search", @a) + call assert_equal("search", @1) + + " ? + call setline(1, ["Ysearch"]) + exe "normal gg$\"ad?Y\<CR>" + call assert_equal("Ysearc", @a) + call assert_equal("Ysearc", @1) + + " n + call setline(1, ["Ynext"]) + normal gg$"adn + call assert_equal("Ynex", @a) + call assert_equal("Ynex", @1) + + " N + call setline(1, ["prevY"]) + normal gg0"adN + call assert_equal("prev", @a) + call assert_equal("prev", @1) + + " } + call setline(1, ["one", ""]) + normal gg0"ad} + call assert_equal("one\n", @a) + call assert_equal("one\n", @1) + + " { + call setline(1, ["", "two"]) + normal 2G$"ad{ + call assert_equal("\ntw", @a) + call assert_equal("\ntw", @1) + + bwipe! +endfunc + func Test_recording_status_in_ex_line() norm qx redraw! @@ -482,6 +565,82 @@ func Test_v_register() bwipe! endfunc +" Test for executing the contents of a register as an Ex command with line +" continuation. +func Test_execute_reg_as_ex_cmd() + " Line continuation with just two lines + let code =<< trim END + let l = [ + \ 1] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1], l) + + " Line continuation with more than two lines + let code =<< trim END + let l = [ + \ 1, + \ 2, + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use comments interspersed with code + let code =<< trim END + let l = [ + "\ one + \ 1, + "\ two + \ 2, + "\ three + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use line continuation in the middle + let code =<< trim END + let a = "one" + let l = [ + \ 1, + \ 2] + let b = "two" + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2], l) + call assert_equal("one", a) + call assert_equal("two", b) + + " only one line with a \ + let @r = "\\let l = 1" + call assert_fails('@r', 'E10:') + + " only one line with a "\ + let @r = ' "\ let i = 1' + @r + call assert_false(exists('i')) + + " first line also begins with a \ + let @r = "\\let l = [\n\\ 1]" + call assert_fails('@r', 'E10:') + + " Test with a large number of lines + let @r = "let str = \n" + let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312) + let @r ..= ' \ ""' + @r + call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str) +endfunc + func Test_ve_blockpaste() new set ve=all diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 58061f020d..1ad82b7290 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -528,7 +528,7 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) return a1.cterm_fg_color != a2.cterm_fg_color || a1.cterm_bg_color != a2.cterm_bg_color || a1.cterm_ae_attr != a2.cterm_ae_attr - || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL) + || (a1.cterm_ae_attr & HL_ANY_UNDERLINE && a1.rgb_sp_color != a2.rgb_sp_color); } } @@ -552,15 +552,27 @@ static void update_attrs(UI *ui, int attr_id) bool strikethrough = attr & HL_STRIKETHROUGH; bool underline; + bool underlineline; bool undercurl; + bool underdot; + bool underdash; if (data->unibi_ext.set_underline_style != -1) { underline = attr & HL_UNDERLINE; + underlineline = attr & HL_UNDERLINELINE; undercurl = attr & HL_UNDERCURL; + underdash = attr & HL_UNDERDASH; + underdot = attr & HL_UNDERDOT; } else { - underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL); + underline = attr & HL_ANY_UNDERLINE; + underlineline = false; undercurl = false; + underdot = false; + underdash = false; } + bool has_any_underline = undercurl || underline + || underdot || underdash || underlineline; + if (unibi_get_str(data->ut, unibi_set_attributes)) { if (bold || reverse || underline || standout) { UNIBI_SET_NUM_VAR(data->params[0], standout); @@ -599,11 +611,24 @@ static void update_attrs(UI *ui, int attr_id) if (strikethrough && data->unibi_ext.enter_strikethrough_mode != -1) { unibi_out_ext(ui, data->unibi_ext.enter_strikethrough_mode); } + if (underlineline && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 2); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } if (undercurl && data->unibi_ext.set_underline_style != -1) { UNIBI_SET_NUM_VAR(data->params[0], 3); unibi_out_ext(ui, data->unibi_ext.set_underline_style); } - if ((undercurl || underline) && data->unibi_ext.set_underline_color != -1) { + if (underdot && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 4); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } + if (underdash && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 5); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } + + if (has_any_underline && data->unibi_ext.set_underline_color != -1) { int color = attrs.rgb_sp_color; if (color != -1) { UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red @@ -652,13 +677,13 @@ static void update_attrs(UI *ui, int attr_id) data->default_attr = fg == -1 && bg == -1 - && !bold && !italic && !underline && !undercurl && !reverse && !standout + && !bold && !italic && !has_any_underline && !reverse && !standout && !strikethrough; // Non-BCE terminals can't clear with non-default background color. Some BCE // terminals don't support attributes either, so don't rely on it. But assume // italic and bold has no effect if there is no text. - data->can_clear_attr = !reverse && !standout && !underline && !undercurl + data->can_clear_attr = !reverse && !standout && !has_any_underline && !strikethrough && (data->bce || bg == -1); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 31b9614c34..7c67c058b0 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -639,8 +639,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) } } else { // non-positive indicates no request - wp->w_height_request = (int)MAX(height, 0); - wp->w_width_request = (int)MAX(width, 0); + wp->w_height_request = MAX(height, 0); + wp->w_width_request = MAX(width, 0); win_set_inner_size(wp); } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 83048d911f..d5299202b0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6763,9 +6763,6 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) curwin = switchwin->sw_curwin; curbuf = curwin->w_buffer; } - // If called by win_execute() and executing the command changed the - // directory, it now has to be restored. - fix_current_dir(); } /// Make "buf" the current buffer. diff --git a/src/nvim/window.h b/src/nvim/window.h index e2fd2c515d..42701f72b4 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -5,6 +5,7 @@ #include "nvim/buffer_defs.h" #include "nvim/mark.h" +#include "nvim/os/os.h" // Values for file_name_in_line() #define FNAME_MESS 1 // give error message @@ -47,12 +48,36 @@ typedef struct { do { \ win_T *const wp_ = (wp); \ const pos_T curpos_ = wp_->w_cursor; \ + char_u cwd_[MAXPATHL]; \ + char_u autocwd_[MAXPATHL]; \ + bool apply_acd_ = false; \ + int cwd_status_ = FAIL; \ + /* Getting and setting directory can be slow on some systems, only do */ \ + /* this when the current or target window/tab have a local directory or */ \ + /* 'acd' is set. */ \ + if (curwin != wp \ + && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ + || p_acd)) { \ + cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + } \ + /* If 'acd' is set, check we are using that directory. If yes, then */ \ + /* apply 'acd' afterwards, otherwise restore the current directory. */ \ + if (cwd_status_ == OK && p_acd) { \ + do_autochdir(); \ + apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \ + } \ switchwin_T switchwin_; \ if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ check_cursor(); \ block; \ } \ restore_win_noblock(&switchwin_, true); \ + if (apply_acd_) { \ + do_autochdir(); \ + } else if (cwd_status_ == OK) { \ + os_chdir((char *)cwd_); \ + } \ /* Update the status line if the cursor moved. */ \ if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ wp_->w_redr_status = true; \ diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index e50e812b4b..372cbf2c30 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua local matches = helpers.matches local meths = helpers.meths local source = helpers.source +local pcall_err = helpers.pcall_err before_each(clear) @@ -225,6 +226,86 @@ describe('autocmd api', function() local aus = meths.get_autocmds { event = "InsertEnter" } eq({ { buflocal = false, command = ':echo "1"', event = "InsertEnter", once = false, pattern = "*" } }, aus) end) + + it('should work with buffer numbers', function() + command [[new]] + command [[au! InsertEnter]] + command [[au InsertEnter <buffer=1> :echo "1"]] + command [[au InsertEnter <buffer=2> :echo "2"]] + + local aus = meths.get_autocmds { event = "InsertEnter", buffer = 0 } + eq({{ + buffer = 2, + buflocal = true, + command = ':echo "2"', + event = 'InsertEnter', + once = false, + pattern = '<buffer=2>', + }}, aus) + + aus = meths.get_autocmds { event = "InsertEnter", buffer = 1 } + eq({{ + buffer = 1, + buflocal = true, + command = ':echo "1"', + event = "InsertEnter", + once = false, + pattern = "<buffer=1>", + }}, aus) + + aus = meths.get_autocmds { event = "InsertEnter", buffer = { 1, 2 } } + eq({{ + buffer = 1, + buflocal = true, + command = ':echo "1"', + event = "InsertEnter", + once = false, + pattern = "<buffer=1>", + }, { + buffer = 2, + buflocal = true, + command = ':echo "2"', + event = "InsertEnter", + once = false, + pattern = "<buffer=2>", + }}, aus) + + eq("Invalid value for 'buffer': must be an integer or array of integers", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = "foo" })) + eq("Invalid value for 'buffer': must be an integer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { "foo", 42 } })) + eq("Invalid buffer id: 42", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = { 42 } })) + + local bufs = {} + for _ = 1, 257 do + table.insert(bufs, meths.create_buf(true, false)) + end + + eq("Too many buffers. Please limit yourself to 256 or fewer", pcall_err(meths.get_autocmds, { event = "InsertEnter", buffer = bufs })) + end) + + it('should return autocmds when group is specified by id', function() + local auid = meths.create_augroup("nvim_test_augroup", { clear = true }) + meths.create_autocmd("FileType", { group = auid, command = 'echo "1"' }) + meths.create_autocmd("FileType", { group = auid, command = 'echo "2"' }) + + local aus = meths.get_autocmds { group = auid } + eq(2, #aus) + + local aus2 = meths.get_autocmds { group = auid, event = "InsertEnter" } + eq(0, #aus2) + end) + + it('should return autocmds when group is specified by name', function() + local auname = "nvim_test_augroup" + meths.create_augroup(auname, { clear = true }) + meths.create_autocmd("FileType", { group = auname, command = 'echo "1"' }) + meths.create_autocmd("FileType", { group = auname, command = 'echo "2"' }) + + local aus = meths.get_autocmds { group = auname } + eq(2, #aus) + + local aus2 = meths.get_autocmds { group = auname, event = "InsertEnter" } + eq(0, #aus2) + end) end) describe('groups', function() @@ -275,7 +356,7 @@ describe('autocmd api', function() end) describe('groups: 2', function() - it('raises error for undefined augroup', function() + it('raises error for undefined augroup name', function() local success, code = unpack(meths.exec_lua([[ return {pcall(function() vim.api.nvim_create_autocmd("FileType", { @@ -289,6 +370,39 @@ describe('autocmd api', function() eq(false, success) matches('invalid augroup: NotDefined', code) end) + + it('raises error for undefined augroup id', function() + local success, code = unpack(meths.exec_lua([[ + return {pcall(function() + -- Make sure the augroup is deleted + vim.api.nvim_del_augroup_by_id(1) + + vim.api.nvim_create_autocmd("FileType", { + pattern = "*", + group = 1, + command = "echo 'hello'", + }) + end)} + ]], {})) + + eq(false, success) + matches('invalid augroup: 1', code) + end) + + it('raises error for invalid group type', function() + local success, code = unpack(meths.exec_lua([[ + return {pcall(function() + vim.api.nvim_create_autocmd("FileType", { + pattern = "*", + group = true, + command = "echo 'hello'", + }) + end)} + ]], {})) + + eq(false, success) + matches("'group' must be a string or an integer", code) + end) end) describe('patterns', function() @@ -441,6 +555,35 @@ describe('autocmd api', function() meths.do_autocmd("User", { pattern = "TestCommand" }) eq('matched', meths.get_var('matched')) end) + + it('can pass group by id', function() + meths.set_var("group_executed", false) + + local auid = meths.create_augroup("nvim_test_augroup", { clear = true }) + meths.create_autocmd("FileType", { + group = auid, + command = 'let g:group_executed = v:true', + }) + + eq(false, meths.get_var("group_executed")) + meths.do_autocmd("FileType", { group = auid }) + eq(true, meths.get_var("group_executed")) + end) + + it('can pass group by name', function() + meths.set_var("group_executed", false) + + local auname = "nvim_test_augroup" + meths.create_augroup(auname, { clear = true }) + meths.create_autocmd("FileType", { + group = auname, + command = 'let g:group_executed = v:true', + }) + + eq(false, meths.get_var("group_executed")) + meths.do_autocmd("FileType", { group = auname }) + eq(true, meths.get_var("group_executed")) + end) end) describe('nvim_create_augroup', function() diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 443689754c..06cdb0bc19 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -28,8 +28,11 @@ describe('API: highlight',function() bold = true, italic = true, reverse = true, - undercurl = true, underline = true, + underlineline = true, + undercurl = true, + underdot = true, + underdash = true, strikethrough = true, } @@ -52,7 +55,7 @@ describe('API: highlight',function() eq('Invalid highlight id: 30000', string.match(emsg, 'Invalid.*')) -- Test all highlight properties. - command('hi NewHighlight gui=underline,bold,undercurl,italic,reverse,strikethrough') + command('hi NewHighlight gui=underline,bold,underlineline,undercurl,underdot,underdash,italic,reverse,strikethrough') eq(expected_rgb2, nvim("get_hl_by_id", hl_id, true)) -- Test nil argument. @@ -195,6 +198,9 @@ describe("API: set highlight", function() reverse = true, undercurl = true, underline = true, + underdash = true, + underdot = true, + underlineline = true, strikethrough = true, cterm = { italic = true, @@ -211,6 +217,9 @@ describe("API: set highlight", function() reverse = true, undercurl = true, underline = true, + underdash = true, + underdot = true, + underlineline = true, strikethrough = true, } local highlight3_result_cterm = { @@ -274,7 +283,7 @@ describe("API: set highlight", function() exec_capture('highlight Test_hl')) meths.set_hl(0, 'Test_hl2', highlight3_config) - eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,undercurl,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', + eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,underlineline,undercurl,underdot,underdash,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', exec_capture('highlight Test_hl2')) -- Colors are stored exactly as they are defined. diff --git a/test/functional/legacy/autochdir_spec.lua b/test/functional/legacy/autochdir_spec.lua index 37a94476a0..13cb6cd287 100644 --- a/test/functional/legacy/autochdir_spec.lua +++ b/test/functional/legacy/autochdir_spec.lua @@ -1,8 +1,12 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) local clear, eq, matches = helpers.clear, helpers.eq, helpers.matches -local eval, command, call = helpers.eval, helpers.command, helpers.call -local exec_capture = helpers.exec_capture +local eval, command, call, meths = helpers.eval, helpers.command, helpers.call, helpers.meths +local source, exec_capture = helpers.source, helpers.exec_capture + +local function expected_empty() + eq({}, meths.get_vvar('errors')) +end describe('autochdir behavior', function() local dir = 'Xtest_functional_legacy_autochdir' @@ -10,19 +14,66 @@ describe('autochdir behavior', function() before_each(function() lfs.mkdir(dir) clear() + command('set shellslash') end) after_each(function() helpers.rmdir(dir) end) - -- Tests vim/vim/777 without test_autochdir(). + -- Tests vim/vim#777 without test_autochdir(). it('sets filename', function() command('set acd') command('new') command('w '..dir..'/Xtest') eq('Xtest', eval("expand('%')")) - eq(dir, eval([[substitute(getcwd(), '.*[/\\]\(\k*\)', '\1', '')]])) + eq(dir, eval([[substitute(getcwd(), '.*/\(\k*\)', '\1', '')]])) + end) + + it(':file in win_execute() does not cause wrong directory', function() + command('cd '..dir) + source([[ + func Test_set_filename_other_window() + let cwd = getcwd() + call mkdir('Xa') + call mkdir('Xb') + call mkdir('Xc') + try + args Xa/aaa.txt Xb/bbb.txt + set acd + let winid = win_getid() + snext + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt') + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + finally + set noacd + call chdir(cwd) + call delete('Xa', 'rf') + call delete('Xb', 'rf') + call delete('Xc', 'rf') + bwipe! aaa.txt + bwipe! bbb.txt + bwipe! ccc.txt + endtry + endfunc + ]]) + call('Test_set_filename_other_window') + expected_empty() + end) + + it('win_execute() does not change directory', function() + local subdir = 'Xfile' + command('cd '..dir) + command('set acd') + call('mkdir', subdir) + local winid = eval('win_getid()') + command('new '..subdir..'/file') + matches(dir..'/'..subdir..'$', eval('getcwd()')) + command('cd ..') + matches(dir..'$', eval('getcwd()')) + call('win_execute', winid, 'echo') + matches(dir..'$', eval('getcwd()')) end) it(':verbose pwd shows whether autochdir is used', function() @@ -30,29 +81,36 @@ describe('autochdir behavior', function() command('cd '..dir) local cwd = eval('getcwd()') command('edit global.txt') - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) call('mkdir', subdir) command('split '..subdir..'/local.txt') command('lcd '..subdir) - matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) - command('set autochdir') + matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('set acd') command('wincmd w') - matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) - command('lcd '..cwd) - matches('%[window%].*'..dir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd')) command('tcd '..cwd) - matches('%[tabpage%].*'..dir, exec_capture('verbose pwd')) + matches('%[tabpage%].*'..dir..'$', exec_capture('verbose pwd')) command('cd '..cwd) - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) + command('lcd '..cwd) + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) command('edit') - matches('%[autochdir%].*'..dir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'$', exec_capture('verbose pwd')) + command('enew') command('wincmd w') - matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) - command('set noautochdir') - matches('%[autochdir%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) command('wincmd w') - matches('%[global%].*'..dir, exec_capture('verbose pwd')) + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) + command('wincmd w') + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('set noacd') + matches('%[autochdir%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) + command('wincmd w') + matches('%[window%].*'..dir..'$', exec_capture('verbose pwd')) + command('cd '..cwd) + matches('%[global%].*'..dir..'$', exec_capture('verbose pwd')) command('wincmd w') - matches('%[window%].*'..dir..'[/\\]'..subdir, exec_capture('verbose pwd')) + matches('%[window%].*'..dir..'/'..subdir..'$', exec_capture('verbose pwd')) end) end) diff --git a/test/functional/options/autochdir_spec.lua b/test/functional/options/autochdir_spec.lua index 2fce0a5ed9..74959a8e76 100644 --- a/test/functional/options/autochdir_spec.lua +++ b/test/functional/options/autochdir_spec.lua @@ -1,7 +1,9 @@ +local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear local eq = helpers.eq -local getcwd = helpers.funcs.getcwd +local funcs = helpers.funcs +local command = helpers.command describe("'autochdir'", function() it('given on the shell gets processed properly', function() @@ -9,11 +11,34 @@ describe("'autochdir'", function() -- By default 'autochdir' is off, thus getcwd() returns the repo root. clear(targetdir..'/tty-test.c') - local rootdir = getcwd() + local rootdir = funcs.getcwd() local expected = rootdir .. '/' .. targetdir -- With 'autochdir' on, we should get the directory of tty-test.c. clear('--cmd', 'set autochdir', targetdir..'/tty-test.c') - eq(helpers.iswin() and expected:gsub('/', '\\') or expected, getcwd()) + eq(helpers.iswin() and expected:gsub('/', '\\') or expected, funcs.getcwd()) + end) + + it('is not overwritten by getwinvar() call #17609',function() + local curdir = string.gsub(lfs.currentdir(), '\\', '/') + local dir_a = curdir..'/Xtest-functional-options-autochdir.dir_a' + local dir_b = curdir..'/Xtest-functional-options-autochdir.dir_b' + lfs.mkdir(dir_a) + lfs.mkdir(dir_b) + clear() + command('set shellslash') + command('set autochdir') + command('edit '..dir_a..'/file1') + eq(dir_a, funcs.getcwd()) + command('lcd '..dir_b) + eq(dir_b, funcs.getcwd()) + command('botright vnew ../file2') + eq(curdir, funcs.getcwd()) + command('wincmd w') + eq(dir_a, funcs.getcwd()) + funcs.getwinvar(2, 'foo') + eq(dir_a, funcs.getcwd()) + helpers.rmdir(dir_a) + helpers.rmdir(dir_b) end) end) diff --git a/test/functional/plugin/lsp/incremental_sync_spec.lua b/test/functional/plugin/lsp/incremental_sync_spec.lua index 4e3eddb960..4985da9cd7 100644 --- a/test/functional/plugin/lsp/incremental_sync_spec.lua +++ b/test/functional/plugin/lsp/incremental_sync_spec.lua @@ -207,16 +207,16 @@ describe('incremental synchronization', function() { range = { ['start'] = { - character = 0, - line = 1 + character = 11, + line = 0, }, ['end'] = { character = 0, line = 1 } }, - rangeLength = 0, - text = 'hello world\n' + rangeLength = 1, + text = '\nhello world\n' } } test_edit({"hello world"}, {"yyp"}, expected_text_changes, 'utf-16', '\n') @@ -226,19 +226,57 @@ describe('incremental synchronization', function() { range = { ['start'] = { + character = 11, + line = 0 + }, + ['end'] = { character = 0, line = 1 + } + }, + rangeLength = 1, + text = '\n\n' + } + } + test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n') + end) + it('adding a line to an empty buffer', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 0 }, ['end'] = { character = 0, line = 1 } }, + rangeLength = 1, + text = '\n\n' + } + } + test_edit({""}, {"o"}, expected_text_changes, 'utf-16', '\n') + end) + it('insert a line above the current line', function() + local expected_text_changes = { + { + range = { + ['start'] = { + character = 0, + line = 0 + }, + ['end'] = { + character = 0, + line = 0 + } + }, rangeLength = 0, text = '\n' } } - test_edit({"hello world"}, {"o"}, expected_text_changes, 'utf-16', '\n') + test_edit({""}, {"O"}, expected_text_changes, 'utf-16', '\n') end) end) describe('multi line edit', function() diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index 986db96a18..5bdfec574e 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -50,29 +50,33 @@ local function basic_register_test(noblock) text, stuff and some more some some text, stuff and some more]]) - -- deleting a word to named ("a) updates "1 (and not "-) + -- deleting a word to named ("a) doesn't update "1 or "- feed('gg"adwj"1P^"-P') expect([[ , stuff and some more - some textsome some text, stuff and some more]]) + some some random text + some some text, stuff and some more]]) -- deleting a line does update "" feed('ggdd""P') expect([[ , stuff and some more - some textsome some text, stuff and some more]]) + some some random text + some some text, stuff and some more]]) feed('ggw<c-v>jwyggP') if noblock then expect([[ stuf - me t + me s , stuff and some more - some textsome some text, stuff and some more]]) + some some random text + some some text, stuff and some more]]) else expect([[ stuf, stuff and some more - me tsome textsome some text, stuff and some more]]) + me ssome some random text + some some text, stuff and some more]]) end -- pasting in visual does unnamed delete of visual selection diff --git a/test/functional/terminal/highlight_spec.lua b/test/functional/terminal/highlight_spec.lua index ebbae1df14..ab4b4e9147 100644 --- a/test/functional/terminal/highlight_spec.lua +++ b/test/functional/terminal/highlight_spec.lua @@ -305,10 +305,17 @@ describe('synIDattr()', function() eq('79', eval('synIDattr(hlID("Keyword"), "fg")')) end) - it('returns "1" if group has "strikethrough" attribute', function() - eq('', eval('synIDattr(hlID("Normal"), "strikethrough")')) - eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough")')) - eq('1', eval('synIDattr(hlID("Keyword"), "strikethrough", "gui")')) + it('returns "1" if group has given highlight attribute', function() + local hl_attrs = { + 'underline', 'underlineline', 'undercurl', 'underdot', 'underdash', 'strikethrough' + } + for _,hl_attr in ipairs(hl_attrs) do + local context = 'using ' .. hl_attr .. ' attr' + command('highlight Keyword cterm=' .. hl_attr .. ' gui=' .. hl_attr) + eq('', eval('synIDattr(hlID("Normal"), "'.. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '")'), context) + eq('1', eval('synIDattr(hlID("Keyword"), "' .. hl_attr .. '", "gui")'), context) + end end) end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 1575cab591..ae5d34fa7a 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1362,3 +1362,276 @@ if (h->n_buckets < new_n_buckets) { // expand end) end) + +describe('decorations: signs', function() + local screen, ns + before_each(function() + clear() + screen = Screen.new(50, 10) + screen:attach() + screen:set_default_attr_ids { + [1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Grey}; + [2] = {foreground = Screen.colors.Blue1, bold = true}; + } + + ns = meths.create_namespace 'test' + meths.win_set_option(0, 'signcolumn', 'auto:9') + end) + + local example_text = [[ +l1 +l2 +l3 +l4 +l5 +]] + + it('can add a single sign (no end row)', function() + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'}) + + screen:expect{grid=[[ + {1: }^l1 | + S l2 | + {1: }l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + end) + + it('can add a single sign (with end row)', function() + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1}) + + screen:expect{grid=[[ + {1: }^l1 | + S l2 | + {1: }l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + end) + + it('can add multiple signs (single extmark)', function() + pending('TODO(lewis6991): Support ranged signs') + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2}) + + screen:expect{grid=[[ + {1: }^l1 | + S l2 | + S l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + end) + + it('can add multiple signs (multiple extmarks)', function() + pending('TODO(lewis6991): Support ranged signs') + insert(example_text) + feed'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) + meths.buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4}) + + screen:expect{grid=[[ + {1: }^l1 | + S1l2 | + {1: }l3 | + S2l4 | + S2l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + end) + + it('can add multiple signs (multiple extmarks) 2', function() + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'}) + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'}) + + screen:expect{grid=[[ + {1: }^l1 | + S2S1l2 | + {1: }l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + -- TODO(lewis6991): Support ranged signs + -- meths.buf_set_extmark(1, ns, 1, -1, {sign_text='S3', end_row = 2}) + + -- screen:expect{grid=[[ + -- {1: }^l1 | + -- S3S2S1l2 | + -- S3{1: }l3 | + -- {1: }l4 | + -- {1: }l5 | + -- {1: } | + -- {2:~ }| + -- {2:~ }| + -- {2:~ }| + -- | + -- ]]} + + end) + + it('can add multiple signs (multiple extmarks) 3', function() + pending('TODO(lewis6991): Support ranged signs') + + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2}) + meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3}) + + screen:expect{grid=[[ + {1: }^l1 | + S1{1: }l2 | + S2S1l3 | + S2{1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + end) + + it('can add multiple signs (multiple extmarks) 4', function() + insert(example_text) + feed 'gg' + + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0}) + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row=1}) + + screen:expect{grid=[[ + S1^l1 | + S2l2 | + {1: }l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + end) + + it('works with old signs', function() + insert(example_text) + feed 'gg' + + helpers.command('sign define Oldsign text=x') + helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) + + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'}) + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'}) + meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'}) + + screen:expect{grid=[[ + S4S1^l1 | + S2x l2 | + S5{1: }l3 | + {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + end) + + it('works with old signs (with range)', function() + pending('TODO(lewis6991): Support ranged signs') + insert(example_text) + feed 'gg' + + helpers.command('sign define Oldsign text=x') + helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]]) + + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'}) + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S3', end_row = 4}) + meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'}) + meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'}) + + screen:expect{grid=[[ + S3S4S1^l1 | + S2S3x l2 | + S5S3{1: }l3 | + S3{1: }l4 | + S3{1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + end) + + it('can add a ranged sign (with start out of view)', function() + pending('TODO(lewis6991): Support ranged signs') + + insert(example_text) + command 'set signcolumn=yes:2' + feed 'gg' + feed '2<C-e>' + + meths.buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3}) + + screen:expect{grid=[[ + X {1: }^l3 | + X {1: }l4 | + {1: }l5 | + {1: } | + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + {2:~ }| + | + ]]} + + end) + +end) diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index bd2692d19a..3a25d7e813 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -6,7 +6,7 @@ local clear = helpers.clear local command = helpers.command local insert = helpers.insert local write_file = helpers.write_file -local source = helpers.source +local exec = helpers.exec describe('Diff mode screen', function() local fname = 'Xtest-functional-diff-screen-1' @@ -1075,10 +1075,8 @@ it('diff updates line numbers below filler lines', function() [9] = {background = Screen.colors.LightMagenta}, [10] = {bold = true, foreground = Screen.colors.Brown}, [11] = {foreground = Screen.colors.Brown}, - [12] = {foreground = Screen.colors.Brown, bold = true, background = Screen.colors.Red}; - [13] = {background = Screen.colors.Gray90}; }) - source([[ + exec([[ call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b']) vnew call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b']) @@ -1135,24 +1133,6 @@ it('diff updates line numbers below filler lines', function() {3:[No Name] [+] }{7:[No Name] [+] }| | ]]) - command("set signcolumn number tgc cursorline cursorlineopt=number,line") - command("hi CursorLineNr guibg=red") - screen:expect{grid=[[ - {1: }a {3:│}{11: 2 }a | - {1: }a {3:│}{11: 1 }a | - {1: }a {3:│}{12:3 }{13:^a }| - {1: }{8:x}{9: }{3:│}{11: 1 }{8:y}{9: }| - {1: }{4:x }{3:│}{11: }{2:----------------}| - {1: }{4:x }{3:│}{11: }{2:----------------}| - {1: }b {3:│}{11: 2 }b | - {1: }b {3:│}{11: 3 }b | - {1: }b {3:│}{11: 4 }b | - {1: }b {3:│}{11: 5 }b | - {1: }b {3:│}{11: 6 }b | - {6:~ }{3:│}{6:~ }| - {3:[No Name] [+] }{7:[No Name] [+] }| - signcolumn=auto | - ]]} end) it('Align the filler lines when changing text in diff mode', function() @@ -1169,7 +1149,7 @@ it('Align the filler lines when changing text in diff mode', function() [7] = {foreground = Screen.colors.Blue1, bold = true}; [8] = {reverse = true, bold = true}; }) - source([[ + exec([[ call setline(1, range(1, 15)) vnew call setline(1, range(9, 15)) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 12643eec1e..64f0ba3419 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -840,7 +840,7 @@ describe("'listchars' highlight", function() end) end) -describe('CursorLine highlight', function() +describe('CursorLine and CursorLineNr highlights', function() before_each(clear) it('overridden by Error, ColorColumn if fg not set', function() @@ -1081,7 +1081,7 @@ describe('CursorLine highlight', function() ]]) end) - it('with split-windows in diff-mode', function() + it('with split windows in diff mode', function() local screen = Screen.new(50,12) screen:set_default_attr_ids({ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, @@ -1093,7 +1093,6 @@ describe('CursorLine highlight', function() [7] = {background = Screen.colors.Red, foreground = Screen.colors.White}, [8] = {bold = true, foreground = Screen.colors.Blue1}, [9] = {bold = true, reverse = true}, - [10] = {bold = true}, }) screen:attach() @@ -1170,6 +1169,61 @@ describe('CursorLine highlight', function() background = Screen.colors.Red}, }) end) + + it('CursorLineNr shows correctly just below filler lines', function() + local screen = Screen.new(50,12) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, + [2] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1}, + [3] = {reverse = true}, + [4] = {background = Screen.colors.LightBlue}, + [5] = {background = Screen.colors.Red, foreground = Screen.colors.White}, + [6] = {background = Screen.colors.White, bold = true, foreground = Screen.colors.Black}, + [7] = {bold = true, foreground = Screen.colors.Blue1}, + [8] = {bold = true, reverse = true}, + [9] = {foreground = Screen.colors.Brown}, + }) + screen:attach() + + command('hi CursorLine guibg=red guifg=white') + command('hi CursorLineNr guibg=white guifg=black gui=bold') + command('set cursorline number') + command('call setline(1, ["baz", "foo", "foo", "bar"])') + feed('2gg0') + command('vnew') + command('call setline(1, ["foo", "foo", "bar"])') + command('windo diffthis') + command('1wincmd w') + screen:expect([[ + {1: }{9: }{2:-------------------}{3:│}{1: }{9: 1 }{4:baz }| + {1: }{6: 1 }{5:^foo }{3:│}{1: }{6: 2 }{5:foo }| + {1: }{9: 2 }foo {3:│}{1: }{9: 3 }foo | + {1: }{9: 3 }bar {3:│}{1: }{9: 4 }bar | + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + command('set cursorlineopt=number') + screen:expect([[ + {1: }{9: }{2:-------------------}{3:│}{1: }{9: 1 }{4:baz }| + {1: }{6: 1 }^foo {3:│}{1: }{6: 2 }{5:foo }| + {1: }{9: 2 }foo {3:│}{1: }{9: 3 }foo | + {1: }{9: 3 }bar {3:│}{1: }{9: 4 }bar | + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {7:~ }{3:│}{7:~ }| + {8:[No Name] [+] }{3:[No Name] [+] }| + | + ]]) + end) end) describe('ColorColumn highlight', function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index a5af898652..3e94fdbf44 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -1559,10 +1559,11 @@ end function Screen:_equal_attrs(a, b) return a.bold == b.bold and a.standout == b.standout and - a.underline == b.underline and a.undercurl == b.undercurl and - a.italic == b.italic and a.reverse == b.reverse and - a.foreground == b.foreground and a.background == b.background and - a.special == b.special and a.blend == b.blend and + a.underline == b.underline and a.underlineline == b.underlineline and + a.undercurl == b.undercurl and a.underdot == b.underdot and + a.underdash == b.underdash and a.italic == b.italic and + a.reverse == b.reverse and a.foreground == b.foreground and + a.background == b.background and a.special == b.special and a.blend == b.blend and a.strikethrough == b.strikethrough and a.fg_indexed == b.fg_indexed and a.bg_indexed == b.bg_indexed end diff --git a/test/unit/strings_spec.lua b/test/unit/strings_spec.lua index e54c82b26a..e085ac749d 100644 --- a/test/unit/strings_spec.lua +++ b/test/unit/strings_spec.lua @@ -138,3 +138,15 @@ describe('vim_strchr()', function() eq(nil, vim_strchr('«\237\175\191\237\188\128»', 0x10FF00)) end) end) + +describe('strcase_save()' , function() + local strcase_save = function(input_string, upper) + local res = strings.strcase_save(to_cstr(input_string), upper) + return ffi.string(res) + end + + itp('decodes overlong encoded characters.', function() + eq("A", strcase_save("\xc1\x81", true)) + eq("a", strcase_save("\xc1\x81", false)) + end) +end) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 2001171378..30c4752b87 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -144,8 +144,8 @@ endif() include(ExternalProject) -set(LIBUV_URL https://github.com/libuv/libuv/archive/7ae0c9543d0080968766288c73874aee3798ae30.tar.gz) -set(LIBUV_SHA256 02ade646f52221e56f2515f8d0bfb8099204d21f6973b2a139bc726807ea803c) +set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.44.0.tar.gz) +set(LIBUV_SHA256 f2482d547009d26d4d590ed6588043c540e707c833df52536744cb9a809e6617) set(MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/cpp-3.0.0/msgpack-3.0.0.tar.gz) set(MSGPACK_SHA256 bfbb71b7c02f806393bc3cbc491b40523b89e64f83860c58e3e54af47de176e4) @@ -203,8 +203,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891 set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.1.tar.gz) set(TREESITTER_C_SHA256 ffcc2ef0eded59ad1acec9aec4f9b0c7dd209fc1a85d85f8b0e81298e3dddcc2) -set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.3.tar.gz) -set(TREESITTER_SHA256 ab52fe93e0c658cff656b9d10d67cdd29084247052964eba13ed6f0e9fa3bd36) +set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.6.tar.gz) +set(TREESITTER_SHA256 4d37eaef8a402a385998ff9aca3e1043b4a3bba899bceeff27a7178e1165b9de) if(USE_BUNDLED_UNIBILIUM) include(BuildUnibilium) diff --git a/third-party/cmake/BuildTreesitterParsers.cmake b/third-party/cmake/BuildTreesitterParsers.cmake index f966d640e6..4ceb402455 100644 --- a/third-party/cmake/BuildTreesitterParsers.cmake +++ b/third-party/cmake/BuildTreesitterParsers.cmake @@ -15,7 +15,6 @@ PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${DEPS_BUILD_DIR}/src/treesitter-c/CMakeLists.txt CMAKE_ARGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_GENERATOR=${CMAKE_GENERATOR} -DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} |