diff options
129 files changed, 4704 insertions, 2296 deletions
diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml index 413c2e90c6..ee72cf5f01 100644 --- a/.github/workflows/api-docs.yml +++ b/.github/workflows/api-docs.yml @@ -26,7 +26,9 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y doxygen python3 python3-msgpack luajit + sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y python3 luajit + conda install -c conda-forge doxygen=1.9.2 msgpack-python + echo "$CONDA/bin" >> $GITHUB_PATH - name: Setup git config run: | diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index 613475b00d..30f08c5297 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -45,6 +45,7 @@ check_function_exists(readlink HAVE_READLINK) check_function_exists(setpgid HAVE_SETPGID) check_function_exists(setsid HAVE_SETSID) check_function_exists(sigaction HAVE_SIGACTION) +check_function_exists(strnlen HAVE_STRNLEN) check_function_exists(strcasecmp HAVE_STRCASECMP) check_function_exists(strncasecmp HAVE_STRNCASECMP) check_function_exists(strptime HAVE_STRPTIME) diff --git a/config/config.h.in b/config/config.h.in index 27a28116af..b0635fb52b 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -30,6 +30,7 @@ #cmakedefine HAVE_SETPGID #cmakedefine HAVE_SETSID #cmakedefine HAVE_SIGACTION +#cmakedefine HAVE_STRNLEN #cmakedefine HAVE_STRCASECMP #cmakedefine HAVE_STRINGS_H #cmakedefine HAVE_STRNCASECMP diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 74df871544..a3db4ae87a 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -1,7 +1,7 @@ " Vim functions for file type detection " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2020 Aug 17 +" Last Change: 2021 Nov 27 " These functions are moved here from runtime/filetype.vim to make startup " faster. diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 482d8c198d..d2d010882e 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2353,7 +2353,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) |api-indexing| {opts} Optional parameters. • id : id of the extmark to edit. - • end_line : ending line of the mark, 0-based + • end_row : ending line of the mark, 0-based inclusive. • end_col : ending col of the mark, 0-based exclusive. diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 656bb10c45..5f376a600e 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -210,6 +210,11 @@ effective prompt text for a buffer, with |prompt_getprompt()|. The user can go to Normal mode and navigate through the buffer. This can be useful to see older output or copy text. +The CTRL-W key can be used to start a window command, such as CTRL-W w to +switch to the next window. This also works in Insert mode (use Shift-CTRL-W +to delete a word). When leaving the window Insert mode will be stopped. When +coming back to the prompt window Insert mode will be restored. + Any command that starts Insert mode, such as "a", "i", "A" and "I", will move the cursor to the last line. "A" will move to the end of the line, "I" to the start of the line. diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 6b46ac9cf2..7716af25bd 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -863,9 +863,11 @@ Note: these are typed literally, they are not special keys! *:<amatch>* *<amatch>* <amatch> When executing autocommands, is replaced with the match for which this autocommand was executed. *E497* - It differs from <afile> only when the file name isn't used - to match with (for FileType, Syntax and SpellFileMissing + It differs from <afile> when the file name isn't used to + match with (for FileType, Syntax and SpellFileMissing events). + When the match is with a file name, it is expanded to the + full path. *:<sfile>* *<sfile>* <sfile> When executing a ":source" command, is replaced with the file name of the sourced file. *E498* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 9a65737dae..d02510a829 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -574,61 +574,64 @@ match({str}, {pat}, {groups}, {severity_map}, {defaults}) diagnostic |diagnostic-structure| or `nil` if {pat} fails to match {str}. -open_float({bufnr}, {opts}) *vim.diagnostic.open_float()* +open_float({opts}, {...}) *vim.diagnostic.open_float()* Show diagnostics in a floating window. Parameters: ~ - {bufnr} number|nil Buffer number. Defaults to the current - buffer. - {opts} table|nil Configuration table with the same keys - as |vim.lsp.util.open_floating_preview()| in - addition to the following: - • namespace: (number) Limit diagnostics to the - given namespace - • scope: (string, default "line") Show - diagnostics from the whole buffer ("buffer"), - the current cursor line ("line"), or the - current cursor position ("cursor"). - • pos: (number or table) If {scope} is "line" or - "cursor", use this position rather than the - cursor position. If a number, interpreted as a - line number; otherwise, a (row, col) tuple. - • severity_sort: (default false) Sort diagnostics - by severity. Overrides the setting from - |vim.diagnostic.config()|. - • severity: See |diagnostic-severity|. Overrides - the setting from |vim.diagnostic.config()|. - • header: (string or table) String to use as the - header for the floating window. If a table, it - is interpreted as a [text, hl_group] tuple. - Overrides the setting from - |vim.diagnostic.config()|. - • source: (string) Include the diagnostic source - in the message. One of "always" or "if_many". - Overrides the setting from - |vim.diagnostic.config()|. - • format: (function) A function that takes a - diagnostic as input and returns a string. The - return value is the text used to display the - diagnostic. Overrides the setting from - |vim.diagnostic.config()|. - • prefix: (function, string, or table) Prefix - each diagnostic in the floating window. If a - function, it must have the signature - (diagnostic, i, total) -> (string, string), - where {i} is the index of the diagnostic being - evaluated and {total} is the total number of - diagnostics displayed in the window. The - function should return a string which is - prepended to each diagnostic in the window as - well as an (optional) highlight group which - will be used to highlight the prefix. If - {prefix} is a table, it is interpreted as a - [text, hl_group] tuple as in |nvim_echo()|; - otherwise, if {prefix} is a string, it is - prepended to each diagnostic in the window with - no highlight. Overrides the setting from - |vim.diagnostic.config()|. + {opts} table|nil Configuration table with the same keys + as |vim.lsp.util.open_floating_preview()| in + addition to the following: + • bufnr: (number) Buffer number to show + diagnostics from. Defaults to the current + buffer. + • namespace: (number) Limit diagnostics to the + given namespace + • scope: (string, default "line") Show diagnostics + from the whole buffer ("buffer"), the current + cursor line ("line"), or the current cursor + position ("cursor"). Shorthand versions are also + accepted ("c" for "cursor", "l" for "line", "b" + for "buffer"). + • pos: (number or table) If {scope} is "line" or + "cursor", use this position rather than the + cursor position. If a number, interpreted as a + line number; otherwise, a (row, col) tuple. + • severity_sort: (default false) Sort diagnostics + by severity. Overrides the setting from + |vim.diagnostic.config()|. + • severity: See |diagnostic-severity|. Overrides + the setting from |vim.diagnostic.config()|. + • header: (string or table) String to use as the + header for the floating window. If a table, it + is interpreted as a [text, hl_group] tuple. + Overrides the setting from + |vim.diagnostic.config()|. + • source: (string) Include the diagnostic source + in the message. One of "always" or "if_many". + Overrides the setting from + |vim.diagnostic.config()|. + • format: (function) A function that takes a + diagnostic as input and returns a string. The + return value is the text used to display the + diagnostic. Overrides the setting from + |vim.diagnostic.config()|. + • prefix: (function, string, or table) Prefix each + diagnostic in the floating window. If a + function, it must have the signature + (diagnostic, i, total) -> (string, string), + where {i} is the index of the diagnostic being + evaluated and {total} is the total number of + diagnostics displayed in the window. The + function should return a string which is + prepended to each diagnostic in the window as + well as an (optional) highlight group which will + be used to highlight the prefix. If {prefix} is + a table, it is interpreted as a [text, hl_group] + tuple as in |nvim_echo()|; otherwise, if + {prefix} is a string, it is prepended to each + diagnostic in the window with no highlight. + Overrides the setting from + |vim.diagnostic.config()|. Return: ~ tuple ({float_bufnr}, {win_id}) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 4d4e011c08..75b782fbff 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2042,10 +2042,29 @@ v:option_new New value of the option. Valid while executing an |OptionSet| autocommand. *v:option_old* v:option_old Old value of the option. Valid while executing an |OptionSet| - autocommand. + autocommand. Depending on the command used for setting and the + kind of option this is either the local old value or the + global old value. + *v:option_oldlocal* +v:option_oldlocal + Old local value of the option. Valid while executing an + |OptionSet| autocommand. + *v:option_oldglobal* +v:option_oldglobal + Old global value of the option. Valid while executing an + |OptionSet| autocommand. *v:option_type* v:option_type Scope of the set command. Valid while executing an |OptionSet| autocommand. Can be either "global" or "local" + *v:option_command* +v:option_command + Command used to set the option. Valid while executing an + |OptionSet| autocommand. + value option was set via ~ + "setlocal" |:setlocal| or ":let l:xxx" + "setglobal" |:setglobal| or ":let g:xxx" + "set" |:set| or |:let| + "modeline" |modeline| *v:operator* *operator-variable* v:operator The last operator given in Normal mode. This is a single character except for commands starting with <g> or <z>, @@ -2992,7 +3011,11 @@ bufadd({name}) *bufadd()* number. Otherwise return the buffer number of the newly created buffer. When {name} is an empty string then a new buffer is always created. - The buffer will not have' 'buflisted' set. + The buffer will not have 'buflisted' set and not be loaded + yet. To add some text to the buffer use this: > + let bufnr = bufadd('someName') + call bufload(bufnr) + call setbufline(bufnr, 1, ['some', 'text']) < Can also be used as a |method|: > let bufnr = 'somename'->bufadd() @@ -3258,7 +3281,7 @@ char2nr({string} [, {utf8}]) *char2nr()* Can also be used as a |method|: > GetChar()->char2nr() - +< *charidx()* charidx({string}, {idx} [, {countcc}]) Return the character index of the byte at {idx} in {string}. @@ -3281,6 +3304,9 @@ charidx({string}, {idx} [, {countcc}]) echo charidx('áb́ć', 3) returns 1 echo charidx('áb́ć', 6, 1) returns 4 echo charidx('áb́ć', 16) returns -1 +< + Can also be used as a |method|: > + GetName()->charidx(idx) chdir({dir}) *chdir()* Change the current working directory to {dir}. The scope of @@ -3393,8 +3419,8 @@ complete({startcol}, {matches}) *complete()* *E785* < This isn't very useful, but it shows how it works. Note that an empty string is returned to avoid a zero being inserted. - Can also be used as a |method|, the second argument is passed - in: > + Can also be used as a |method|, the base is passed as the + second argument: > GetMatches()->complete(col('.')) complete_add({expr}) *complete_add()* @@ -3531,7 +3557,7 @@ confirm({msg} [, {choices} [, {default} [, {type}]]]) Can also be used as a |method|in: > BuildMessage()->confirm("&Yes\n&No") - +< *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. @@ -3742,7 +3768,7 @@ deletebufline({buf}, {first}[, {last}]) *deletebufline()* Can also be used as a |method|: > GetBuffer()->deletebufline(1) - +< dictwatcheradd({dict}, {pattern}, {callback}) *dictwatcheradd()* Adds a watcher to a dictionary. A dictionary watcher is identified by three components: @@ -4360,6 +4386,9 @@ flatten({list} [, {maxdepth}]) *flatten()* :echo flatten([1, [2, [3, 4]], 5], 1) < [1, 2, [3, 4], 5] + Can also be used as a |method|: > + mylist->flatten() +< float2nr({expr}) *float2nr()* Convert {expr} to a Number by omitting the part after the decimal point. @@ -4697,7 +4726,9 @@ getbufinfo([{dict}]) < To get buffer-local options use: > getbufvar({bufnr}, '&option_name') - +< + Can also be used as a |method|: > + GetBufnr()->getbufinfo() < *getbufline()* getbufline({buf}, {lnum} [, {end}]) @@ -5199,6 +5230,9 @@ getmarklist([{buf}]) *getmarklist()* Refer to |getpos()| for getting information about a specific mark. + Can also be used as a |method|: > + GetBufnr()->getmarklist() + getmatches([{win}]) *getmatches()* Returns a |List| with all matches previously defined for the current window by |matchadd()| and the |:match| commands. @@ -5852,9 +5886,9 @@ histadd({history}, {item}) *histadd()* :let date=input("Enter date: ") < This function is not available in the |sandbox|. - Can also be used as a |method|, the base is used for the + Can also be used as a |method|, the base is passed as the second argument: > - GetPattern()->histadd('search') + GetHistory()->histadd('search') histdel({history} [, {item}]) *histdel()* Clear {history}, i.e. delete all its entries. See |hist-names| @@ -6499,8 +6533,8 @@ libcall({libname}, {funcname}, {argument}) Examples: > :echo libcall("libc.so", "getenv", "HOME") -< Can also be used as a |method|, where the base is passed as - the argument to the called function: > +< Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcall("libc.so", "getenv") < *libcallnr()* @@ -6512,8 +6546,8 @@ libcallnr({libname}, {funcname}, {argument}) :call libcallnr("libc.so", "printf", "Hello World!\n") :call libcallnr("libc.so", "sleep", 10) < - Can also be used as a |method|, where the base is passed as - the argument to the called function: > + Can also be used as a |method|, the base is passed as the + third argument: > GetValue()->libcallnr("libc.so", "printf") < line({expr} [, {winid}]) *line()* @@ -7037,7 +7071,7 @@ matchstrpos({expr}, {pat} [, {start} [, {count}]]) *matchstrpos()* Can also be used as a |method|: > GetText()->matchstrpos('word') - +< *max()* max({expr}) Return the maximum value of all items in {expr}. {expr} can be a |List| or a |Dictionary|. For a Dictionary, @@ -7587,6 +7621,9 @@ prompt_getprompt({buf}) *prompt_getprompt()* If the buffer doesn't exist or isn't a prompt buffer, an empty string is returned. + Can also be used as a |method|: > + GetBuffer()->prompt_getprompt() + prompt_setcallback({buf}, {expr}) *prompt_setcallback()* Set prompt callback for buffer {buf} to {expr}. When {expr} is an empty string the callback is removed. This has only @@ -8072,6 +8109,9 @@ screenattr({row}, {col}) *screenattr()* arbitrary number that can only be used to compare to the attribute at other positions. + Can also be used as a |method|: > + GetRow()->screenattr(col) + screenchar({row}, {col}) *screenchar()* The result is a Number, which is the character at position [row, col] on the screen. This works for every possible @@ -8082,6 +8122,9 @@ screenchar({row}, {col}) *screenchar()* This is mainly to be used for testing. Returns -1 when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenchar(col) + screenchars({row}, {col}) *screenchars()* The result is a List of Numbers. The first number is the same as what |screenchar()| returns. Further numbers are @@ -8089,6 +8132,9 @@ screenchars({row}, {col}) *screenchars()* This is mainly to be used for testing. Returns an empty List when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenchars(col) + screencol() *screencol()* The result is a Number, which is the current screen column of the cursor. The leftmost column has number 1. @@ -8124,6 +8170,9 @@ screenpos({winid}, {lnum}, {col}) *screenpos()* right position and use |screencol()| to get the value with |conceal| taken into account. + Can also be used as a |method|: > + GetWinid()->screenpos(lnum, col) + screenrow() *screenrow()* The result is a Number, which is the current screen row of the cursor. The top line has number one. @@ -8140,6 +8189,9 @@ screenstring({row}, {col}) *screenstring()* This is mainly to be used for testing. Returns an empty String when row or col is out of range. + Can also be used as a |method|: > + GetRow()->screenstring(col) + search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* Search for regexp pattern {pattern}. The search starts at the cursor position (you can use |cursor()| to set it). @@ -8230,6 +8282,8 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* without the 'e' flag if the cursor is on the "f" of "if". The 'n' flag tells the function not to move the cursor. + Can also be used as a |method|: > + GetPattern()->search() searchcount([{options}]) *searchcount()* Get or update the last search count, like what is displayed @@ -8350,7 +8404,9 @@ searchcount([{options}]) *searchcount()* value. see |cursor()|, |getpos() (default: cursor's position) - + Can also be used as a |method|: > + GetSearchOpts()->searchcount() +< searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()* Search for the declaration of {name}. @@ -8369,6 +8425,9 @@ searchdecl({name} [, {global} [, {thisblock}]]) *searchdecl()* echo getline('.') endif < + Can also be used as a |method|: > + GetName()->searchdecl() +< *searchpair()* searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeout}]]]]) @@ -8483,6 +8542,9 @@ searchpos({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *searchpos()* < In this example "submatch" is 2 when a lowercase letter is found |/\l|, 3 when an uppercase letter is found |/\u|. + Can also be used as a |method|: > + GetPattern()->searchpos() + server2client({clientid}, {string}) *server2client()* Send a reply string to {clientid}. The most recent {clientid} that sent a string can be retrieved with expand("<client>"). @@ -8494,6 +8556,9 @@ server2client({clientid}, {string}) *server2client()* See also |clientserver|. Example: > :echo server2client(expand("<client>"), "HELLO") + +< Can also be used as a |method|: > + GetClientId()->server2client(string) < serverlist() *serverlist()* Returns a list of server addresses, or empty if all servers @@ -8560,6 +8625,10 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* If {buf} is not a valid buffer or {lnum} is not valid, an error message is given. + Can also be used as a |method|, the base is passed as the + third argument: > + GetText()->setbufline(buf, lnum) + setbufvar({buf}, {varname}, {val}) *setbufvar()* Set option or local variable {varname} in buffer {buf} to {val}. @@ -8574,6 +8643,10 @@ setbufvar({buf}, {varname}, {val}) *setbufvar()* :call setbufvar("todo", "myvar", "foobar") < This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->setbufvar(buf, varname) + setcharsearch({dict}) *setcharsearch()* Set the current character search information to {dict}, which contains one or more of the following entries: @@ -8594,6 +8667,9 @@ setcharsearch({dict}) *setcharsearch()* :call setcharsearch(prevsearch) < Also see |getcharsearch()|. + Can also be used as a |method|: > + SavedSearch()->setcharsearch() + setcmdpos({pos}) *setcmdpos()* Set the cursor position in the command line to byte position {pos}. The first position is 1. @@ -8609,6 +8685,9 @@ setcmdpos({pos}) *setcmdpos()* Returns FALSE when successful, TRUE when not editing the command line. + Can also be used as a |method|: > + GetPos()->setcmdpos() + setenv({name}, {val}) *setenv()* Set environment variable {name} to {val}. Example: > call setenv('HOME', '/home/myhome') @@ -8616,6 +8695,10 @@ setenv({name}, {val}) *setenv()* < When {val} is |v:null| the environment variable is deleted. See also |expr-env|. + Can also be used as a |method|, the base is passed as the + second argument: > + GetPath()->setenv('PATH') + setfperm({fname}, {mode}) *setfperm()* *chmod* Set the file permissions for {fname} to {mode}. {mode} must be a string with 9 characters. It is of the form @@ -8661,7 +8744,11 @@ setline({lnum}, {text}) *setline()* < Note: The '[ and '] marks are not set. -setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* + Can also be used as a |method|, the base is passed as the + second argument: > + GetText()->setline(lnum) + +setloclist({nr}, {list} [, {action} [, {what}]]) *setloclist()* Create or replace or add to the location list for window {nr}. {nr} can be the window number or the |window-ID|. When {nr} is zero the current window is used. @@ -8677,6 +8764,10 @@ setloclist({nr}, {list}[, {action}[, {what}]]) *setloclist()* only the items listed in {what} are set. Refer to |setqflist()| for the list of supported keys in {what}. + Can also be used as a |method|, the base is passed as the + second argument: > + GetLoclist()->setloclist(winnr) + setmatches({list} [, {win}]) *setmatches()* Restores a list of matches saved by |getmatches() for the current window|. Returns 0 if successful, otherwise -1. All @@ -8685,6 +8776,9 @@ setmatches({list} [, {win}]) *setmatches()* If {win} is specified, use the window with this number or window ID instead of the current window. + Can also be used as a |method|: > + GetMatches()->setmatches() +< *setpos()* setpos({expr}, {list}) Set the position for String {expr}. Possible values: @@ -8734,8 +8828,10 @@ setpos({expr}, {list}) also set the preferred column. Also see the "curswant" key in |winrestview()|. + Can also be used as a |method|: > + GetPosition()->setpos('.') -setqflist({list} [, {action}[, {what}]]) *setqflist()* +setqflist({list} [, {action} [, {what}]]) *setqflist()* Create or replace or add to the quickfix list. If the optional {what} dictionary argument is supplied, then @@ -8844,7 +8940,10 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()* independent of the 'errorformat' setting. Use a command like `:cc 1` to jump to the first position. - + Can also be used as a |method|, the base is passed as the + second argument: > + GetErrorlist()->setqflist() +< *setreg()* setreg({regname}, {value} [, {options}]) Set the register {regname} to {value}. @@ -8901,6 +9000,10 @@ setreg({regname}, {value} [, {options}]) nothing: > :call setreg('a', '', 'al') +< Can also be used as a |method|, the base is passed as the + second argument: > + GetText()->setreg('a') + settabvar({tabnr}, {varname}, {val}) *settabvar()* Set tab-local variable {varname} to {val} in tab page {tabnr}. |t:var| @@ -8909,6 +9012,10 @@ settabvar({tabnr}, {varname}, {val}) *settabvar()* Tabs are numbered starting with one. This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->settabvar(tab, name) + settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* Set option or local variable {varname} in window {winnr} to {val}. @@ -8925,6 +9032,10 @@ settabwinvar({tabnr}, {winnr}, {varname}, {val}) *settabwinvar()* :call settabwinvar(3, 2, "myvar", "foobar") < This function is not available in the |sandbox|. + Can also be used as a |method|, the base is passed as the + fourth argument: > + GetValue()->settabwinvar(tab, winnr, name) + settagstack({nr}, {dict} [, {action}]) *settagstack()* Modify the tag stack of the window {nr} using {dict}. {nr} can be the window number or the |window-ID|. @@ -8958,6 +9069,9 @@ settagstack({nr}, {dict} [, {action}]) *settagstack()* call settagstack(1003, stack) unlet stack < + Can also be used as a |method|, the base is passed as the + second argument: > + GetStack()->settagstack(winnr) setwinvar({nr}, {varname}, {val}) *setwinvar()* Like |settabwinvar()| for the current tab page. @@ -8965,10 +9079,17 @@ setwinvar({nr}, {varname}, {val}) *setwinvar()* :call setwinvar(1, "&list", 0) :call setwinvar(2, "myvar", "foobar") +< Can also be used as a |method|, the base is passed as the + third argument: > + GetValue()->setwinvar(winnr, name) + sha256({string}) *sha256()* Returns a String with 64 hex characters, which is the SHA256 checksum of {string}. + Can also be used as a |method|: > + GetText()->sha256() + shellescape({string} [, {special}]) *shellescape()* Escape {string} for use as a shell command argument. @@ -9000,6 +9121,8 @@ shellescape({string} [, {special}]) *shellescape()* :call system("chmod +w -- " . shellescape(expand("%"))) < See also |::S|. + Can also be used as a |method|: > + GetCommand()->shellescape() shiftwidth([{col}]) *shiftwidth()* Returns the effective value of 'shiftwidth'. This is the @@ -9022,6 +9145,9 @@ shiftwidth([{col}]) *shiftwidth()* 'vartabstop' feature. If no {col} argument is given, column 1 will be assumed. + Can also be used as a |method|: > + GetColumn()->shiftwidth() + sign_ functions are documented here: |sign-functions-details| simplify({filename}) *simplify()* @@ -9041,6 +9167,8 @@ simplify({filename}) *simplify()* directory. In order to resolve all the involved symbolic links before simplifying the path name, use |resolve()|. + Can also be used as a |method|: > + GetName()->simplify() sin({expr}) *sin()* Return the sine of {expr}, measured in radians, as a |Float|. @@ -9172,6 +9300,9 @@ soundfold({word}) This can be used for making spelling suggestions. Note that the method can be quite slow. + Can also be used as a |method|: > + GetWord()->soundfold() +< *spellbadword()* spellbadword([{sentence}]) Without argument: The result is the badly spelled word under @@ -9197,6 +9328,9 @@ spellbadword([{sentence}]) The spelling information for the current window and the value of 'spelllang' are used. + Can also be used as a |method|: > + GetText()->spellbadword() +< *spellsuggest()* spellsuggest({word} [, {max} [, {capital}]]) Return a |List| with spelling suggestions to replace {word}. @@ -9219,6 +9353,8 @@ spellsuggest({word} [, {max} [, {capital}]]) The spelling information for the current window is used. The values of 'spelllang' and 'spellsuggest' are used. + Can also be used as a |method|: > + GetWord()->spellsuggest() split({string} [, {pattern} [, {keepempty}]]) *split()* Make a |List| out of {string}. When {pattern} is omitted or @@ -9342,7 +9478,7 @@ str2nr({string} [, {base}]) *str2nr()* When {base} is omitted base 10 is used. This also means that a leading zero doesn't cause octal conversion to be used, as with the default String to Number conversion. Example: > - let nr = str2nr('123') + let nr = str2nr('0123') < When {base} is 16 a leading "0x" or "0X" is ignored. With a different base the result will be zero. Similarly, when @@ -9350,6 +9486,20 @@ str2nr({string} [, {base}]) *str2nr()* {base} is 2 a leading "0b" or "0B" is ignored. Text after the number is silently ignored. + Can also be used as a |method|: > + GetText()->str2nr() + +strcharpart({src}, {start} [, {len}]) *strcharpart()* + Like |strpart()| but using character index and length instead + of byte index and length. Composing characters are counted + separately. + When a character index is used where a character does not + exist it is assumed to be one character. For example: > + strcharpart('abc', -1, 2) +< results in 'a'. + + Can also be used as a |method|: > + GetText()->strcharpart(5) strchars({string} [, {skipcc}]) *strchars()* The result is a Number, which is the number of characters @@ -9375,14 +9525,8 @@ strchars({string} [, {skipcc}]) *strchars()* endfunction endif < -strcharpart({src}, {start} [, {len}]) *strcharpart()* - Like |strpart()| but using character index and length instead - of byte index and length. Composing characters are counted - separately. - When a character index is used where a character does not - exist it is assumed to be one character. For example: > - strcharpart('abc', -1, 2) -< results in 'a'. + Can also be used as a |method|: > + GetText()->strchars() strdisplaywidth({string} [, {col}]) *strdisplaywidth()* The result is a Number, which is the number of display cells @@ -9397,6 +9541,9 @@ strdisplaywidth({string} [, {col}]) *strdisplaywidth()* Ambiguous, this function's return value depends on 'ambiwidth'. Also see |strlen()|, |strwidth()| and |strchars()|. + Can also be used as a |method|: > + GetText()->strdisplaywidth() + strftime({format} [, {time}]) *strftime()* The result is a String, which is a formatted date and time, as specified by the {format} string. The given {time} is used, @@ -9414,12 +9561,18 @@ strftime({format} [, {time}]) *strftime()* :echo strftime("%c", getftime("file.c")) Show mod time of file.c. +< Can also be used as a |method|: > + GetFormat()->strftime() + strgetchar({str}, {index}) *strgetchar()* Get character {index} from {str}. This uses a character index, not a byte index. Composing characters are considered separate characters here. Also see |strcharpart()| and |strchars()|. + Can also be used as a |method|: > + GetText()->strgetchar(5) + stridx({haystack}, {needle} [, {start}]) *stridx()* The result is a Number, which gives the byte index in {haystack} of the first occurrence of the String {needle}. @@ -9439,6 +9592,9 @@ stridx({haystack}, {needle} [, {start}]) *stridx()* stridx() works similar to the C function strstr(). When used with a single character it works similar to strchr(). + Can also be used as a |method|: > + GetHaystack()->stridx(needle) + *string()* string({expr}) Return {expr} converted to a String. If {expr} is a Number, Float, String, Blob or a composition of them, then the result @@ -9501,6 +9657,9 @@ strpart({src}, {start} [, {len} [, {chars}]]) *strpart()* example, to get the character under the cursor: > strpart(getline("."), col(".") - 1, 1, v:true) < + Can also be used as a |method|: > + GetText()->strpart(5) + strptime({format}, {timestring}) *strptime()* The result is a Number, which is a unix timestamp representing the date and time in {timestring}, which is expected to match @@ -9525,7 +9684,9 @@ strptime({format}, {timestring}) *strptime()* :echo strftime("%c", strptime("%Y%m%d%H%M%S", "19970427115355") + 3600) < Sun Apr 27 12:53:55 1997 - + Can also be used as a |method|: > + GetFormat()->strptime(timestring) +< strridx({haystack}, {needle} [, {start}]) *strridx()* The result is a Number, which gives the byte index in {haystack} of the last occurrence of the String {needle}. @@ -9544,6 +9705,9 @@ strridx({haystack}, {needle} [, {start}]) *strridx()* When used with a single character it works similar to the C function strrchr(). + Can also be used as a |method|: > + GetHaystack()->strridx(needle) + strtrans({string}) *strtrans()* The result is a String, which is {string} with all unprintable characters translated into printable characters |'isprint'|. @@ -9592,6 +9756,9 @@ submatch({nr} [, {list}]) *submatch()* *E935* < This finds the first number in the line and adds one to it. A line break is included as a newline character. + Can also be used as a |method|: > + GetNr()->submatch() + substitute({string}, {pat}, {sub}, {flags}) *substitute()* The result is a String, which is a copy of {string}, in which the first match of {pat} is replaced with {sub}. @@ -9654,6 +9821,9 @@ swapinfo({fname}) *swapinfo()* Not a swap file: does not contain correct block ID Magic number mismatch: Info in first block is invalid + Can also be used as a |method|: > + GetFilename()->swapinfo() + swapname({buf}) *swapname()* The result is the swap file path of the buffer {buf}. For the use of {buf}, see |bufname()| above. @@ -9661,6 +9831,9 @@ swapname({buf}) *swapname()* |:swapname| (unless there is no swap file). If buffer {buf} has no swap file, returns an empty string. + Can also be used as a |method|: > + GetBufname()->swapname() + synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position {lnum} and {col} in the current window. @@ -9857,6 +10030,8 @@ tabpagebuflist([{arg}]) *tabpagebuflist()* endfor < Note that a buffer may appear in more than one window. + Can also be used as a |method|: > + GetTabpage()->tabpagebuflist() tabpagenr([{arg}]) *tabpagenr()* The result is a Number, which is the number of the current @@ -9883,6 +10058,9 @@ tabpagewinnr({tabarg} [, {arg}]) *tabpagewinnr()* tabpagewinnr(4, '$') " number of windows in tab page 4 < When {tabarg} is invalid zero is returned. + Can also be used as a |method|: > + GetTabpage()->tabpagewinnr() +< *tagfiles()* tagfiles() Returns a |List| with the file names used to search for tags for the current buffer. This is the 'tags' option expanded. @@ -9931,6 +10109,9 @@ taglist({expr} [, {filename}]) *taglist()* located by Vim. Refer to |tags-file-format| for the format of the tags file generated by the different ctags tools. + Can also be used as a |method|: > + GetTagpattern()->taglist() + tempname() *tempname()* *temp-file-name* The result is a String, which is the name of a file that doesn't exist. It can be used for a temporary file. Example: > @@ -9998,6 +10179,9 @@ timer_info([{id}]) -1 means forever "callback" the callback + Can also be used as a |method|: > + GetTimer()->timer_info() +< timer_pause({timer}, {paused}) *timer_pause()* Pause or unpause a timer. A paused timer does not invoke its callback when its time expires. Unpausing a timer may cause @@ -10011,6 +10195,9 @@ timer_pause({timer}, {paused}) *timer_pause()* String, then the timer is paused, otherwise it is unpaused. See |non-zero-arg|. + Can also be used as a |method|: > + GetTimer()->timer_pause(1) +< *timer_start()* *timer* *timers* timer_start({time}, {callback} [, {options}]) Create a timer and return the timer ID. @@ -10038,11 +10225,19 @@ timer_start({time}, {callback} [, {options}]) \ {'repeat': 3}) < This invokes MyHandler() three times at 500 msec intervals. + Can also be used as a |method|: > + GetMsec()->timer_start(callback) + +< Not available in the |sandbox|. + timer_stop({timer}) *timer_stop()* Stop a timer. The timer callback will no longer be invoked. {timer} is an ID returned by timer_start(), thus it must be a Number. If {timer} does not exist there is no error. + Can also be used as a |method|: > + GetTimer()->timer_stop() +< timer_stopall() *timer_stopall()* Stop all timers. The timer callbacks will no longer be invoked. Useful if some timers is misbehaving. If there are @@ -10053,11 +10248,17 @@ tolower({expr}) *tolower()* characters turned into lowercase (just like applying |gu| to the string). + Can also be used as a |method|: > + GetText()->tolower() + toupper({expr}) *toupper()* The result is a copy of the String given, with all lowercase characters turned into uppercase (just like applying |gU| to the string). + Can also be used as a |method|: > + GetText()->toupper() + tr({src}, {fromstr}, {tostr}) *tr()* The result is a copy of the {src} string with all characters which appear in {fromstr} replaced by the character in that @@ -10072,6 +10273,9 @@ tr({src}, {fromstr}, {tostr}) *tr()* echo tr("<blob>", "<>", "{}") < returns "{blob}" + Can also be used as a |method|: > + GetText()->tr(from, to) + trim({text} [, {mask} [, {dir}]]) *trim()* Return {text} as a String where any character in {mask} is removed from the beginning and/or end of {text}. @@ -10095,6 +10299,9 @@ trim({text} [, {mask} [, {dir}]]) *trim()* echo trim(" vim ", " ", 2) < returns " vim" + Can also be used as a |method|: > + GetText()->trim() + trunc({expr}) *trunc()* Return the largest integral value with magnitude less than or equal to {expr} as a |Float| (truncate towards zero). @@ -10151,6 +10358,9 @@ undofile({name}) *undofile()* buffer without a file name will not write an undo file. Useful in combination with |:wundo| and |:rundo|. + Can also be used as a |method|: > + GetFilename()->undofile() + undotree() *undotree()* Return the current state of the undo tree in a dictionary with the following items: @@ -10249,8 +10459,10 @@ virtcol({expr}) *virtcol()* all lines: > echo max(map(range(1, line('$')), "virtcol([v:val, '$'])")) +< Can also be used as a |method|: > + GetPos()->virtcol() -visualmode([expr]) *visualmode()* +visualmode([{expr}]) *visualmode()* The result is a String, which describes the last Visual mode used in the current buffer. Initially it returns an empty string, but once Visual mode has been used, it returns "v", @@ -10264,7 +10476,7 @@ visualmode([expr]) *visualmode()* Visual mode that was used. If Visual mode is active, use |mode()| to get the Visual mode (e.g., in a |:vmap|). - If [expr] is supplied and it evaluates to a non-zero Number or + If {expr} is supplied and it evaluates to a non-zero Number or a non-empty String, then the Visual mode will be cleared and the old value is returned. See |non-zero-arg|. @@ -10303,11 +10515,18 @@ win_execute({id}, {command} [, {silent}]) *win_execute()* have unexpected side effects. Use |:noautocmd| if needed. Example: > call win_execute(winid, 'syntax enable') +< + Can also be used as a |method|, the base is passed as the + second argument: > + GetCommand()->win_execute(winid) win_findbuf({bufnr}) *win_findbuf()* Returns a |List| with |window-ID|s for windows that contain buffer {bufnr}. When there is none the list is empty. + Can also be used as a |method|: > + GetBufnr()->win_findbuf() + win_getid([{win} [, {tab}]]) *win_getid()* Get the |window-ID| for the specified window. When {win} is missing use the current window. @@ -10317,6 +10536,9 @@ win_getid([{win} [, {tab}]]) *win_getid()* number {tab}. The first tab has number one. Return zero if the window cannot be found. + Can also be used as a |method|: > + GetWinnr()->win_getid() + win_gettype([{nr}]) *win_gettype()* Return the type of the window: "autocmd" autocommand window. Temporary window @@ -10337,20 +10559,32 @@ win_gettype([{nr}]) *win_gettype()* popup window then 'buftype' is "terminal" and win_gettype() returns "popup". + Can also be used as a |method|: > + GetWinid()->win_gettype() +< win_gotoid({expr}) *win_gotoid()* Go to window with ID {expr}. This may also change the current tabpage. Return TRUE if successful, FALSE if the window cannot be found. -win_id2tabwin({expr} *win_id2tabwin()* + Can also be used as a |method|: > + GetWinid()->win_gotoid() + +win_id2tabwin({expr}) *win_id2tabwin()* Return a list with the tab number and window number of window with ID {expr}: [tabnr, winnr]. Return [0, 0] if the window cannot be found. + Can also be used as a |method|: > + GetWinid()->win_id2tabwin() + win_id2win({expr}) *win_id2win()* Return the window number of window with ID {expr}. Return 0 if the window cannot be found in the current tabpage. + Can also be used as a |method|: > + GetWinid()->win_id2win() + win_screenpos({nr}) *win_screenpos()* Return the screen position of window {nr} as a list with two numbers: [row, col]. The first window always has position @@ -10360,6 +10594,9 @@ win_screenpos({nr}) *win_screenpos()* Returns [0, 0] if the window cannot be found in the current tabpage. + Can also be used as a |method|: > + GetWinid()->win_screenpos() +< win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* Move the window {nr} to a new split of the window {target}. This is similar to moving to {target}, creating a new window @@ -10380,6 +10617,9 @@ win_splitmove({nr}, {target} [, {options}]) *win_splitmove()* present, the values of 'splitbelow' and 'splitright' are used. + Can also be used as a |method|: > + GetWinid()->win_splitmove(target) +< *winbufnr()* winbufnr({nr}) The result is a Number, which is the number of the buffer associated with window {nr}. {nr} can be the window number or @@ -10414,6 +10654,9 @@ winheight({nr}) *winheight()* This excludes any window toolbar line. Examples: > :echo "The current window has " . winheight(0) . " lines." + +< Can also be used as a |method|: > + GetWinid()->winheight() < winlayout([{tabnr}]) *winlayout()* The result is a nested List containing the layout of windows @@ -10445,6 +10688,9 @@ winlayout([{tabnr}]) *winlayout()* ['col', [['leaf', 1002], ['row', [['leaf', 1003], ['leaf', 1001]]], ['leaf', 1000]]] < + Can also be used as a |method|: > + GetTabnr()->winlayout() +< *winline()* winline() The result is a Number, which is the screen line of the cursor in the window. This is counting screen lines from the top of @@ -10479,6 +10725,9 @@ winnr([{arg}]) The result is a Number, which is the number of the current let window_count = winnr('$') let prev_window = winnr('#') let wnum = winnr('3k') + +< Can also be used as a |method|: > + GetWinval()->winnr() < *winrestcmd()* winrestcmd() Returns a sequence of |:resize| commands that should restore @@ -10507,6 +10756,9 @@ winrestview({dict}) If you have changed the values the result is unpredictable. If the window size changed the result won't be the same. + Can also be used as a |method|: > + GetView()->winrestview() +< *winsaveview()* winsaveview() Returns a |Dictionary| that contains information to restore the view of the current window. Use |winrestview()| to @@ -10545,6 +10797,8 @@ winwidth({nr}) *winwidth()* < For getting the terminal or screen size, see the 'columns' option. + Can also be used as a |method|: > + GetWinid()->winwidth() wordcount() *wordcount()* The result is a dictionary of byte/chars/word statistics for @@ -10603,17 +10857,19 @@ writefile({object}, {fname} [, {flags}]) :let fl = readfile("foo", "b") :call writefile(fl, "foocopy", "b") +< Can also be used as a |method|: > + GetText()->writefile("thefile") xor({expr}, {expr}) *xor()* Bitwise XOR on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. Example: > :let bits = xor(bits, 0x80) -< Can also be used as a |method|: > +< + Can also be used as a |method|: > :let bits = bits->xor(0x80) < - *string-match* Matching a pattern in a String diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index 6416f49061..173d3c0cdf 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -93,6 +93,7 @@ REFERENCE MANUAL: These files explain every detail of Vim. *reference_toc* General subjects ~ |intro.txt| general introduction to Vim; notation used in help files +|nvim.txt| Transitioning from Vim |help.txt| overview and quick reference (this file) |helphelp.txt| about using the help files |index.txt| alphabetical index of all commands @@ -133,17 +134,19 @@ Advanced editing ~ |api.txt| Nvim API via RPC, Lua and VimL Special issues ~ -|testing.txt| testing Vim and Vim scripts -|print.txt| printing -|remote.txt| using Vim as a server or client +|testing.txt| testing Vim and Vim scripts +|print.txt| printing +|remote_plugin.txt| Nvim support for remote plugins Programming language support ~ |indent.txt| automatic indenting for C and other languages |lsp.txt| Language Server Protocol (LSP) |treesitter.txt| tree-sitter library for incremental parsing of buffers +|diagnostic.txt| Diagnostic framework |syntax.txt| syntax highlighting |filetype.txt| settings done specifically for a type of file |quickfix.txt| commands for a quick edit-compile-fix cycle +|provider.txt| Built-in remote plugin hosts |ft_ada.txt| Ada (the programming language) support |ft_ps1.txt| Filetype plugin for Windows PowerShell |ft_raku.txt| Filetype plugin for Raku @@ -164,6 +167,7 @@ GUI ~ Interfaces ~ |if_cscop.txt| using Cscope with Vim +|if_perl.txt| Perl interface |if_pyth.txt| Python interface |if_ruby.txt| Ruby interface |sign.txt| debugging signs @@ -171,6 +175,16 @@ Interfaces ~ Versions ~ |vim_diff.txt| Main differences between Nvim and Vim |vi_diff.txt| Main differences between Vim and Vi +|deprecated.txt| Deprecated items that have been or will be removed + +Other ~ +|terminal_emulator.txt| Terminal buffers +|term.txt| Terminal UI +|ui.txt| Nvim UI protocol +|channel.txt| Nvim asynchronous IO +|dev_style.txt| Nvim style guide +|job_control.txt| Spawn and control multiple processes + *standard-plugin-list* Standard plugins ~ |matchit.txt| Extended |%| matching diff --git a/runtime/doc/if_lua.txt b/runtime/doc/if_lua.txt deleted file mode 100644 index 34bcf0f039..0000000000 --- a/runtime/doc/if_lua.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - NVIM REFERENCE MANUAL - -Moved to |lua.txt| - -============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 9bd304cbb4..11f9d62e69 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1909,14 +1909,14 @@ make_client_capabilities() *vim.lsp.protocol.resolve_capabilities()* resolve_capabilities({server_capabilities}) - `*` to match one or more characters in a path segment `?` to - match on one character in a path segment `**` to match any - number of path segments, including none `{}` to group - conditions (e.g. `**/*.{ts,js}` matches all TypeScript and - JavaScript files) `[]` to declare a range of characters to - match in a path segment (e.g., `example.[0-9]` to match on - `example.0` , `example.1` , …) `[!...]` to negate a range of - characters to match in a path segment (e.g., `example.[!0-9]` - to match on `example.a` , `example.b` , but not `example.0` ) + Creates a normalized object describing LSP server + capabilities. + + Parameters: ~ + {server_capabilities} table Table of capabilities + supported by the server + + Return: ~ + table Normalized table of capabilities vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt deleted file mode 100644 index 5368cf0f4f..0000000000 --- a/runtime/doc/msgpack_rpc.txt +++ /dev/null @@ -1,8 +0,0 @@ - - - NVIM REFERENCE MANUAL - -This document was merged into |api.txt| and |develop.txt|. - -============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 038808b760..47633c750c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -684,9 +684,12 @@ A jump table for the options with a short description can be found at |Q_op|. 'autowrite' 'aw' boolean (default off) global Write the contents of the file, if it has been modified, on each - :next, :rewind, :last, :first, :previous, :stop, :suspend, :tag, :!, - :make, CTRL-] and CTRL-^ command; and when a :buffer, CTRL-O, CTRL-I, - '{A-Z0-9}, or `{A-Z0-9} command takes one to another file. + `:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`, + `:suspend`, `:tag`, `:!`, `:make`, CTRL-] and CTRL-^ command; and when + a :buffer, CTRL-O, CTRL-I, '{A-Z0-9}, or `{A-Z0-9} command takes one + to another file. + A buffer is not written if it becomes hidden, e.g. when 'bufhidden' is + set to "hide" and `:next` is used Note that for some commands the 'autowrite' option is not used, see 'autowriteall' for that. Some buffers will not be written, specifically when 'buftype' is diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 9c1f584415..ba1da209f7 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -837,9 +837,9 @@ lists. They set one of the existing error lists as the current one. *:chistory* *:chi* :[count]chi[story] Show the list of error lists. The current list is marked with ">". The output looks like: - error list 1 of 3; 43 errors ~ - > error list 2 of 3; 0 errors ~ - error list 3 of 3; 15 errors ~ + error list 1 of 3; 43 errors :make ~ + > error list 2 of 3; 0 errors :helpgrep tag ~ + error list 3 of 3; 15 errors :grep ex_help *.c ~ When [count] is given, then the count'th quickfix list is made the current list. Example: > diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 5079d900c9..895ee5ebef 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -404,6 +404,9 @@ sign_define({list}) \ 'text' : '!!'} \ ]) < + Can also be used as a |method|: > + GetSignList()->sign_define() + sign_getdefined([{name}]) *sign_getdefined()* Get a list of defined signs and their attributes. This is similar to the |:sign-list| command. @@ -416,11 +419,12 @@ sign_getdefined([{name}]) *sign_getdefined()* following entries: icon full path to the bitmap file of the sign linehl highlight group used for the whole line the - sign is placed in. + sign is placed in; not present if not set. name name of the sign text text that is displayed when there is no icon or the GUI is not being used. - texthl highlight group used for the text item + texthl highlight group used for the text item; not + present if not set. numhl highlight group used for 'number' column at the associated line. Overrides |hl-LineNr|, |hl-CursorLineNr|. @@ -435,6 +439,9 @@ sign_getdefined([{name}]) *sign_getdefined()* " Get the attribute of the sign named mySign echo sign_getdefined("mySign") < + Can also be used as a |method|: > + GetSignList()->sign_getdefined() + sign_getplaced([{buf} [, {dict}]]) *sign_getplaced()* Return a list of signs placed in a buffer or all the buffers. This is similar to the |:sign-place-list| command. @@ -495,6 +502,9 @@ sign_getplaced([{buf} [, {dict}]]) *sign_getplaced()* " Get a List of all the placed signs echo sign_getplaced() < + Can also be used as a |method|: > + GetBufname()->sign_getplaced() +< *sign_jump()* sign_jump({id}, {group}, {buf}) Open the buffer {buf} or jump to the window that contains @@ -510,7 +520,9 @@ sign_jump({id}, {group}, {buf}) " Jump to sign 10 in the current buffer call sign_jump(10, '', '') < - + Can also be used as a |method|: > + GetSignid()->sign_jump() +< *sign_place()* sign_place({id}, {group}, {name}, {buf} [, {dict}]) Place the sign defined as {name} at line {lnum} in file or @@ -560,7 +572,9 @@ sign_place({id}, {group}, {name}, {buf} [, {dict}]) call sign_place(10, 'g3', 'sign4', 'json.c', \ {'lnum' : 40, 'priority' : 90}) < - + Can also be used as a |method|: > + GetSignid()->sign_place(group, name, expr) +< *sign_placelist()* sign_placelist({list}) Place one or more signs. This is similar to the @@ -620,6 +634,8 @@ sign_placelist({list}) \ 'lnum' : 50} \ ]) < + Can also be used as a |method|: > + GetSignlist()->sign_placelist() sign_undefine([{name}]) *sign_undefine()* sign_undefine({list}) @@ -644,6 +660,8 @@ sign_undefine({list}) " Delete all the signs call sign_undefine() < + Can also be used as a |method|: > + GetSignlist()->sign_undefine() sign_unplace({group} [, {dict}]) *sign_unplace()* Remove a previously placed sign in one or more buffers. This @@ -686,6 +704,9 @@ sign_unplace({group} [, {dict}]) *sign_unplace()* " Remove all the placed signs from all the buffers call sign_unplace('*') + +< Can also be used as a |method|: > + GetSigngroup()->sign_unplace() < sign_unplacelist({list}) *sign_unplacelist()* Remove previously placed signs from one or more buffers. This @@ -715,5 +736,8 @@ sign_unplacelist({list}) *sign_unplacelist()* \ {'id' : 20, 'buffer' : 'b.vim'}, \ ]) < + Can also be used as a |method|: > + GetSignlist()->sign_unplacelist() +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 72f08c37ca..b57b8bfd5c 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -3161,6 +3161,14 @@ buffer by buffer basis. For more detailed instructions see |ft_sql.txt|. +SQUIRREL *squirrel.vim* *ft-squirrel-syntax* + +Squirrel is a high level imperative, object-oriented programming language, +designed to be a light-weight scripting language that fits in the size, memory +bandwidth, and real-time requirements of applications like video games. Files +with the following extensions are recognized as squirrel files: .nut. + + TCSH *tcsh.vim* *ft-tcsh-syntax* This covers the shell named "tcsh". It is a superset of csh. See |csh.vim| diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index f0bda5aaf8..8ec66d26a4 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -157,6 +157,9 @@ assert_nobeep({cmd}) *assert_nobeep()* produces a beep or visual bell. Also see |assert_beeps()|. + Can also be used as a |method|: > + GetCmd()->assert_nobeep() +< *assert_notequal()* assert_notequal({expected}, {actual} [, {msg}]) The opposite of `assert_equal()`: add an error message to diff --git a/runtime/filetype.vim b/runtime/filetype.vim index f0285c09d0..3e57ae7f0f 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Nov 16 +" Last Change: 2021 Dec 03 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -1646,6 +1646,9 @@ au BufNewFile,BufRead .tcshrc,*.tcsh,tcsh.tcshrc,tcsh.login call dist#ft#SetFile " (patterns ending in a start further below) au BufNewFile,BufRead .login,.cshrc,csh.cshrc,csh.login,csh.logout,*.csh,.alias call dist#ft#CSH() +" Zig +au BufNewFile,BufRead *.zig setf zig + " Z-Shell script (patterns ending in a star further below) au BufNewFile,BufRead .zprofile,*/etc/zprofile,.zfbfmarks setf zsh au BufNewFile,BufRead .zshrc,.zshenv,.zlogin,.zlogout,.zcompdump setf zsh diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index a98c75e541..7c03ff2873 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2021 Nov 03 +" Last Change: 2021 Nov 27 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -12,6 +12,7 @@ let b:did_indent = 1 setlocal indentexpr=GetVimIndent() setlocal indentkeys+==end,=},=else,=cat,=finall,=END,0\\,0=\"\\\ setlocal indentkeys-=0# +setlocal indentkeys-=: let b:undo_indent = "setl indentkeys< indentexpr<" diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 280222e28a..522e26caa7 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -16,10 +16,6 @@ for _, v in pairs(a.nvim_get_all_options_info()) do if v.shortname ~= "" then options_info[v.shortname] = v end end -local is_global_option = function(info) return info.scope == "global" end -local is_buffer_option = function(info) return info.scope == "buf" end -local is_window_option = function(info) return info.scope == "win" end - local get_scoped_options = function(scope) local result = {} for name, option_info in pairs(options_info) do diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 708ee1727a..59cb9f8c5b 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -520,8 +520,8 @@ local function diagnostic_move_pos(opts, pos) local float_opts = type(float) == "table" and float or {} vim.schedule(function() M.open_float( - vim.api.nvim_win_get_buf(win_id), vim.tbl_extend("keep", float_opts, { + bufnr = vim.api.nvim_win_get_buf(win_id), scope = "cursor", focus = false, }) @@ -658,7 +658,10 @@ function M.set(namespace, bufnr, diagnostics, opts) vim.api.nvim_buf_call(bufnr, function() vim.api.nvim_command( - string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr)) + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) ) end) end @@ -1132,12 +1135,15 @@ end --- Show diagnostics in a floating window. --- ----@param bufnr number|nil Buffer number. Defaults to the current buffer. ---@param opts table|nil Configuration table with the same keys as --- |vim.lsp.util.open_floating_preview()| in addition to the following: +--- - bufnr: (number) Buffer number to show diagnostics from. +--- Defaults to the current buffer. --- - namespace: (number) Limit diagnostics to the given namespace --- - scope: (string, default "line") Show diagnostics from the whole buffer ("buffer"), --- the current cursor line ("line"), or the current cursor position ("cursor"). +--- Shorthand versions are also accepted ("c" for "cursor", "l" for "line", "b" +--- for "buffer"). --- - pos: (number or table) If {scope} is "line" or "cursor", use this position rather --- than the cursor position. If a number, interpreted as a line number; --- otherwise, a (row, col) tuple. @@ -1166,15 +1172,21 @@ end --- highlight. --- Overrides the setting from |vim.diagnostic.config()|. ---@return tuple ({float_bufnr}, {win_id}) -function M.open_float(bufnr, opts) - vim.validate { - bufnr = { bufnr, 'n', true }, - opts = { opts, 't', true }, - } +function M.open_float(opts, ...) + -- Support old (bufnr, opts) signature + local bufnr + if opts == nil or type(opts) == "number" then + bufnr = opts + opts = ... + else + vim.validate { + opts = { opts, 't', true }, + } + end opts = opts or {} - bufnr = get_bufnr(bufnr) - local scope = opts.scope or "line" + bufnr = get_bufnr(bufnr or opts.bufnr) + local scope = ({l = "line", c = "cursor", b = "buffer"})[opts.scope] or opts.scope or "line" local lnum, col if scope == "line" or scope == "cursor" then if not opts.pos then @@ -1332,9 +1344,14 @@ function M.reset(namespace, bufnr) end end - vim.api.nvim_command( - string.format("doautocmd <nomodeline> DiagnosticChanged %s", vim.api.nvim_buf_get_name(bufnr)) - ) + vim.api.nvim_buf_call(bufnr, function() + vim.api.nvim_command( + string.format( + "doautocmd <nomodeline> DiagnosticChanged %s", + vim.fn.fnameescape(vim.api.nvim_buf_get_name(bufnr)) + ) + ) + end) end --- Add all diagnostics to the quickfix list. diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 61a700bd15..95bc4635b0 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -489,7 +489,8 @@ local function text_document_did_open_handler(bufnr, client) -- Protect against a race where the buffer disappears -- between `did_open_handler` and the scheduled function firing. if vim.api.nvim_buf_is_valid(bufnr) then - vim.lsp.diagnostic.redraw(bufnr, client.id) + local namespace = vim.lsp.diagnostic.get_namespace(client.id) + vim.diagnostic.show(namespace, bufnr) end end) end @@ -559,6 +560,12 @@ end --- --- - {handlers} (table): The handlers used by the client as described in |lsp-handler|. --- +--- - {requests} (table): The current pending requests in flight +--- to the server. Entries are key-value pairs with the key +--- being the request ID while the value is a table with `type`, +--- `bufnr`, and `method` key-value pairs. `type` is either "pending" +--- for an active request, or "cancel" for a cancel request. +--- --- - {config} (table): copy of the table that was passed by the user --- to |vim.lsp.start_client()|. --- diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index f02ebfb9dc..8e3ed9b002 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -116,31 +116,30 @@ end --- asks the user to select one. -- ---@returns The client that the user selected or nil -local function select_client(method) - local clients = vim.tbl_values(vim.lsp.buf_get_clients()); - clients = vim.tbl_filter(function (client) +local function select_client(method, on_choice) + validate { + on_choice = { on_choice, 'function', false }, + } + local clients = vim.tbl_values(vim.lsp.buf_get_clients()) + clients = vim.tbl_filter(function(client) return client.supports_method(method) end, clients) -- better UX when choices are always in the same order (between restarts) - table.sort(clients, function (a, b) return a.name < b.name end) + table.sort(clients, function(a, b) + return a.name < b.name + end) if #clients > 1 then - local choices = {} - for k,v in pairs(clients) do - table.insert(choices, string.format("%d %s", k, v.name)) - end - local user_choice = vim.fn.confirm( - "Select a language server:", - table.concat(choices, "\n"), - 0, - "Question" - ) - if user_choice == 0 then return nil end - return clients[user_choice] + vim.ui.select(clients, { + prompt = 'Select a language server:', + format_item = function(client) + return client.name + end, + }, on_choice) elseif #clients < 1 then - return nil + on_choice(nil) else - return clients[1] + on_choice(clients[1]) end end @@ -152,11 +151,15 @@ end -- ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting function M.formatting(options) - local client = select_client("textDocument/formatting") - if client == nil then return end - local params = util.make_formatting_params(options) - return client.request("textDocument/formatting", params, nil, vim.api.nvim_get_current_buf()) + local bufnr = vim.api.nvim_get_current_buf() + select_client('textDocument/formatting', function(client) + if client == nil then + return + end + + return client.request('textDocument/formatting', params, nil, bufnr) + end) end --- Performs |vim.lsp.buf.formatting()| synchronously. @@ -172,17 +175,20 @@ end ---@param timeout_ms (number) Request timeout ---@see |vim.lsp.buf.formatting_seq_sync| function M.formatting_sync(options, timeout_ms) - local client = select_client("textDocument/formatting") - if client == nil then return end - local params = util.make_formatting_params(options) local bufnr = vim.api.nvim_get_current_buf() - local result, err = client.request_sync("textDocument/formatting", params, timeout_ms, bufnr) - if result and result.result then - util.apply_text_edits(result.result, bufnr) - elseif err then - vim.notify("vim.lsp.buf.formatting_sync: " .. err, vim.log.levels.WARN) - end + select_client('textDocument/formatting', function(client) + if client == nil then + return + end + + local result, err = client.request_sync('textDocument/formatting', params, timeout_ms, bufnr) + if result and result.result then + util.apply_text_edits(result.result, bufnr) + elseif err then + vim.notify('vim.lsp.buf.formatting_sync: ' .. err, vim.log.levels.WARN) + end + end) end --- Formats the current buffer by sequentially requesting formatting from attached clients. @@ -238,12 +244,15 @@ end ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_formatting(options, start_pos, end_pos) - local client = select_client("textDocument/rangeFormatting") - if client == nil then return end - local params = util.make_given_range_params(start_pos, end_pos) params.options = util.make_formatting_params(options).options - return client.request("textDocument/rangeFormatting", params) + select_client('textDocument/rangeFormatting', function(client) + if client == nil then + return + end + + return client.request('textDocument/rangeFormatting', params) + end) end --- Renames all references to the symbol under the cursor. diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 075da41b23..8850d25233 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -153,19 +153,6 @@ function M.get_namespace(client_id) return _client_namespaces[client_id] end ---- Save diagnostics to the current buffer. ---- ---- Handles saving diagnostics from multiple clients in the same buffer. ----@param diagnostics Diagnostic[] ----@param bufnr number ----@param client_id number ----@private -function M.save(diagnostics, bufnr, client_id) - local namespace = M.get_namespace(client_id) - vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) -end --- }}} - --- |lsp-handler| for the method "textDocument/publishDiagnostics" --- --- See |vim.diagnostic.config()| for configuration options. Handler-specific @@ -245,6 +232,23 @@ end -- Deprecated Functions {{{ + +--- Save diagnostics to the current buffer. +--- +---@deprecated Prefer |vim.diagnostic.set()| +--- +--- Handles saving diagnostics from multiple clients in the same buffer. +---@param diagnostics Diagnostic[] +---@param bufnr number +---@param client_id number +---@private +function M.save(diagnostics, bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.save is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) + local namespace = M.get_namespace(client_id) + vim.diagnostic.set(namespace, bufnr, diagnostic_lsp_to_vim(diagnostics, bufnr, client_id)) +end +-- }}} + --- Get all diagnostics for clients --- ---@deprecated Prefer |vim.diagnostic.get()| @@ -253,6 +257,7 @@ end --- If nil, diagnostics of all clients are included. ---@return table with diagnostics grouped by bufnr (bufnr: Diagnostic[]) function M.get_all(client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_all is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local result = {} local namespace if client_id then @@ -274,6 +279,7 @@ end --- Else, return just the diagnostics associated with the client_id. ---@param predicate function|nil Optional function for filtering diagnostics function M.get(bufnr, client_id, predicate) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) predicate = predicate or function() return true end if client_id == nil then local all_diagnostics = {} @@ -335,6 +341,7 @@ end ---@param severity DiagnosticSeverity ---@param client_id number the client id function M.get_count(bufnr, severity, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_count is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) severity = severity_lsp_to_vim(severity) local opts = { severity = severity } if client_id ~= nil then @@ -351,6 +358,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic function M.get_prev(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -368,6 +376,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Previous diagnostic position function M.get_prev_pos(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_prev_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -384,6 +393,7 @@ end --- ---@param opts table See |vim.lsp.diagnostic.goto_next()| function M.goto_prev(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_prev is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -401,6 +411,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic function M.get_next(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -418,6 +429,7 @@ end ---@param opts table See |vim.lsp.diagnostic.goto_next()| ---@return table Next diagnostic position function M.get_next_pos(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_next_pos is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -432,6 +444,7 @@ end --- ---@deprecated Prefer |vim.diagnostic.goto_next()| function M.goto_next(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.goto_next is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if opts then if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -455,6 +468,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_signs(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_signs is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -475,6 +489,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_underline(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_underline is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -496,6 +511,7 @@ end --- - severity_limit (DiagnosticSeverity): --- - Limit severity of diagnostics found. E.g. "Warning" means { "Error", "Warning" } will be valid. function M.set_virtual_text(diagnostics, bufnr, client_id, _, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_virtual_text is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) local namespace = M.get_namespace(client_id) if opts and not opts.severity and opts.severity_limit then opts.severity = {min=severity_lsp_to_vim(opts.severity_limit)} @@ -514,6 +530,7 @@ end ---@return an array of [text, hl_group] arrays. This can be passed directly to --- the {virt_text} option of |nvim_buf_set_extmark()|. function M.get_virtual_text_chunks_for_line(bufnr, _, line_diags, opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.get_virtual_text_chunks_for_line is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) return vim.diagnostic._get_virt_text_chunks(diagnostic_lsp_to_vim(line_diags, bufnr), opts) end @@ -531,6 +548,7 @@ end ---@param position table|nil The (0,0)-indexed position ---@return table {popup_bufnr, win_id} function M.show_position_diagnostics(opts, buf_nr, position) + vim.api.nvim_echo({{'vim.lsp.diagnostic.show_position_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} opts.scope = "cursor" opts.pos = position @@ -554,6 +572,7 @@ end ---@param client_id number|nil the client id ---@return table {popup_bufnr, win_id} function M.show_line_diagnostics(opts, buf_nr, line_nr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.show_line_diagnostics is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} opts.scope = "line" opts.pos = line_nr @@ -565,7 +584,7 @@ end --- Redraw diagnostics for the given buffer and client --- ----@deprecated Prefer |vim.diagnostic.redraw()| +---@deprecated Prefer |vim.diagnostic.show()| --- --- This calls the "textDocument/publishDiagnostics" handler manually using --- the cached diagnostics already received from the server. This can be useful @@ -577,6 +596,7 @@ end --- client. The default is to redraw diagnostics for all attached --- clients. function M.redraw(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.redraw is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) bufnr = get_bufnr(bufnr) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) @@ -604,6 +624,7 @@ end --- - {workspace}: (boolean, default true) --- - Set the list with workspace diagnostics function M.set_qflist(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -635,6 +656,7 @@ end --- - {workspace}: (boolean, default false) --- - Set the list with workspace diagnostics function M.set_loclist(opts) + vim.api.nvim_echo({{'vim.lsp.diagnostic.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) opts = opts or {} if opts.severity then opts.severity = severity_lsp_to_vim(opts.severity) @@ -662,6 +684,7 @@ end -- send diagnostic information and the client will still process it. The -- diagnostics are simply not displayed to the user. function M.disable(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.disable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.disable(bufnr, client.id) @@ -682,6 +705,7 @@ end --- client. The default is to enable diagnostics for all attached --- clients. function M.enable(bufnr, client_id) + vim.api.nvim_echo({{'vim.lsp.diagnostic.enable is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) if not client_id then return vim.lsp.for_each_buffer_client(bufnr, function(client) M.enable(bufnr, client.id) diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index b3aa8b934f..86c9e2fd58 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -776,149 +776,9 @@ function protocol.make_client_capabilities() } end ---[=[ -export interface DocumentFilter { - --A language id, like `typescript`. - language?: string; - --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. - scheme?: string; - --A glob pattern, like `*.{ts,js}`. - -- - --Glob patterns can have the following syntax: - --- `*` to match one or more characters in a path segment - --- `?` to match on one character in a path segment - --- `**` to match any number of path segments, including none - --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) - --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) - pattern?: string; -} ---]=] - ---[[ ---Static registration options to be returned in the initialize request. -interface StaticRegistrationOptions { - --The id used to register the request. The id can be used to deregister - --the request again. See also Registration#id. - id?: string; -} - -export interface DocumentFilter { - --A language id, like `typescript`. - language?: string; - --A Uri [scheme](#Uri.scheme), like `file` or `untitled`. - scheme?: string; - --A glob pattern, like `*.{ts,js}`. - -- - --Glob patterns can have the following syntax: - --- `*` to match one or more characters in a path segment - --- `?` to match on one character in a path segment - --- `**` to match any number of path segments, including none - --- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) - --- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - --- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) - pattern?: string; -} -export type DocumentSelector = DocumentFilter[]; -export interface TextDocumentRegistrationOptions { - --A document selector to identify the scope of the registration. If set to null - --the document selector provided on the client side will be used. - documentSelector: DocumentSelector | null; -} - ---Code Action options. -export interface CodeActionOptions { - --CodeActionKinds that this server may return. - -- - --The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server - --may list out every specific kind they provide. - codeActionKinds?: CodeActionKind[]; -} - -interface ServerCapabilities { - --Defines how text documents are synced. Is either a detailed structure defining each notification or - --for backwards compatibility the TextDocumentSyncKind number. If omitted it defaults to `TextDocumentSyncKind.None`. - textDocumentSync?: TextDocumentSyncOptions | number; - --The server provides hover support. - hoverProvider?: boolean; - --The server provides completion support. - completionProvider?: CompletionOptions; - --The server provides signature help support. - signatureHelpProvider?: SignatureHelpOptions; - --The server provides goto definition support. - definitionProvider?: boolean; - --The server provides Goto Type Definition support. - -- - --Since 3.6.0 - typeDefinitionProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides Goto Implementation support. - -- - --Since 3.6.0 - implementationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides find references support. - referencesProvider?: boolean; - --The server provides document highlight support. - documentHighlightProvider?: boolean; - --The server provides document symbol support. - documentSymbolProvider?: boolean; - --The server provides workspace symbol support. - workspaceSymbolProvider?: boolean; - --The server provides code actions. The `CodeActionOptions` return type is only - --valid if the client signals code action literal support via the property - --`textDocument.codeAction.codeActionLiteralSupport`. - codeActionProvider?: boolean | CodeActionOptions; - --The server provides code lens. - codeLensProvider?: CodeLensOptions; - --The server provides document formatting. - documentFormattingProvider?: boolean; - --The server provides document range formatting. - documentRangeFormattingProvider?: boolean; - --The server provides document formatting on typing. - documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions; - --The server provides rename support. RenameOptions may only be - --specified if the client states that it supports - --`prepareSupport` in its initial `initialize` request. - renameProvider?: boolean | RenameOptions; - --The server provides document link support. - documentLinkProvider?: DocumentLinkOptions; - --The server provides color provider support. - -- - --Since 3.6.0 - colorProvider?: boolean | ColorProviderOptions | (ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides folding provider support. - -- - --Since 3.10.0 - foldingRangeProvider?: boolean | FoldingRangeProviderOptions | (FoldingRangeProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides go to declaration support. - -- - --Since 3.14.0 - declarationProvider?: boolean | (TextDocumentRegistrationOptions & StaticRegistrationOptions); - --The server provides execute command support. - executeCommandProvider?: ExecuteCommandOptions; - --Workspace specific server capabilities - workspace?: { - --The server supports workspace folder. - -- - --Since 3.6.0 - workspaceFolders?: { - * The server has support for workspace folders - supported?: boolean; - * Whether the server wants to receive workspace folder - * change notifications. - * - * If a strings is provided the string is treated as a ID - * under which the notification is registered on the client - * side. The ID can be used to unregister for these events - * using the `client/unregisterCapability` request. - changeNotifications?: string | boolean; - } - } - --Experimental server capabilities. - experimental?: any; -} ---]] - --- Creates a normalized object describing LSP server capabilities. +---@param server_capabilities table Table of capabilities supported by the server +---@return table Normalized table of capabilities function protocol.resolve_capabilities(server_capabilities) local general_properties = {} local text_document_sync_properties diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 059e66c53a..a3a91a34f7 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -1531,6 +1531,7 @@ end --- ---@param items (table) list of items function M.set_loclist(items, win_id) + vim.api.nvim_echo({{'vim.lsp.util.set_loclist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) vim.fn.setloclist(win_id or 0, {}, ' ', { title = 'Language Server'; items = items; @@ -1544,6 +1545,7 @@ end --- ---@param items (table) list of items function M.set_qflist(items) + vim.api.nvim_echo({{'vim.lsp.util.set_qflist is deprecated. See :h deprecated', 'WarningMsg'}}, true, {}) vim.fn.setqflist({}, ' ', { title = 'Language Server'; items = items; diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 4a58bee8ce..67d91360d8 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2021 Nov 27 +" Last Change: 2021 Nov 29 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -485,7 +485,7 @@ func s:StartDebugCommon(dict) " Run the command if the bang attribute was given and got to the debug " window. if get(a:dict, 'bang', 0) - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') call win_gotoid(s:ptywin) endif endfunc @@ -524,9 +524,14 @@ func TermDebugSendCommand(cmd) endif endfunc -" Send a command only when stopped. Used for :Next and :Step. -func s:SendCommandIfStopped(cmd) +" Send a command that resumes the program. If the program isn't stopped the +" command is not sent (to avoid a repeated command to cause trouble). +" If the command is sent then reset s:stopped. +func s:SendResumingCommand(cmd) if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + " call ch_log('assume that program is running after this command') call s:SendCommand(a:cmd) " else " call ch_log('dropping command, program is running: ' . a:cmd) @@ -815,11 +820,11 @@ func s:InstallCommands() command -nargs=? Break call s:SetBreakpoint(<q-args>) command Clear call s:ClearBreakpoint() - command Step call s:SendCommandIfStopped('-exec-step') - command Over call s:SendCommandIfStopped('-exec-next') - command Finish call s:SendCommandIfStopped('-exec-finish') + command Step call s:SendResumingCommand('-exec-step') + command Over call s:SendResumingCommand('-exec-next') + command Finish call s:SendResumingCommand('-exec-finish') command -nargs=* Run call s:Run(<q-args>) - command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) + command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . <q-args>) if s:way == 'prompt' command Stop call s:PromptInterrupt() @@ -978,9 +983,9 @@ endfunc func s:Run(args) if a:args != '' - call s:SendCommand('-exec-arguments ' . a:args) + call s:SendResumingCommand('-exec-arguments ' . a:args) endif - call s:SendCommand('-exec-run') + call s:SendResumingCommand('-exec-run') endfunc func s:SendEval(expr) diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index d3ca4de0e2..76b47d2e59 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Django template " Maintainer: Dave Hodder <dmh@dmh.org.uk> -" Last Change: 2014 Jul 13 +" Last Change: 2021 Nov 29 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -31,6 +31,7 @@ syn keyword djangoStatement contained closecomment widthratio url with endwith syn keyword djangoStatement contained get_current_language trans noop blocktrans syn keyword djangoStatement contained endblocktrans get_available_languages syn keyword djangoStatement contained get_current_language_bidi plural +syn keyword djangoStatement contained translate blocktranslate endblocktranslate " Django templete built-in filters syn keyword djangoFilter contained add addslashes capfirst center cut date diff --git a/runtime/syntax/squirrel.vim b/runtime/syntax/squirrel.vim new file mode 100644 index 0000000000..81d59cc986 --- /dev/null +++ b/runtime/syntax/squirrel.vim @@ -0,0 +1,50 @@ +" Vim syntax file +" Language: squirrel +" Current Maintainer: Matt Dunford (zenmatic@gmail.com) +" URL: https://github.com/zenmatic/vim-syntax-squirrel +" Last Change: 2021 Nov 28 + +" http://squirrel-lang.org/ + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +" inform C syntax that the file was included from cpp.vim +let b:filetype_in_cpp_family = 1 + +" Read the C syntax to start with +runtime! syntax/c.vim +unlet b:current_syntax + +" squirrel extensions +syn keyword squirrelStatement delete this in yield resume base clone +syn keyword squirrelAccess local +syn keyword cConstant null +syn keyword squirrelModifier static +syn keyword squirrelType bool instanceof typeof +syn keyword squirrelExceptions throw try catch +syn keyword squirrelStructure class function extends constructor +syn keyword squirrelBoolean true false +syn keyword squirrelRepeat foreach + +syn region squirrelMultiString start='@"' end='"$' end='";$'me=e-1 + +syn match squirrelShComment "^\s*#.*$" + +" Default highlighting +hi def link squirrelAccess squirrelStatement +hi def link squirrelExceptions Exception +hi def link squirrelStatement Statement +hi def link squirrelModifier Type +hi def link squirrelType Type +hi def link squirrelStructure Structure +hi def link squirrelBoolean Boolean +hi def link squirrelMultiString String +hi def link squirrelRepeat cRepeat +hi def link squirrelShComment Comment + +let b:current_syntax = "squirrel" + +" vim: ts=8 diff --git a/runtime/syntax/vb.vim b/runtime/syntax/vb.vim index 8ddb1efac3..607f6130ba 100644 --- a/runtime/syntax/vb.vim +++ b/runtime/syntax/vb.vim @@ -1,9 +1,11 @@ " Vim syntax file -" Language: Visual Basic -" Maintainer: Tim Chase <vb.vim@tim.thechases.com> -" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> -" (tried multiple times to contact, but email bounced) +" Language: Visual Basic +" Maintainer: Doug Kearns <dougkearns@gmail.com> +" Former Maintainer: Tim Chase <vb.vim@tim.thechases.com> +" Former Maintainer: Robert M. Cortopassi <cortopar@mindspring.com> +" (tried multiple times to contact, but email bounced) " Last Change: +" 2021 Nov 26 Incorporated additions from Doug Kearns " 2005 May 25 Synched with work by Thomas Barthel " 2004 May 30 Added a few keywords @@ -13,7 +15,7 @@ " quit when a syntax file was already loaded if exists("b:current_syntax") - finish + finish endif " VB is case insensitive @@ -233,7 +235,7 @@ syn keyword vbKeyword Public PublicNotCreateable OnNewProcessSingleUse syn keyword vbKeyword InSameProcessMultiUse GlobalMultiUse Resume Seek syn keyword vbKeyword Set Static Step String Time WithEvents -syn keyword vbTodo contained TODO +syn keyword vbTodo contained TODO "Datatypes syn keyword vbTypes Boolean Byte Currency Date Decimal Double Empty @@ -319,46 +321,54 @@ syn match vbNumber "\<\d\+\>" syn match vbNumber "\<\d\+\.\d*\>" "floating point number, starting with a dot syn match vbNumber "\.\d\+\>" -"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" -"syn match vbNumber ":[[:xdigit:]]\+" -"syn match vbNumber "[-+]\=\<\d\+\>" -syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" -syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" -syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" +"syn match vbNumber "{[[:xdigit:]-]\+}\|&[hH][[:xdigit:]]\+&" +"syn match vbNumber ":[[:xdigit:]]\+" +"syn match vbNumber "[-+]\=\<\d\+\>" +syn match vbFloat "[-+]\=\<\d\+[eE][\-+]\=\d\+" +syn match vbFloat "[-+]\=\<\d\+\.\d*\([eE][\-+]\=\d\+\)\=" +syn match vbFloat "[-+]\=\<\.\d\+\([eE][\-+]\=\d\+\)\=" -" String and Character contstants +" String and Character constants syn region vbString start=+"+ end=+"\|$+ syn region vbComment start="\(^\|\s\)REM\s" end="$" contains=vbTodo syn region vbComment start="\(^\|\s\)\'" end="$" contains=vbTodo -syn match vbLineNumber "^\d\+\(\s\|$\)" -syn match vbTypeSpecifier "[a-zA-Z0-9][\$%&!#]"ms=s+1 +syn match vbLineLabel "^\h\w\+:" +syn match vbLineNumber "^\d\+\(:\|\s\|$\)" +syn match vbTypeSpecifier "\<\a\w*[@\$%&!#]"ms=s+1 syn match vbTypeSpecifier "#[a-zA-Z0-9]"me=e-1 +" Conditional Compilation +syn match vbPreProc "^#const\>" +syn region vbPreProc matchgroup=PreProc start="^#if\>" end="\<then\>" transparent contains=TOP +syn region vbPreProc matchgroup=PreProc start="^#elseif\>" end="\<then\>" transparent contains=TOP +syn match vbPreProc "^#else\>" +syn match vbPreProc "^#end\s*if\>" " Define the default highlighting. " Only when an item doesn't have highlighting yet -hi def link vbBoolean Boolean -hi def link vbLineNumber Comment -hi def link vbComment Comment -hi def link vbConditional Conditional -hi def link vbConst Constant -hi def link vbDefine Constant -hi def link vbError Error -hi def link vbFunction Identifier -hi def link vbIdentifier Identifier -hi def link vbNumber Number -hi def link vbFloat Float -hi def link vbMethods PreProc -hi def link vbOperator Operator -hi def link vbRepeat Repeat -hi def link vbString String -hi def link vbStatement Statement -hi def link vbKeyword Statement -hi def link vbEvents Special -hi def link vbTodo Todo -hi def link vbTypes Type -hi def link vbTypeSpecifier Type - +hi def link vbBoolean Boolean +hi def link vbLineNumber Comment +hi def link vbLineLabel Comment +hi def link vbComment Comment +hi def link vbConditional Conditional +hi def link vbConst Constant +hi def link vbDefine Constant +hi def link vbError Error +hi def link vbFunction Identifier +hi def link vbIdentifier Identifier +hi def link vbNumber Number +hi def link vbFloat Float +hi def link vbMethods PreProc +hi def link vbOperator Operator +hi def link vbRepeat Repeat +hi def link vbString String +hi def link vbStatement Statement +hi def link vbKeyword Statement +hi def link vbEvents Special +hi def link vbTodo Todo +hi def link vbTypes Type +hi def link vbTypeSpecifier Type +hi def link vbPreProc PreProc let b:current_syntax = "vb" diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index d0272916b9..41993b65b0 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -53,7 +53,7 @@ syn case ignore syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineNr DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu +syn keyword vimHLGroup contained ColorColumn Cursor CursorColumn CursorIM CursorLine CursorLineFold CursorLineNr CursorLineSign DiffAdd DiffChange DiffDelete DiffText Directory EndOfBuffer ErrorMsg FoldColumn Folded IncSearch LineNr MatchParen Menu ModeMsg MoreMsg NonText Normal Pmenu PmenuSbar PmenuSel PmenuThumb Question QuickFixLine Scrollbar Search SignColumn SpecialKey SpellBad SpellCap SpellLocal SpellRare StatusLine StatusLineNC TabLine TabLineFill TabLineSel Title Tooltip VertSplit Visual WarningMsg WildMenu syn match vimHLGroup contained "Conceal" syn keyword vimOnlyHLGroup contained LineNrAbove LineNrBelow StatusLineTerm Terminal VisualNOS syn keyword nvimHLGroup contained Substitute TermCursor TermCursorNC diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 185d55daed..bb16459a7f 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -468,9 +468,11 @@ list(APPEND NVIM_LINK_LIBRARIES if(UNIX) list(APPEND NVIM_LINK_LIBRARIES - m - util - ) + m) + if (NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS") + list(APPEND NVIM_LINK_LIBRARIES + util) + endif() endif() set(NVIM_EXEC_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUA_PREFERRED_LIBRARIES}) diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 6e25e627b9..742b953c2a 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -339,7 +339,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// @param col Column where to place the mark, 0-based. |api-indexing| /// @param opts Optional parameters. /// - id : id of the extmark to edit. -/// - end_line : ending line of the mark, 0-based inclusive. +/// - end_row : ending line of the mark, 0-based inclusive. /// - end_col : ending col of the mark, 0-based exclusive. /// - hl_group : name of the highlight group used to highlight /// this mark. @@ -431,16 +431,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } int line2 = -1; - if (opts->end_line.type == kObjectTypeInteger) { - Integer val = opts->end_line.data.integer; + + // For backward compatibility we support "end_line" as an alias for "end_row" + if (HAS_KEY(opts->end_line)) { + if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line"); + goto error; + } + opts->end_row = opts->end_line; + } + + if (opts->end_row.type == kObjectTypeInteger) { + Integer val = opts->end_row.data.integer; if (val < 0 || val > buf->b_ml.ml_line_count) { - api_set_error(err, kErrorTypeValidation, "end_line value outside range"); + api_set_error(err, kErrorTypeValidation, "end_row value outside range"); goto error; } else { line2 = (int)val; } - } else if (HAS_KEY(opts->end_line)) { - api_set_error(err, kErrorTypeValidation, "end_line is not an integer"); + } else if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "end_row is not an integer"); goto error; } @@ -571,10 +581,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer OPTION_TO_BOOL(right_gravity, right_gravity, true); // Only error out if they try to set end_right_gravity without - // setting end_col or end_line + // setting end_col or end_row if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) { api_set_error(err, kErrorTypeValidation, - "cannot set end_right_gravity without setting end_line or end_col"); + "cannot set end_right_gravity without setting end_row or end_col"); goto error; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index e956a54dbc..f3e7f2f1dc 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -5,6 +5,7 @@ return { set_extmark = { "id"; "end_line"; + "end_row"; "end_col"; "hl_group"; "virt_text"; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d470def277..9b407eab8b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -513,7 +513,7 @@ String cbuf_to_string(const char *buf, size_t size) String cstrn_to_string(const char *str, size_t maxsize) FUNC_ATTR_NONNULL_ALL { - return cbuf_to_string(str, strnlen(str, maxsize)); + return cbuf_to_string(str, STRNLEN(str, maxsize)); } /// Creates a String using the given C string. Unlike diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 36179b1fab..4f7c320129 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -842,7 +842,7 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); msg_multiline_attr((const char *)chunk.text.data, chunk.attr, - false, &need_clear); + true, &need_clear); } if (history) { msg_ext_set_kind("echomsg"); @@ -1842,6 +1842,9 @@ static void write_msg(String message, bool to_err) ++no_wait_return; for (uint32_t i = 0; i < message.size; i++) { + if (got_int) { + break; + } if (to_err) { PUSH_CHAR(i, err_pos, err_line_buf, emsg); } else { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 490fe5a0ac..9044657358 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1213,10 +1213,13 @@ win_found: // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } + curbuf = curwin->w_buffer; + // May need to restore insert mode for a prompt buffer. + entering_window(curwin); + prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab - curbuf = curwin->w_buffer; xfree(globaldir); globaldir = aco->globaldir; diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 47d89717e8..c4e5d9522b 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -14,8 +14,8 @@ /// @return Folded variant. #define CH_FOLD(c) \ utf_fold((sizeof(c) == sizeof(char)) \ - ?((int)(uint8_t)(c)) \ - :((int)(c))) + ? ((int)(uint8_t)(c)) \ + : ((int)(c))) /// Flags for vim_str2nr() typedef enum { diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index e334fd166e..4e1d7f9d78 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -108,6 +108,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a int head = 0; one_more = (State & INSERT) + || (State & TERM_FOCUS) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 7d2ce04743..9bfb8a9d4a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -227,6 +227,7 @@ typedef struct insert_state { cmdarg_T *ca; int mincol; int cmdchar; + int cmdchar_todo; // cmdchar to handle once in init_prompt int startln; long count; int c; @@ -290,6 +291,7 @@ static void insert_enter(InsertState *s) s->did_backspace = true; s->old_topfill = -1; s->replaceState = REPLACE; + s->cmdchar_todo = s->cmdchar; // Remember whether editing was restarted after CTRL-O did_restart_edit = restart_edit; // sleep before redrawing, needed for "CTRL-O :" that results in an @@ -585,7 +587,8 @@ static int insert_check(VimState *state) } if (bt_prompt(curbuf)) { - init_prompt(s->cmdchar); + init_prompt(s->cmdchar_todo); + s->cmdchar_todo = NUL; } // If we inserted a character at the last position of the last line in the @@ -655,10 +658,17 @@ static int insert_check(VimState *state) static int insert_execute(VimState *state, int key) { + InsertState *const s = (InsertState *)state; + if (stop_insert_mode) { + // Insert mode ended, possibly from a callback. + s->count = 0; + s->nomove = true; + return 0; + } + if (key == K_IGNORE || key == K_NOP) { return -1; // get another key } - InsertState *s = (InsertState *)state; s->c = key; // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V. @@ -984,6 +994,15 @@ static int insert_handle_key(InsertState *s) break; case Ctrl_W: // delete word before the cursor + if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) { + // In a prompt window CTRL-W is used for window commands. + // Use Shift-CTRL-W to delete a word. + stuffcharReadbuff(Ctrl_W); + restart_edit = 'A'; + s->nomove = true; + s->count = 0; + return 0; + } s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space); auto_format(false, true); break; @@ -1653,10 +1672,21 @@ static void init_prompt(int cmdchar_todo) coladvance(MAXCOL); changed_bytes(curbuf->b_ml.ml_line_count, 0); } + + // Insert always starts after the prompt, allow editing text after it. + if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) { + Insstart.lnum = curwin->w_cursor.lnum; + Insstart.col = STRLEN(prompt); + Insstart_orig = Insstart; + Insstart_textlen = Insstart.col; + Insstart_blank_vcol = MAXCOL; + arrow_used = false; + } + if (cmdchar_todo == 'A') { coladvance(MAXCOL); } - if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) { + if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) { curwin->w_cursor.col = STRLEN(prompt); } // Make sure the cursor is in a valid position. @@ -5241,7 +5271,7 @@ static int ins_complete(int c, bool enable_pum) funcname = get_complete_funcname(ctrl_x_mode); if (*funcname == NUL) { semsg(_(e_notset), ctrl_x_mode == CTRL_X_FUNCTION - ? "completefunc" : "omnifunc"); + ? "completefunc" : "omnifunc"); // restore did_ai, so that adding comment leader works did_ai = save_did_ai; return FAIL; @@ -8241,7 +8271,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) - && (arrow_used + && ((arrow_used && !bt_prompt(curbuf)) || (curwin->w_cursor.lnum == Insstart_orig.lnum && curwin->w_cursor.col <= Insstart_orig.col))) || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2faae08fc8..85e81ee975 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -134,13 +134,15 @@ typedef struct { .vv_flags = flags, \ } +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -150,100 +152,103 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), - VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), - VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), - VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), - VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), - VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), - VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), - VV(VV_FNAME, "fname", VAR_STRING, VV_RO), - VV(VV_LANG, "lang", VAR_STRING, VV_RO), - VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), - VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), - VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), - VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), - VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), - VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), - VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), - VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), - VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), - VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), - VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), - VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), - VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), - VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), - VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_REG, "register", VAR_STRING, VV_RO), - VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), - VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), - VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), - VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), - VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), - VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), - VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), - VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), - VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), - VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), - VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), - VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), - VV(VV_CHAR, "char", VAR_STRING, 0), - VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), - VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), - VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), - VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), - VV(VV_OP, "operator", VAR_STRING, VV_RO), - VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), - VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), - VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), - VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), - VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), - VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), - VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), - VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_FALSE, "false", VAR_BOOL, VV_RO), - VV(VV_TRUE, "true", VAR_BOOL, VV_RO), - VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), - VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), - VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), - VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), - VV(VV_TESTING, "testing", VAR_NUMBER, 0), - VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), - VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), - VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), - VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), - VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), - VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), - VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), // Neovim - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), - VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), - VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -344,7 +349,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= 16); + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -4523,7 +4528,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) * dict.name */ key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) { + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { } if (len == 0) { return FAIL; @@ -6603,8 +6608,9 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL - && (is_funcref ? find_func(trans_name) == NULL - : !translated_function_exists((const char *)trans_name))) { + && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -9649,8 +9655,8 @@ bool var_check_func_name(const char *const name, const bool new_var) { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] - : name[0])) { + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { semsg(_("E704: Funcref variable name must start with a capital: %s"), name); return false; } @@ -10446,11 +10452,15 @@ void option_last_set_msg(LastSet last_set) } } -// reset v:option_new, v:option_old and v:option_type +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. void reset_v_option_vars(void) { - set_vim_var_string(VV_OPTION_NEW, NULL, -1); - set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } diff --git a/src/nvim/eval.h b/src/nvim/eval.h index d34348a274..3b3a68bd29 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -138,6 +138,9 @@ typedef enum { VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, + VV_OPTION_OLDLOCAL, + VV_OPTION_OLDGLOBAL, + VV_OPTION_COMMAND, VV_OPTION_TYPE, VV_ERRORS, VV_FALSE, @@ -209,8 +212,8 @@ typedef enum { /// flags for find_name_end() #define FNE_INCL_BR 1 // find_name_end(): include [] in name -#define FNE_CHECK_START 2 /* find_name_end(): check name starts with - valid character */ +#define FNE_CHECK_START 2 // find_name_end(): check name starts with + // valid character typedef struct { TimeWatcher tw; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index dfc51d80af..9a76b67de6 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -17,7 +17,7 @@ end -- Usable with the base key: use the last function argument as the method base. -- Value is from funcs.h file. "BASE_" prefix is omitted. -local LAST = "BASE_LAST" +-- local LAST = "BASE_LAST" (currently unused after port of v8.2.1168) return { funcs={ @@ -26,14 +26,14 @@ return { add={args=2, base=1}, ['and']={args=2, base=1}, api_info={}, - append={args=2, base=LAST}, - appendbufline={args=3, base=LAST}, + append={args=2, base=2}, + appendbufline={args=3, base=3}, argc={args={0, 1}}, argidx={}, arglistid={args={0, 2}}, argv={args={0, 2}}, asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc - assert_beeps={args={1}, base=1}, + assert_beeps={args=1, base=1}, assert_equal={args={2, 3}, base=2}, assert_equalfile={args={2, 3}, base=1}, assert_exception={args={1, 2}}, @@ -41,7 +41,7 @@ return { assert_false={args={1, 2}, base=1}, assert_inrange={args={3, 4}, base=3}, assert_match={args={2, 3}, base=2}, - assert_nobeep={args={1}}, + assert_nobeep={args=1, base=1}, assert_notequal={args={2, 3}, base=2}, assert_notmatch={args={2, 3}, base=2}, assert_report={args=1, base=1}, @@ -53,8 +53,8 @@ return { bufadd={args=1, base=1}, bufexists={args=1, base=1}, buffer_exists={args=1, base=1, func='f_bufexists'}, -- obsolete - buffer_name={args={0, 1}, func='f_bufname'}, -- obsolete - buffer_number={args={0, 1}, func='f_bufnr'}, -- obsolete + buffer_name={args={0, 1}, base=1, func='f_bufname'}, -- obsolete + buffer_number={args={0, 1}, base=1, func='f_bufnr'}, -- obsolete buflisted={args=1, base=1}, bufload={args=1, base=1}, bufloaded={args=1, base=1}, @@ -71,7 +71,7 @@ return { chanclose={args={1, 2}}, chansend={args=2}, char2nr={args={1, 2}, base=1}, - charidx={args={2, 3}}, + charidx={args={2, 3}, base=1}, chdir={args=1, base=1}, cindent={args=1, base=1}, clearmatches={args={0, 1}, base=1}, @@ -121,7 +121,7 @@ return { filter={args=2, base=1}, finddir={args={1, 3}, base=1}, findfile={args={1, 3}, base=1}, - flatten={args={1, 2}}, + flatten={args={1, 2}, base=1}, float2nr={args=1, base=1}, floor={args=1, base=1, func="float_op_wrapper", data="&floor"}, fmod={args=2, base=1}, @@ -137,7 +137,7 @@ return { ['function']={args={1, 3}, base=1}, garbagecollect={args={0, 1}}, get={args={2, 3}, base=1}, - getbufinfo={args={0, 1}}, + getbufinfo={args={0, 1}, base=1}, getbufline={args={2, 3}, base=1}, getbufvar={args={2, 3}, base=1}, getchangelist={args={0, 1}, base=1}, @@ -152,7 +152,7 @@ return { getcompletion={args={2, 3}, base=1}, getcurpos={}, getcwd={args={0, 2}, base=1}, - getenv={args={1}, base=1}, + getenv={args=1, base=1}, getfontname={args={0, 1}}, getfperm={args=1, base=1}, getfsize={args=1, base=1}, @@ -161,7 +161,7 @@ return { getjumplist={args={0, 2}, base=1}, getline={args={1, 2}, base=1}, getloclist={args={1, 2}}, - getmarklist={args={0, 1}}, + getmarklist={args={0, 1}, base=1}, getmatches={args={0, 1}}, getmousepos={}, getpid={}, @@ -262,7 +262,7 @@ return { pow={args=2, base=1}, prevnonblank={args=1, base=1}, printf={args=varargs(1), base=2}, - prompt_getprompt={args=1}, + prompt_getprompt={args=1, base=1}, prompt_setcallback={args={2, 2}, base=1}, prompt_setinterrupt={args={2, 2}, base=1}, prompt_setprompt={args={2, 2}, base=1}, @@ -291,82 +291,82 @@ return { rpcstart={args={1, 2}}, rpcstop={args=1}, rubyeval={args=1, base=1}, - screenattr={args=2}, - screenchar={args=2}, - screenchars={args=2}, + screenattr={args=2, base=1}, + screenchar={args=2, base=1}, + screenchars={args=2, base=1}, screencol={}, - screenpos={args=3}, + screenpos={args=3, base=1}, screenrow={}, - screenstring={args=2}, - search={args={1, 4}}, - searchcount={args={0,1}}, - searchdecl={args={1, 3}}, + screenstring={args=2, base=1}, + search={args={1, 4}, base=1}, + searchcount={args={0, 1}, base=1}, + searchdecl={args={1, 3}, base=1}, searchpair={args={3, 7}}, searchpairpos={args={3, 7}}, - searchpos={args={1, 4}}, + searchpos={args={1, 4}, base=1}, serverlist={}, serverstart={args={0, 1}}, serverstop={args=1}, - setbufline={args=3}, - setbufvar={args=3}, - setcharsearch={args=1}, - setcmdpos={args=1}, - setenv={args=2}, + setbufline={args=3, base=3}, + setbufvar={args=3, base=3}, + setcharsearch={args=1, base=1}, + setcmdpos={args=1, base=1}, + setenv={args=2, base=2}, setfperm={args=2, base=1}, - setline={args=2}, - setloclist={args={2, 4}}, - setmatches={args={1, 2}}, - setpos={args=2}, - setqflist={args={1, 3}}, - setreg={args={2, 3}}, - settabvar={args=3}, - settabwinvar={args=4}, - settagstack={args={2, 3}}, - setwinvar={args=3}, - sha256={args=1}, - shellescape={args={1, 2}}, - shiftwidth={args={0, 1}}, - sign_define={args={1, 2}}, - sign_getdefined={args={0, 1}}, - sign_getplaced={args={0, 2}}, - sign_jump={args={3, 3}}, - sign_place={args={4, 5}}, - sign_placelist={args={1}}, - sign_undefine={args={0, 1}}, - sign_unplace={args={1, 2}}, - sign_unplacelist={args={1}}, - simplify={args=1}, + setline={args=2, base=2}, + setloclist={args={2, 4}, base=2}, + setmatches={args={1, 2}, base=1}, + setpos={args=2, base=2}, + setqflist={args={1, 3}, base=1}, + setreg={args={2, 3}, base=2}, + settabvar={args=3, base=3}, + settabwinvar={args=4, base=4}, + settagstack={args={2, 3}, base=2}, + setwinvar={args=3, base=3}, + sha256={args=1, base=1}, + shellescape={args={1, 2}, base=1}, + shiftwidth={args={0, 1}, base=1}, + sign_define={args={1, 2}, base=1}, + sign_getdefined={args={0, 1}, base=1}, + sign_getplaced={args={0, 2}, base=1}, + sign_jump={args=3, base=1}, + sign_place={args={4, 5}, base=1}, + sign_placelist={args=1, base=1}, + sign_undefine={args={0, 1}, base=1}, + sign_unplace={args={1, 2}, base=1}, + sign_unplacelist={args=1, base=1}, + simplify={args=1, base=1}, sin={args=1, base=1, func="float_op_wrapper", data="&sin"}, sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"}, sockconnect={args={2,3}}, sort={args={1, 3}, base=1}, - soundfold={args=1}, + soundfold={args=1, base=1}, stdioopen={args=1}, - spellbadword={args={0, 1}}, - spellsuggest={args={1, 3}}, + spellbadword={args={0, 1}, base=1}, + spellsuggest={args={1, 3}, base=1}, split={args={1, 3}, base=1}, sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, stdpath={args=1}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, - str2nr={args={1, 3}}, - strcharpart={args={2, 3}}, - strchars={args={1,2}}, - strdisplaywidth={args={1, 2}}, - strftime={args={1, 2}}, - strgetchar={args={2, 2}}, - stridx={args={2, 3}}, + str2nr={args={1, 3}, base=1}, + strcharpart={args={2, 3}, base=1}, + strchars={args={1, 2}, base=1}, + strdisplaywidth={args={1, 2}, base=1}, + strftime={args={1, 2}, base=1}, + strgetchar={args=2, base=1}, + stridx={args={2, 3}, base=1}, string={args=1, base=1}, strlen={args=1, base=1}, - strpart={args={2, 4}}, - strptime={args=2}, - strridx={args={2, 3}}, + strpart={args={2, 4}, base=1}, + strptime={args=2, base=1}, + strridx={args={2, 3}, base=1}, strtrans={args=1, base=1}, strwidth={args=1, base=1}, - submatch={args={1, 2}}, + submatch={args={1, 2}, base=1}, substitute={args=4, base=1}, - swapinfo={args={1}}, - swapname={args={1}}, + swapinfo={args=1, base=1}, + swapname={args=1, base=1}, synID={args=3}, synIDattr={args={2, 3}, base=1}, synIDtrans={args=1, base=1}, @@ -374,58 +374,58 @@ return { synstack={args=2}, system={args={1, 2}, base=1}, systemlist={args={1, 3}, base=1}, - tabpagebuflist={args={0, 1}}, + tabpagebuflist={args={0, 1}, base=1}, tabpagenr={args={0, 1}}, - tabpagewinnr={args={1, 2}}, + tabpagewinnr={args={1, 2}, base=1}, tagfiles={}, - taglist={args={1, 2}}, + taglist={args={1, 2}, base=1}, tan={args=1, base=1, func="float_op_wrapper", data="&tan"}, tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"}, tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, test_write_list_log={args=1}, - timer_info={args={0,1}}, - timer_pause={args=2}, - timer_start={args={2,3}}, - timer_stop={args=1}, + timer_info={args={0, 1}, base=1}, + timer_pause={args=2, base=1}, + timer_start={args={2, 3}, base=1}, + timer_stop={args=1, base=1}, timer_stopall={args=0}, - tolower={args=1}, - toupper={args=1}, - tr={args=3}, - trim={args={1,3}}, + tolower={args=1, base=1}, + toupper={args=1, base=1}, + tr={args=3, base=1}, + trim={args={1, 3}, base=1}, trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"}, type={args=1, base=1}, - undofile={args=1}, + undofile={args=1, base=1}, undotree={}, uniq={args={1, 3}, base=1}, values={args=1, base=1}, - virtcol={args=1}, + virtcol={args=1, base=1}, visualmode={args={0, 1}}, wait={args={2,3}}, wildmenumode={}, - win_execute={args={2, 3}}, - win_findbuf={args=1}, - win_getid={args={0,2}}, - win_gettype={args={0,1}}, - win_gotoid={args=1}, - win_id2tabwin={args=1}, - win_id2win={args=1}, - win_screenpos={args=1}, - win_splitmove={args={2, 3}}, + win_execute={args={2, 3}, base=2}, + win_findbuf={args=1, base=1}, + win_getid={args={0, 2}, base=1}, + win_gettype={args={0, 1}, base=1}, + win_gotoid={args=1, base=1}, + win_id2tabwin={args=1, base=1}, + win_id2win={args=1, base=1}, + win_screenpos={args=1, base=1}, + win_splitmove={args={2, 3}, base=1}, winbufnr={args=1, base=1}, wincol={}, windowsversion={}, - winheight={args=1}, - winlayout={args={0, 1}}, + winheight={args=1, base=1}, + winlayout={args={0, 1}, base=1}, winline={}, - winnr={args={0, 1}}, + winnr={args={0, 1}, base=1}, winrestcmd={}, - winrestview={args=1}, + winrestview={args=1, base=1}, winsaveview={}, - winwidth={args=1}, + winwidth={args=1, base=1}, wordcount={}, - writefile={args={2, 3}}, + writefile={args={2, 3}, base=1}, xor={args=2, base=1}, }, } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 5008945f09..797420c150 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -726,11 +726,11 @@ json_decode_string_cycle_start: semsg(_("E474: Using comma in place of colon: %.*s"), LENP(p, e)); goto json_decode_string_fail; } else if (last_container.special_val == NULL - ? (last_container.container.v_type == VAR_DICT - ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (tv_list_len(last_container.container.vval.v_list) - == 0)) - : (tv_list_len(last_container.special_val) == 0)) { + ? (last_container.container.v_type == VAR_DICT + ? (DICT_LEN(last_container.container.vval.v_dict) == 0) + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { semsg(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index dfadd28ebe..33ca4016cf 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11551,6 +11551,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict; rettv->vval.v_number = -1; + if (check_secure()) { + return; + } if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 77944851d2..f95fe84f69 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -305,9 +305,8 @@ void ex_align(exarg_T *eap) */ do { (void)set_indent(++new_indent, 0); - } - while (linelen(NULL) <= width); - --new_indent; + } while (linelen(NULL) <= width); + new_indent--; break; } --new_indent; @@ -1457,7 +1456,7 @@ error: filterend: if (curbuf != old_curbuf) { - --no_wait_return; + no_wait_return--; emsg(_("E135: *Filter* Autocommands must not change current buffer")); } if (itmp != NULL) { @@ -2094,7 +2093,7 @@ void do_wqall(exarg_T *eap) } if (buf->b_ffname == NULL) { semsg(_("E141: No file name for buffer %" PRId64), (int64_t)buf->b_fnum); - ++error; + error++; } else if (check_readonly(&eap->forceit, buf) || check_overwrite(eap, buf, buf->b_fname, buf->b_ffname, FALSE) == FAIL) { @@ -2120,17 +2119,15 @@ void do_wqall(exarg_T *eap) } } -/* - * Check the 'write' option. - * Return TRUE and give a message when it's not st. - */ -int not_writing(void) +// Check the 'write' option. +// Return true and give a message when it's not st. +bool not_writing(void) { if (p_write) { - return FALSE; + return false; } emsg(_("E142: File not written: Writing is disabled by 'write' option")); - return TRUE; + return true; } /* @@ -2945,7 +2942,7 @@ void ex_append(exarg_T *eap) } for (;;) { - msg_scroll = TRUE; + msg_scroll = true; need_wait_return = false; if (curbuf->b_p_ai) { if (append_indent >= 0) { @@ -3131,7 +3128,7 @@ void ex_z(exarg_T *eap) // the number of '-' and '+' multiplies the distance if (*kind == '-' || *kind == '+') { - for (x = kind + 1; *x == *kind; ++x) { + for (x = kind + 1; *x == *kind; x++) { } } @@ -3214,26 +3211,24 @@ void ex_z(exarg_T *eap) ex_no_reprint = true; } -/* - * Check if the secure flag is set (.exrc or .vimrc in current directory). - * If so, give an error message and return TRUE. - * Otherwise, return FALSE. - */ -int check_secure(void) +// Check if the secure flag is set (.exrc or .vimrc in current directory). +// If so, give an error message and return true. +// Otherwise, return false. +bool check_secure(void) { if (secure) { secure = 2; emsg(_(e_curdir)); - return TRUE; + return true; } // In the sandbox more things are not allowed, including the things // disallowed in secure mode. if (sandbox != 0) { emsg(_(e_sandbox)); - return TRUE; + return true; } - return FALSE; + return false; } /// Previous substitute replacement string diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 024eb0a904..77cd50ecb7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -435,7 +435,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) && cstack.cs_idx < 0 && !(getline_is_func && func_has_abort(real_cookie))) { - did_emsg = FALSE; + did_emsg = false; } /* @@ -2717,7 +2717,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * */ gap = &curbuf->b_ucmds; for (;;) { - for (j = 0; j < gap->ga_len; ++j) { + for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); cp = eap->cmd; np = uc->uc_name; @@ -5328,7 +5328,7 @@ static void uc_list(char_u *name, size_t name_len) ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; for (;;) { - for (i = 0; i < gap->ga_len; ++i) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); a = cmd->uc_argt; @@ -5715,7 +5715,7 @@ static void ex_delcommand(exarg_T *eap) gap = &curbuf->b_ucmds; for (;;) { - for (i = 0; i < gap->ga_len; ++i) { + for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); cmp = STRCMP(eap->arg, cmd->uc_name); if (cmp <= 0) { @@ -8338,9 +8338,7 @@ static void ex_redir(exarg_T *eap) if (var_redir_start(skipwhite(arg), append) == OK) { redir_vname = 1; } - } - // TODO: redirect to a buffer - else { + } else { // TODO(vim): redirect to a buffer semsg(_(e_invarg2), eap->arg); } } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index f60f0ebe98..b1c59a607c 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -437,7 +437,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int } } } else { - *should_free = FALSE; + *should_free = false; ret = value; } @@ -888,11 +888,10 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int skip; int result; cstack_T *const cstack = eap->cstack; - skip = CHECK_SKIP; + bool skip = CHECK_SKIP; if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] @@ -902,14 +901,14 @@ void ex_else(exarg_T *eap) return; } eap->errmsg = N_("E582: :elseif without :if"); - skip = TRUE; + skip = true; } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { if (eap->cmdidx == CMD_else) { eap->errmsg = N_("E583: multiple :else"); return; } eap->errmsg = N_("E584: :elseif after :else"); - skip = TRUE; + skip = true; } // if skipping or the ":if" was TRUE, reset ACTIVE, otherwise set it @@ -917,7 +916,7 @@ void ex_else(exarg_T *eap) if (eap->errmsg == NULL) { cstack->cs_flags[cstack->cs_idx] = CSF_TRUE; } - skip = TRUE; // don't evaluate an ":elseif" + skip = true; // don't evaluate an ":elseif" } else { cstack->cs_flags[cstack->cs_idx] = CSF_ACTIVE; } @@ -932,7 +931,7 @@ void ex_else(exarg_T *eap) // later on. if (!skip && dbg_check_skipped(eap) && got_int) { (void)do_intthrow(cstack); - skip = TRUE; + skip = true; } if (eap->cmdidx == CMD_elseif) { @@ -1322,9 +1321,9 @@ void ex_try(exarg_T *eap) void ex_catch(exarg_T *eap) { int idx = 0; - int give_up = FALSE; - int skip = FALSE; - int caught = FALSE; + bool give_up = false; + bool skip = false; + bool caught = false; char_u *end; char_u save_char = 0; char_u *save_cpo; @@ -1335,13 +1334,13 @@ void ex_catch(exarg_T *eap) if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = N_("E603: :catch without :try"); - give_up = TRUE; + give_up = true; } else { if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { // Report what's missing if the matching ":try" is not in its // finally clause. eap->errmsg = get_end_emsg(cstack); - skip = TRUE; + skip = true; } for (idx = cstack->cs_idx; idx > 0; --idx) { if (cstack->cs_flags[idx] & CSF_TRY) { @@ -1352,7 +1351,7 @@ void ex_catch(exarg_T *eap) // Give up for a ":catch" after ":finally" and ignore it. // Just parse. eap->errmsg = N_("E604: :catch after :finally"); - give_up = TRUE; + give_up = true; } else { rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); @@ -1425,7 +1424,7 @@ void ex_catch(exarg_T *eap) // CTRL-C while matching should abort it. // prev_got_int = got_int; - got_int = FALSE; + got_int = false; caught = vim_regexec_nl(®match, (char_u *)current_exception->value, (colnr_T)0); got_int |= prev_got_int; @@ -1602,8 +1601,7 @@ void ex_finally(exarg_T *eap) void ex_endtry(exarg_T *eap) { int idx; - int skip; - int rethrow = FALSE; + bool rethrow = false; int pending = CSTP_NONE; void *rettv = NULL; cstack_T *const cstack = eap->cstack; @@ -1621,20 +1619,19 @@ void ex_endtry(exarg_T *eap) // made inactive by a ":continue", ":break", ":return", or ":finish" in // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. - skip = (did_emsg || got_int || current_exception - || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); + bool skip = did_emsg || got_int || current_exception + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); // Find the matching ":try" and report what's missing. idx = cstack->cs_idx; do { - --idx; - } - while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); + idx--; + } while (idx > 0 && !(cstack->cs_flags[idx] & CSF_TRY)); rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, &cstack->cs_looplevel); - skip = TRUE; + skip = true; /* * If an exception is being thrown, discard it to prevent it from @@ -1677,7 +1674,7 @@ void ex_endtry(exarg_T *eap) // before the ":endtry". That is, throw an interrupt exception and // set "skip" and "rethrow". if (got_int) { - skip = TRUE; + skip = true; (void)do_intthrow(cstack); // The do_intthrow() call may have reset current_exception or // cstack->cs_pending[idx]. diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 25b30b2805..15da4b2d60 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -17,8 +17,8 @@ #define CSF_THROWN 0x0400 // exception thrown to this try conditional #define CSF_CAUGHT 0x0800 // exception caught by this try conditional #define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" -/* Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset - * (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. */ +// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset +// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. /* * What's pending for being reactivated at the ":endtry" of this try diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9cf39802de..475f22d061 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2491,27 +2491,25 @@ char *get_text_locked_msg(void) } /// Check if "curbuf->b_ro_locked" or "allbuf_lock" is set and -/// return TRUE when it is and give an error message. -int curbuf_locked(void) +/// return true when it is and give an error message. +bool curbuf_locked(void) { if (curbuf->b_ro_locked > 0) { emsg(_("E788: Not allowed to edit another buffer now")); - return TRUE; + return true; } return allbuf_locked(); } -/* - * Check if "allbuf_lock" is set and return TRUE when it is and give an error - * message. - */ -int allbuf_locked(void) +// Check if "allbuf_lock" is set and return true when it is and give an error +// message. +bool allbuf_locked(void) { if (allbuf_lock > 0) { emsg(_("E811: Not allowed to change buffer information now")); - return TRUE; + return true; } - return FALSE; + return false; } static int cmdline_charsize(int idx) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index c90115d796..24428c2d9a 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1851,7 +1851,7 @@ failed: msg_scrolled_ign = true; if (!read_stdin && !read_buffer) { - p = (char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0); + p = (char_u *)msg_trunc_attr((char *)IObuff, false, 0); } if (read_stdin || read_buffer || restart_edit != 0 diff --git a/src/nvim/fold.h b/src/nvim/fold.h index a96ea8a039..6b29214760 100644 --- a/src/nvim/fold.h +++ b/src/nvim/fold.h @@ -14,10 +14,10 @@ */ typedef struct foldinfo { linenr_T fi_lnum; // line number where fold starts - int fi_level; /* level of the fold; when this is zero the - other fields are invalid */ - int fi_low_level; /* lowest fold level that starts in the same - line */ + int fi_level; // level of the fold; when this is zero the + // other fields are invalid + int fi_low_level; // lowest fold level that starts in the same + // line long fi_lines; } foldinfo_T; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 2f4b59837a..27e8cb36af 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1680,6 +1680,325 @@ int char_avail(void) return retval != NUL; } +typedef enum { + map_result_fail, // failed, break loop + map_result_get, // get a character from typeahead + map_result_retry, // try to map again + map_result_nomatch // no matching mapping, get char +} map_result_T; + +/// Handle mappings in the typeahead buffer. +/// - When something was mapped, return map_result_retry for recursive mappings. +/// - When nothing mapped and typeahead has a character: return map_result_get. +/// - When there is no match yet, return map_result_nomatch, need to get more +/// typeahead. +static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) +{ + mapblock_T *mp = NULL; + mapblock_T *mp2; + mapblock_T *mp_match; + int mp_match_len = 0; + int max_mlen = 0; + int tb_c1; + int mlen; + int nolmaplen; + int keylen = *keylenp; + int i; + int local_State = get_real_state(); + + // Check for a mappable key sequence. + // Walk through one maphash[] list until we find an entry that matches. + // + // Don't look for mappings if: + // - no_mapping set: mapping disabled (e.g. for CTRL-V) + // - maphash_valid not set: no mappings present. + // - typebuf.tb_buf[typebuf.tb_off] should not be remapped + // - in insert or cmdline mode and 'paste' option set + // - waiting for "hit return to continue" and CR or SPACE typed + // - waiting for a char with --more-- + // - in Ctrl-X mode, and we get a valid char for that mode + tb_c1 = typebuf.tb_buf[typebuf.tb_off]; + if (no_mapping == 0 && maphash_valid + && (no_zero_mapping == 0 || tb_c1 != '0') + && (typebuf.tb_maplen == 0 + || (p_remap + && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)))) + && !(p_paste && (State & (INSERT + CMDLINE))) + && !(State == HITRETURN && (tb_c1 == CAR || tb_c1 == ' ')) + && State != ASKMORE + && State != CONFIRM + && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(tb_c1)) + || ((compl_cont_status & CONT_LOCAL) + && (tb_c1 == Ctrl_N || tb_c1 == Ctrl_P)))) { + if (tb_c1 == K_SPECIAL) { + nolmaplen = 2; + } else { + LANGMAP_ADJUST(tb_c1, !(State & (CMDLINE | INSERT)) && get_real_state() != SELECTMODE); + nolmaplen = 0; + } + // First try buffer-local mappings. + mp = curbuf->b_maphash[MAP_HASH(local_State, tb_c1)]; + mp2 = maphash[MAP_HASH(local_State, tb_c1)]; + if (mp == NULL) { + // There are no buffer-local mappings. + mp = mp2; + mp2 = NULL; + } + // Loop until a partly matching mapping is found or all (local) + // mappings have been checked. + // The longest full match is remembered in "mp_match". + // A full match is only accepted if there is no partly match, so "aa" + // and "aaa" can both be mapped. + mp_match = NULL; + mp_match_len = 0; + for (; mp != NULL; mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : (mp = mp->m_next)) { + // Only consider an entry if the first character matches and it is + // for the current state. + // Skip ":lmap" mappings if keys were mapped. + if (mp->m_keys[0] == tb_c1 && (mp->m_mode & local_State) + && ((mp->m_mode & LANGMAP) == 0 || typebuf.tb_maplen == 0)) { + int nomap = nolmaplen; + int c2; + // find the match length of this mapping + for (mlen = 1; mlen < typebuf.tb_len; mlen++) { + c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; + if (nomap > 0) { + nomap--; + } else if (c2 == K_SPECIAL) { + nomap = 2; + } else { + LANGMAP_ADJUST(c2, true); + } + if (mp->m_keys[mlen] != c2) { + break; + } + } + + // Don't allow mapping the first byte(s) of a multi-byte char. + // Happens when mapping <M-a> and then changing 'encoding'. + // Beware that 0x80 is escaped. + char_u *p1 = mp->m_keys; + char_u *p2 = (char_u *)mb_unescape((const char **)&p1); + + if (p2 != NULL && MB_BYTE2LEN(tb_c1) > utfc_ptr2len(p2)) { + mlen = 0; + } + + // Check an entry whether it matches. + // - Full match: mlen == keylen + // - Partly match: mlen == typebuf.tb_len + keylen = mp->m_keylen; + if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) { + char_u *s; + int n; + + // If only script-local mappings are allowed, check if the + // mapping starts with K_SNR. + s = typebuf.tb_noremap + typebuf.tb_off; + if (*s == RM_SCRIPT + && (mp->m_keys[0] != K_SPECIAL + || mp->m_keys[1] != KS_EXTRA + || mp->m_keys[2] != KE_SNR)) { + continue; + } + + // If one of the typed keys cannot be remapped, skip the entry. + for (n = mlen; --n >= 0;) { + if (*s++ & (RM_NONE|RM_ABBR)) { + break; + } + } + if (n >= 0) { + continue; + } + + if (keylen > typebuf.tb_len) { + if (!*timedout && !(mp_match != NULL && mp_match->m_nowait)) { + // break at a partly match + keylen = KEYLEN_PART_MAP; + break; + } + } else if (keylen > mp_match_len + || (keylen == mp_match_len + && mp_match != NULL + && (mp_match->m_mode & LANGMAP) == 0 + && (mp->m_mode & LANGMAP) != 0)) { + // found a longer match + mp_match = mp; + mp_match_len = keylen; + } + } else { + // No match; may have to check for termcode at next character. + if (max_mlen < mlen) { + max_mlen = mlen; + } + } + } + } + + // If no partly match found, use the longest full match. + if (keylen != KEYLEN_PART_MAP) { + mp = mp_match; + keylen = mp_match_len; + } + } + + // Check for match with 'pastetoggle' + if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { + bool match = typebuf_match_len(p_pt, &mlen); + if (match) { + // write chars to script file(s) + if (mlen > typebuf.tb_maplen) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(mlen - typebuf.tb_maplen)); + } + + del_typebuf(mlen, 0); // remove the chars + set_option_value("paste", !p_paste, NULL, 0); + if (!(State & INSERT)) { + msg_col = 0; + msg_row = Rows - 1; + msg_clr_eos(); // clear ruler + } + status_redraw_all(); + redraw_statuslines(); + showmode(); + setcursor(); + *keylenp = keylen; + return map_result_retry; + } + // Need more chars for partly match. + if (mlen == typebuf.tb_len) { + keylen = KEYLEN_PART_KEY; + } else if (max_mlen < mlen) { + // no match, may have to check for termcode at next character + max_mlen = mlen + 1; + } + } + + if ((mp == NULL || max_mlen >= mp_match_len) && keylen != KEYLEN_PART_MAP) { + // No matching mapping found or found a non-matching mapping that + // matches at least what the matching mapping matched + keylen = 0; + (void)keylen; // suppress clang/dead assignment + // If there was no mapping, use the character from the typeahead + // buffer right here. Otherwise, use the mapping (loop around). + if (mp == NULL) { + *keylenp = keylen; + return map_result_get; // got character, break for loop + } else { + keylen = mp_match_len; + } + } + + // complete match + if (keylen >= 0 && keylen <= typebuf.tb_len) { + char_u *map_str; + int save_m_expr; + int save_m_noremap; + int save_m_silent; + + // Write chars to script file(s). + // Note: :lmap mappings are written *after* being applied. #5658 + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { + gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, + (size_t)(keylen - typebuf.tb_maplen)); + } + + cmd_silent = (typebuf.tb_silent > 0); + del_typebuf(keylen, 0); // remove the mapped keys + + // Put the replacement string in front of mapstr. + // The depth check catches ":map x y" and ":map y x". + if (++*mapdepth >= p_mmd) { + emsg(_("E223: recursive mapping")); + if (State & CMDLINE) { + redrawcmdline(); + } else { + setcursor(); + } + flush_buffers(FLUSH_MINIMAL); + *mapdepth = 0; // for next one + *keylenp = keylen; + return map_result_fail; + } + + // In Select mode and a Visual mode mapping is used: Switch to Visual + // mode temporarily. Append K_SELECT to switch back to Select mode. + if (VIsual_active && VIsual_select && (mp->m_mode & VISUAL)) { + VIsual_select = false; + (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); + } + + // Copy the values from *mp that are used, because evaluating the + // expression may invoke a function that redefines the mapping, thereby + // making *mp invalid. + save_m_expr = mp->m_expr; + save_m_noremap = mp->m_noremap; + save_m_silent = mp->m_silent; + char_u *save_m_keys = NULL; // only saved when needed + char_u *save_m_str = NULL; // only saved when needed + + // Handle ":map <expr>": evaluate the {rhs} as an + // expression. Also save and restore the command line + // for "normal :". + if (mp->m_expr) { + int save_vgetc_busy = vgetc_busy; + const bool save_may_garbage_collect = may_garbage_collect; + + vgetc_busy = 0; + may_garbage_collect = false; + + save_m_keys = vim_strsave(mp->m_keys); + save_m_str = vim_strsave(mp->m_str); + map_str = eval_map_expr(save_m_str, NUL); + vgetc_busy = save_vgetc_busy; + may_garbage_collect = save_may_garbage_collect; + } else { + map_str = mp->m_str; + } + + // Insert the 'to' part in the typebuf.tb_buf. + // If 'from' field is the same as the start of the 'to' field, don't + // remap the first character (but do allow abbreviations). + // If m_noremap is set, don't remap the whole 'to' part. + if (map_str == NULL) { + i = FAIL; + } else { + int noremap; + + // If this is a LANGMAP mapping, then we didn't record the keys + // at the start of the function and have to record them now. + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { + gotchars(map_str, STRLEN(map_str)); + } + + if (save_m_noremap != REMAP_YES) { + noremap = save_m_noremap; + } else if (STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys, + (size_t)keylen) != 0) { + noremap = REMAP_YES; + } else { + noremap = REMAP_SKIP; + } + i = ins_typebuf(map_str, noremap, 0, true, cmd_silent || save_m_silent); + if (save_m_expr) { + xfree(map_str); + } + } + xfree(save_m_keys); + xfree(save_m_str); + *keylenp = keylen; + if (i == FAIL) { + return map_result_fail; + } + return map_result_retry; + } + + *keylenp = keylen; + return map_result_nomatch; +} + // unget one character (can only be done once!) void vungetc(int c) { @@ -1715,44 +2034,27 @@ void vungetc(int c) static int vgetorpeek(bool advance) { int c, c1; - int keylen; - char_u *s; - mapblock_T *mp; - mapblock_T *mp2; - mapblock_T *mp_match; - int mp_match_len = 0; - bool timedout = false; // waited for more than 1 second - // for mapping to complete + bool timedout = false; // waited for more than 1 second for mapping to complete int mapdepth = 0; // check for recursive mapping bool mode_deleted = false; // set when mode has been deleted - int local_State; - int mlen; - int max_mlen; - int i; int new_wcol, new_wrow; int n; - int nolmaplen; int old_wcol, old_wrow; int wait_tb_len; - /* - * This function doesn't work very well when called recursively. This may - * happen though, because of: - * 1. The call to add_to_showcmd(). char_avail() is then used to check if - * there is a character available, which calls this function. In that - * case we must return NUL, to indicate no character is available. - * 2. A GUI callback function writes to the screen, causing a - * wait_return(). - * Using ":normal" can also do this, but it saves the typeahead buffer, - * thus it should be OK. But don't get a key from the user then. - */ - if (vgetc_busy > 0 - && ex_normal_busy == 0) { + // This function doesn't work very well when called recursively. This may + // happen though, because of: + // 1. The call to add_to_showcmd(). char_avail() is then used to check if + // there is a character available, which calls this function. In that + // case we must return NUL, to indicate no character is available. + // 2. A GUI callback function writes to the screen, causing a + // wait_return(). + // Using ":normal" can also do this, but it saves the typeahead buffer, + // thus it should be OK. But don't get a key from the user then. + if (vgetc_busy > 0 && ex_normal_busy == 0) { return NUL; } - local_State = get_real_state(); - ++vgetc_busy; if (advance) { @@ -1765,9 +2067,7 @@ static int vgetorpeek(bool advance) reg_executing = 0; } do { - /* - * get a character: 1. from the stuffbuffer - */ + // get a character: 1. from the stuffbuffer if (typeahead_char != 0) { c = typeahead_char; if (advance) { @@ -1784,30 +2084,27 @@ static int vgetorpeek(bool advance) KeyStuffed = true; } if (typebuf.tb_no_abbr_cnt == 0) { - typebuf.tb_no_abbr_cnt = 1; // no abbreviations now + typebuf.tb_no_abbr_cnt = 1; // no abbreviations now } } else { - /* - * Loop until we either find a matching mapped key, or we - * are sure that it is not a mapped key. - * If a mapped key sequence is found we go back to the start to - * try re-mapping. - */ + // Loop until we either find a matching mapped key, or we + // are sure that it is not a mapped key. + // If a mapped key sequence is found we go back to the start to + // try re-mapping. for (;;) { - /* - * os_breakcheck() is slow, don't use it too often when - * inside a mapping. But call it each time for typed - * characters. - */ + // os_breakcheck() is slow, don't use it too often when + // inside a mapping. But call it each time for typed + // characters. if (typebuf.tb_maplen) { line_breakcheck(); } else { - os_breakcheck(); // check for CTRL-C + os_breakcheck(); // check for CTRL-C } - keylen = 0; + int keylen = 0; if (got_int) { // flush all input c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L); + // If inchar() returns TRUE (script file was active) or we // are inside a mapping, get out of Insert mode. // Otherwise we behave like having gotten a CTRL-C. @@ -1831,361 +2128,49 @@ static int vgetorpeek(bool advance) break; } else if (typebuf.tb_len > 0) { - /* - * Check for a mappable key sequence. - * Walk through one maphash[] list until we find an - * entry that matches. - * - * Don't look for mappings if: - * - no_mapping set: mapping disabled (e.g. for CTRL-V) - * - maphash_valid not set: no mappings present. - * - typebuf.tb_buf[typebuf.tb_off] should not be remapped - * - in insert or cmdline mode and 'paste' option set - * - waiting for "hit return to continue" and CR or SPACE - * typed - * - waiting for a char with --more-- - * - in Ctrl-X mode, and we get a valid char for that mode - */ - mp = NULL; - max_mlen = 0; - c1 = typebuf.tb_buf[typebuf.tb_off]; - if (no_mapping == 0 && maphash_valid - && (no_zero_mapping == 0 || c1 != '0') - && (typebuf.tb_maplen == 0 - || (p_remap - && (typebuf.tb_noremap[typebuf.tb_off] - & (RM_NONE|RM_ABBR)) == 0)) - && !(p_paste && (State & (INSERT + CMDLINE))) - && !(State == HITRETURN && (c1 == CAR || c1 == ' ')) - && State != ASKMORE - && State != CONFIRM - && !((ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c1)) - || ((compl_cont_status & CONT_LOCAL) - && (c1 == Ctrl_N - || c1 == Ctrl_P)))) { - if (c1 == K_SPECIAL) { - nolmaplen = 2; - } else { - LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0 - && get_real_state() != SELECTMODE); - nolmaplen = 0; - } - // First try buffer-local mappings. - mp = curbuf->b_maphash[MAP_HASH(local_State, c1)]; - mp2 = maphash[MAP_HASH(local_State, c1)]; - if (mp == NULL) { - // There are no buffer-local mappings. - mp = mp2; - mp2 = NULL; - } - /* - * Loop until a partly matching mapping is found or - * all (local) mappings have been checked. - * The longest full match is remembered in "mp_match". - * A full match is only accepted if there is no partly - * match, so "aa" and "aaa" can both be mapped. - */ - mp_match = NULL; - mp_match_len = 0; - for (; mp != NULL; - mp->m_next == NULL ? (mp = mp2, mp2 = NULL) : - (mp = mp->m_next)) { - /* - * Only consider an entry if the first character - * matches and it is for the current state. - * Skip ":lmap" mappings if keys were mapped. - */ - if (mp->m_keys[0] == c1 - && (mp->m_mode & local_State) - && ((mp->m_mode & LANGMAP) == 0 - || typebuf.tb_maplen == 0)) { - int nomap = nolmaplen; - int c2; - // find the match length of this mapping - for (mlen = 1; mlen < typebuf.tb_len; mlen++) { - c2 = typebuf.tb_buf[typebuf.tb_off + mlen]; - if (nomap > 0) { - --nomap; - } else if (c2 == K_SPECIAL) { - nomap = 2; - } else { - LANGMAP_ADJUST(c2, TRUE); - } - if (mp->m_keys[mlen] != c2) { - break; - } - } + // Check for a mapping in "typebuf". + map_result_T result = (map_result_T)handle_mapping(&keylen, &timedout, &mapdepth); - /* Don't allow mapping the first byte(s) of a - * multi-byte char. Happens when mapping - * <M-a> and then changing 'encoding'. Beware - * that 0x80 is escaped. */ - char_u *p1 = mp->m_keys; - char_u *p2 = (char_u *)mb_unescape((const char **)&p1); - - if (p2 != NULL && MB_BYTE2LEN(c1) > utfc_ptr2len(p2)) { - mlen = 0; - } - - // Check an entry whether it matches. - // - Full match: mlen == keylen - // - Partly match: mlen == typebuf.tb_len - keylen = mp->m_keylen; - if (mlen == keylen - || (mlen == typebuf.tb_len - && typebuf.tb_len < keylen)) { - /* - * If only script-local mappings are - * allowed, check if the mapping starts - * with K_SNR. - */ - s = typebuf.tb_noremap + typebuf.tb_off; - if (*s == RM_SCRIPT - && (mp->m_keys[0] != K_SPECIAL - || mp->m_keys[1] != KS_EXTRA - || mp->m_keys[2] - != KE_SNR)) { - continue; - } - /* - * If one of the typed keys cannot be - * remapped, skip the entry. - */ - for (n = mlen; --n >= 0;) { - if (*s++ & (RM_NONE|RM_ABBR)) { - break; - } - } - if (n >= 0) { - continue; - } - - if (keylen > typebuf.tb_len) { - if (!timedout && !(mp_match != NULL - && mp_match->m_nowait)) { - // break at a partly match - keylen = KEYLEN_PART_MAP; - break; - } - } else if (keylen > mp_match_len - || (keylen == mp_match_len - && mp_match != NULL - && (mp_match->m_mode & LANGMAP) == 0 - && (mp->m_mode & LANGMAP) != 0)) { - // found a longer match - mp_match = mp; - mp_match_len = keylen; - } - } else { - // No match; may have to check for termcode at next character. - if (max_mlen < mlen) { - max_mlen = mlen; - } - } - } - } - - /* If no partly match found, use the longest full - * match. */ - if (keylen != KEYLEN_PART_MAP) { - mp = mp_match; - keylen = mp_match_len; - } - } - - if (*p_pt != NUL && mp == NULL && (State & (INSERT|NORMAL))) { - bool match = typebuf_match_len(p_pt, &mlen); - if (match) { - // write chars to script file(s) - if (mlen > typebuf.tb_maplen) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(mlen - typebuf.tb_maplen)); - } - - del_typebuf(mlen, 0); // Remove the chars. - set_option_value("paste", !p_paste, NULL, 0); - if (!(State & INSERT)) { - msg_col = 0; - msg_row = Rows - 1; - msg_clr_eos(); // clear ruler - } - status_redraw_all(); - redraw_statuslines(); - showmode(); - setcursor(); - continue; - } - // Need more chars for partly match. - if (mlen == typebuf.tb_len) { - keylen = KEYLEN_PART_KEY; - } else if (max_mlen < mlen) { - // no match, may have to check for termcode at - // next character - max_mlen = mlen + 1; - } + if (result == map_result_retry) { + // try mapping again + continue; } - if ((mp == NULL || max_mlen >= mp_match_len) - && keylen != KEYLEN_PART_MAP) { - // No matching mapping found or found a non-matching mapping that - // matches at least what the matching mapping matched - keylen = 0; - (void)keylen; // suppress clang/dead assignment - // If there was no mapping, use the character from the typeahead - // buffer right here. Otherwise, use the mapping (loop around). - if (mp == NULL) { - // get a character: 2. from the typeahead buffer - c = typebuf.tb_buf[typebuf.tb_off] & 255; - if (advance) { // remove chars from tb_buf - cmd_silent = (typebuf.tb_silent > 0); - if (typebuf.tb_maplen > 0) { - KeyTyped = false; - } else { - KeyTyped = true; - // write char to script file(s) - gotchars(typebuf.tb_buf + typebuf.tb_off, 1); - } - KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; - del_typebuf(1, 0); - } - break; // got character, break for loop - } else { - keylen = mp_match_len; - } + if (result == map_result_fail) { + // failed, use the outer loop + c = -1; + break; } - // complete match - if (keylen >= 0 && keylen <= typebuf.tb_len) { - int save_m_expr; - int save_m_noremap; - int save_m_silent; - - // Write chars to script file(s) - // Note: :lmap mappings are written *after* being applied. #5658 - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { - gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, - (size_t)(keylen - typebuf.tb_maplen)); - } - - cmd_silent = (typebuf.tb_silent > 0); - del_typebuf(keylen, 0); // remove the mapped keys - - /* - * Put the replacement string in front of mapstr. - * The depth check catches ":map x y" and ":map y x". - */ - if (++mapdepth >= p_mmd) { - emsg(_("E223: recursive mapping")); - if (State & CMDLINE) { - redrawcmdline(); + if (result == map_result_get) { + // get a character: 2. from the typeahead buffer + c = typebuf.tb_buf[typebuf.tb_off] & 255; + if (advance) { // remove chars from tb_buf + cmd_silent = (typebuf.tb_silent > 0); + if (typebuf.tb_maplen > 0) { + KeyTyped = false; } else { - setcursor(); + KeyTyped = true; + // write char to script file(s) + gotchars(typebuf.tb_buf + typebuf.tb_off, 1); } - flush_buffers(FLUSH_MINIMAL); - mapdepth = 0; // for next one - c = -1; - break; - } - - /* - * In Select mode and a Visual mode mapping is used: - * Switch to Visual mode temporarily. Append K_SELECT - * to switch back to Select mode. - */ - if (VIsual_active && VIsual_select - && (mp->m_mode & VISUAL)) { - VIsual_select = false; - (void)ins_typebuf(K_SELECT_STRING, REMAP_NONE, 0, true, false); - } - - /* Copy the values from *mp that are used, because - * evaluating the expression may invoke a function - * that redefines the mapping, thereby making *mp - * invalid. */ - save_m_expr = mp->m_expr; - save_m_noremap = mp->m_noremap; - save_m_silent = mp->m_silent; - char_u *save_m_keys = NULL; // only saved when needed - char_u *save_m_str = NULL; // only saved when needed - - /* - * Handle ":map <expr>": evaluate the {rhs} as an - * expression. Also save and restore the command line - * for "normal :". - */ - if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; - const bool save_may_garbage_collect = may_garbage_collect; - - vgetc_busy = 0; - may_garbage_collect = false; - - save_m_keys = vim_strsave(mp->m_keys); - save_m_str = vim_strsave(mp->m_str); - s = eval_map_expr(save_m_str, NUL); - vgetc_busy = save_vgetc_busy; - may_garbage_collect = save_may_garbage_collect; - } else { - s = mp->m_str; + KeyNoremap = typebuf.tb_noremap[typebuf.tb_off]; + del_typebuf(1, 0); } - - /* - * Insert the 'to' part in the typebuf.tb_buf. - * If 'from' field is the same as the start of the - * 'to' field, don't remap the first character (but do - * allow abbreviations). - * If m_noremap is set, don't remap the whole 'to' - * part. - */ - if (s == NULL) { - i = FAIL; - } else { - int noremap; - - // If this is a LANGMAP mapping, then we didn't record the keys - // at the start of the function and have to record them now. - if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { - gotchars(s, STRLEN(s)); - } - - if (save_m_noremap != REMAP_YES) { - noremap = save_m_noremap; - } else if ( - STRNCMP(s, save_m_keys != NULL - ? save_m_keys : mp->m_keys, - (size_t)keylen) - != 0) { - noremap = REMAP_YES; - } else { - noremap = REMAP_SKIP; - } - i = ins_typebuf(s, noremap, - 0, TRUE, cmd_silent || save_m_silent); - if (save_m_expr) { - xfree(s); - } - } - xfree(save_m_keys); - xfree(save_m_str); - if (i == FAIL) { - c = -1; - break; - } - continue; + break; } + + // not enough characters, get more } - /* - * get a character: 3. from the user - handle <Esc> in Insert mode - */ - /* - * special case: if we get an <ESC> in insert mode and there - * are no more characters at once, we pretend to go out of - * insert mode. This prevents the one second delay after - * typing an <ESC>. If we get something after all, we may - * have to redisplay the mode. That the cursor is in the wrong - * place does not matter. - */ + // get a character: 3. from the user - handle <Esc> in Insert mode + + // special case: if we get an <ESC> in insert mode and there + // are no more characters at once, we pretend to go out of + // insert mode. This prevents the one second delay after + // typing an <ESC>. If we get something after all, we may + // have to redisplay the mode. That the cursor is in the wrong + // place does not matter. c = 0; new_wcol = curwin->w_wcol; new_wrow = curwin->w_wrow; @@ -2196,10 +2181,8 @@ static int vgetorpeek(bool advance) && ex_normal_busy == 0 && typebuf.tb_maplen == 0 && (State & INSERT) - && (p_timeout - || (keylen == KEYLEN_PART_KEY && p_ttimeout)) - && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, - 3, 25L)) == 0) { + && (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout)) + && (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) { colnr_T col = 0, vcol; char_u *ptr; @@ -2215,11 +2198,9 @@ static int vgetorpeek(bool advance) if (curwin->w_cursor.col != 0) { if (curwin->w_wcol > 0) { if (did_ai) { - /* - * We are expecting to truncate the trailing - * white-space, so find the last non-white - * character -- webb - */ + // We are expecting to truncate the trailing + // white-space, so find the last non-white + // character -- webb col = vcol = curwin->w_wcol = 0; ptr = get_cursor_line_ptr(); while (col < curwin->w_cursor.col) { @@ -2233,7 +2214,7 @@ static int vgetorpeek(bool advance) + curwin->w_wcol / curwin->w_width_inner; curwin->w_wcol %= curwin->w_width_inner; curwin->w_wcol += curwin_col_off(); - col = 0; // no correction needed + col = 0; // no correction needed } else { --curwin->w_wcol; col = curwin->w_cursor.col - 1; @@ -2261,7 +2242,7 @@ static int vgetorpeek(bool advance) curwin->w_wrow = old_wrow; } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } // Allow mapping for just typed characters. When we get here c @@ -2307,9 +2288,8 @@ static int vgetorpeek(bool advance) break; } - /* - * get a character: 3. from the user - update display - */ + // get a character: 3. from the user - update display + // In insert mode a screen update is skipped when characters // are still available. But when those available characters // are part of a mapping, and we are going to do a blocking @@ -2320,26 +2300,21 @@ static int vgetorpeek(bool advance) if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 && advance && must_redraw != 0 && !need_wait_return) { update_screen(0); - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } - /* - * If we have a partial match (and are going to wait for more - * input from the user), show the partially matched characters - * to the user with showcmd. - */ - i = 0; + // If we have a partial match (and are going to wait for more + // input from the user), show the partially matched characters + // to the user with showcmd. + int showcmd_idx = 0; c1 = 0; if (typebuf.tb_len > 0 && advance && !exmode_active) { - if (((State & (NORMAL | INSERT)) || State == LANGMAP) - && State != HITRETURN) { + if (((State & (NORMAL | INSERT)) || State == LANGMAP) && State != HITRETURN) { // this looks nice when typing a dead character map if (State & INSERT - && ptr2cells(typebuf.tb_buf + typebuf.tb_off - + typebuf.tb_len - 1) == 1) { - edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], - false); - setcursor(); // put cursor back where it belongs + && ptr2cells(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len - 1) == 1) { + edit_putchar(typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len - 1], false); + setcursor(); // put cursor back where it belongs c1 = 1; } // need to use the col and row from above here @@ -2349,11 +2324,10 @@ static int vgetorpeek(bool advance) curwin->w_wrow = new_wrow; push_showcmd(); if (typebuf.tb_len > SHOWCMD_COLS) { - i = typebuf.tb_len - SHOWCMD_COLS; + showcmd_idx = typebuf.tb_len - SHOWCMD_COLS; } - while (i < typebuf.tb_len) { - (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off - + i++]); + while (showcmd_idx < typebuf.tb_len) { + (void)add_to_showcmd(typebuf.tb_buf[typebuf.tb_off + showcmd_idx++]); } curwin->w_wcol = old_wcol; curwin->w_wrow = old_wrow; @@ -2369,9 +2343,7 @@ static int vgetorpeek(bool advance) } } - /* - * get a character: 3. from the user - get it - */ + // get a character: 3. from the user - get it if (typebuf.tb_len == 0) { // timedout may have been set while waiting for a mapping // that has a <Nop> RHS. @@ -2381,8 +2353,7 @@ static int vgetorpeek(bool advance) long wait_time = 0; if (advance) { - if (typebuf.tb_len == 0 - || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { + if (typebuf.tb_len == 0 || !(p_timeout || (p_ttimeout && keylen == KEYLEN_PART_KEY))) { // blocking wait wait_time = -1L; } else if (keylen == KEYLEN_PART_KEY && p_ttm >= 0) { @@ -2397,7 +2368,7 @@ static int vgetorpeek(bool advance) typebuf.tb_buflen - typebuf.tb_off - typebuf.tb_len - 1, wait_time); - if (i != 0) { + if (showcmd_idx != 0) { pop_showcmd(); } if (c1 == 1) { @@ -2407,47 +2378,45 @@ static int vgetorpeek(bool advance) if (State & CMDLINE) { unputcmdline(); } else { - setcursor(); // put cursor back where it belongs + setcursor(); // put cursor back where it belongs } } if (c < 0) { - continue; // end of input script reached + continue; // end of input script reached } - if (c == NUL) { // no character available + if (c == NUL) { // no character available if (!advance) { break; } - if (wait_tb_len > 0) { // timed out + if (wait_tb_len > 0) { // timed out timedout = true; continue; } - } else { // allow mapping for just typed characters + } else { // allow mapping for just typed characters while (typebuf.tb_buf[typebuf.tb_off + typebuf.tb_len] != NUL) { typebuf.tb_noremap[typebuf.tb_off + typebuf.tb_len++] = RM_YES; } } - } // for (;;) - } // if (!character from stuffbuf) + } // for (;;) + } // if (!character from stuffbuf) // if advance is false don't loop on NULs } while (c < 0 || (advance && c == NUL)); - /* - * The "INSERT" message is taken care of here: - * if we return an ESC to exit insert mode, the message is deleted - * if we don't return an ESC but deleted the message before, redisplay it - */ + // The "INSERT" message is taken care of here: + // if we return an ESC to exit insert mode, the message is deleted + // if we don't return an ESC but deleted the message before, redisplay it if (advance && p_smd && msg_silent == 0 && (State & INSERT)) { if (c == ESC && !mode_deleted && !no_mapping && mode_displayed) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // delete mode later + redraw_cmdline = true; // delete mode later } else { unshowmode(false); } } else if (c != ESC && mode_deleted) { if (typebuf.tb_len && !KeyTyped) { - redraw_cmdline = true; // show mode later + redraw_cmdline = true; // show mode later } else { showmode(); } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 7ae7b65702..b2422fd531 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -4,6 +4,7 @@ #include <inttypes.h> #include <stdbool.h> +#include "nvim/ascii.h" #include "nvim/event/loop.h" #include "nvim/ex_eval.h" #include "nvim/iconv.h" @@ -533,6 +534,11 @@ EXTERN int VIsual_mode INIT(= 'v'); /// true when redoing Visual. EXTERN int redo_VIsual_busy INIT(= false); +// The Visual area is remembered for reselection. +EXTERN int resel_VIsual_mode INIT(= NUL); // 'v', 'V', or Ctrl-V +EXTERN linenr_T resel_VIsual_line_count; // number of lines +EXTERN colnr_T resel_VIsual_vcol; // nr of cols or end col + /// When pasting text with the middle mouse button in visual mode with /// restart_edit set, remember where it started so we can set Insstart. EXTERN pos_T where_paste_started; diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 93fcdc55a6..6fc70144ac 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -1253,7 +1253,7 @@ static struct prt_dsc_comment_S prt_dsc_table[] = * Variables for the output PostScript file. */ static FILE *prt_ps_fd; -static int prt_file_error; +static bool prt_file_error; static char_u *prt_ps_file_name = NULL; /* @@ -1329,7 +1329,7 @@ static void prt_write_file_raw_len(char_u *buffer, size_t bytes) if (!prt_file_error && fwrite(buffer, sizeof(char_u), bytes, prt_ps_fd) != bytes) { emsg(_("E455: Error writing to PostScript output file")); - prt_file_error = TRUE; + prt_file_error = true; } } @@ -1981,7 +1981,7 @@ void mch_print_cleanup(void) if (prt_ps_fd != NULL) { fclose(prt_ps_fd); prt_ps_fd = NULL; - prt_file_error = FALSE; + prt_file_error = false; } if (prt_ps_file_name != NULL) { XFREE_CLEAR(prt_ps_file_name); @@ -2203,7 +2203,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Check encoding and character set are compatible if ((p_mbenc->needs_charset & p_mbchar->has_charset) == 0) { emsg(_("E673: Incompatible multi-byte encoding and character set.")); - return FALSE; + return false; } // Add charset name if not empty @@ -2215,7 +2215,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) // Add custom CMap character set name if (*p_pmcs == NUL) { emsg(_("E674: printmbcharset cannot be empty with multi-byte encoding.")); - return FALSE; + return false; } STRLCPY(prt_cmap, p_pmcs, sizeof(prt_cmap) - 2); STRCAT(prt_cmap, "-"); @@ -2231,7 +2231,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) if (!mbfont_opts[OPT_MBFONT_REGULAR].present) { emsg(_("E675: No default font specified for multi-byte printing.")); - return FALSE; + return false; } // Derive CID font names with fallbacks if not defined @@ -2425,12 +2425,12 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit) prt_need_bgcol = false; prt_need_underline = false; - prt_file_error = FALSE; + prt_file_error = false; return OK; } -static int prt_add_resource(struct prt_ps_resource_S *resource) +static bool prt_add_resource(struct prt_ps_resource_S *resource) { FILE *fd_resource; char_u resource_buffer[512]; @@ -2439,7 +2439,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) fd_resource = os_fopen((char *)resource->filename, READBIN); if (fd_resource == NULL) { semsg(_("E456: Can't open file \"%s\""), resource->filename); - return FALSE; + return false; } switch (resource->type) { case PRT_RESOURCE_TYPE_PROCSET: @@ -2449,7 +2449,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) (char *)resource->title); break; default: - return FALSE; + return false; } prt_dsc_textline("BeginDocument", (char *)resource->filename); @@ -2461,7 +2461,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) semsg(_("E457: Can't read PostScript resource file \"%s\""), resource->filename); fclose(fd_resource); - return FALSE; + return false; } if (bytes_read == 0) { break; @@ -2469,7 +2469,7 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_write_file_raw_len(resource_buffer, bytes_read); if (prt_file_error) { fclose(fd_resource); - return FALSE; + return false; } } fclose(fd_resource); @@ -2478,10 +2478,10 @@ static int prt_add_resource(struct prt_ps_resource_S *resource) prt_dsc_noarg("EndResource"); - return TRUE; + return true; } -int mch_print_begin(prt_settings_T *psettings) +bool mch_print_begin(prt_settings_T *psettings) { int bbox[4]; double left; @@ -2495,7 +2495,6 @@ int mch_print_begin(prt_settings_T *psettings) char_u *p; struct prt_ps_resource_S res_cidfont; struct prt_ps_resource_S res_cmap; - int retval = FALSE; /* * PS DSC Header comments - no PS code! @@ -2567,25 +2566,25 @@ int mch_print_begin(prt_settings_T *psettings) // Search for external resources VIM supplies if (!prt_find_resource("prolog", &res_prolog)) { emsg(_("E456: Can't find PostScript resource file \"prolog.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_prolog)) { - return FALSE; + return false; } if (!prt_check_resource(&res_prolog, PRT_PROLOG_VERSION)) { - return FALSE; + return false; } if (prt_out_mbyte) { // Look for required version of multi-byte printing procset if (!prt_find_resource("cidfont", &res_cidfont)) { emsg(_("E456: Can't find PostScript resource file \"cidfont.ps\"")); - return FALSE; + return false; } if (!prt_open_resource(&res_cidfont)) { - return FALSE; + return false; } if (!prt_check_resource(&res_cidfont, PRT_CID_PROLOG_VERSION)) { - return FALSE; + return false; } } @@ -2610,12 +2609,12 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(p_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), p_encoding); - return FALSE; + return false; } } } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform @@ -2629,10 +2628,10 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(prt_ascii_encoding, &res_encoding)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_ascii_encoding); - return FALSE; + return false; } if (!prt_open_resource(&res_encoding)) { - return FALSE; + return false; } // For the moment there are no checks on encoding resource files to // perform @@ -2655,10 +2654,10 @@ int mch_print_begin(prt_settings_T *psettings) if (!prt_find_resource(prt_cmap, &res_cmap)) { semsg(_("E456: Can't find PostScript resource file \"%s.ps\""), prt_cmap); - return FALSE; + return false; } if (!prt_open_resource(&res_cmap)) { - return FALSE; + return false; } } @@ -2736,7 +2735,7 @@ int mch_print_begin(prt_settings_T *psettings) // There will be only one Roman font encoding to be included in the PS // file. if (!prt_add_resource(&res_encoding)) { - return FALSE; + return false; } } @@ -2846,9 +2845,7 @@ int mch_print_begin(prt_settings_T *psettings) prt_dsc_noarg("EndSetup"); // Fail if any problems writing out to the PS file - retval = !prt_file_error; - - return retval; + return !prt_file_error; } void mch_print_end(prt_settings_T *psettings) diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 6f02ebfb48..daef8db267 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -223,9 +223,9 @@ void ex_cstag(exarg_T *eap) switch (p_csto) { case 0: if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); if (msg_col) { msg_putchar('\n'); @@ -249,16 +249,16 @@ void ex_cstag(exarg_T *eap) if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, - FALSE, FALSE, *eap->cmdlinep); - if (ret == FALSE) { + false, false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } } } else if (cs_check_for_connections()) { - ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, false, + false, *eap->cmdlinep); + if (ret == false) { cs_free_tags(); } } @@ -520,7 +520,7 @@ add_err: } -static int cs_check_for_connections(void) +static bool cs_check_for_connections(void) { return cs_cnt_connections() > 0; } @@ -887,20 +887,20 @@ static int cs_find(exarg_T *eap) { char *opt, *pat; - if (cs_check_for_connections() == FALSE) { + if (cs_check_for_connections() == false) { (void)emsg(_("E567: no cscope connections")); - return FALSE; + return false; } if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) { cs_usage_msg(Find); - return FALSE; + return false; } pat = opt + strlen(opt) + 1; if (pat >= (char *)eap->arg + eap_arg_len) { cs_usage_msg(Find); - return FALSE; + return false; } /* @@ -919,8 +919,8 @@ static int cs_find(exarg_T *eap) /// Common code for cscope find, shared by cs_find() and ex_cstag(). -static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, - char_u *cmdline) +static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, + bool use_ll, char_u *cmdline) { char *cmd; int *nummatches; @@ -967,7 +967,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us // next symbol must be + or - if (strchr(CSQF_FLAGS, *qfpos) == NULL) { (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1)); - return FALSE; + return false; } if (*qfpos != '0' @@ -982,7 +982,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us // create the actual command to send to cscope cmd = cs_create_cmd(opt, pat); if (cmd == NULL) { - return FALSE; + return false; } nummatches = xmalloc(sizeof(int) * csinfo_size); @@ -1019,7 +1019,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat); } xfree(nummatches); - return FALSE; + return false; } if (qfpos != NULL && *qfpos != '0') { @@ -1064,7 +1064,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us os_remove((char *)tmp); xfree(tmp); xfree(nummatches); - return TRUE; + return true; } else { char **matches = NULL, **contexts = NULL; size_t matched = 0; @@ -1073,7 +1073,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int us cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched); xfree(nummatches); if (matches == NULL) { - return FALSE; + return false; } (void)cs_manage_matches(matches, contexts, matched, Store); @@ -1499,12 +1499,13 @@ static void cs_file_results(FILE *f, int *nummatches_a) continue; } - context = xmalloc(strlen(cntx) + 5); + size_t context_len = strlen(cntx) + 5; + context = xmalloc(context_len); if (strcmp(cntx, "<global>") == 0) { - strcpy(context, "<<global>>"); + xstrlcpy(context, "<<global>>", context_len); } else { - sprintf(context, "<<%s>>", cntx); + snprintf(context, context_len, "<<%s>>", cntx); } if (search == NULL) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 6a8b70a158..a899ca63ac 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -521,6 +521,9 @@ static void nlua_print_event(void **argv) const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL for (size_t i = 0; i < len;) { + if (got_int) { + break; + } const size_t start = i; while (i < len) { switch (str[i]) { diff --git a/src/nvim/message.c b/src/nvim/message.c index 6fcd4cef8a..eaf7e2622a 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -839,13 +839,11 @@ void msg_schedule_semsg(const char *const fmt, ...) multiqueue_put(main_loop.events, msg_semsg_event, 1, s); } -/* - * Like msg(), but truncate to a single line if p_shm contains 't', or when - * "force" is TRUE. This truncates in another way as for normal messages. - * Careful: The string may be changed by msg_may_trunc()! - * Returns a pointer to the printed message, if wait_return() not called. - */ -char *msg_trunc_attr(char *s, int force, int attr) +// Like msg(), but truncate to a single line if p_shm contains 't', or when +// "force" is true. This truncates in another way as for normal messages. +// Careful: The string may be changed by msg_may_trunc()! +// Returns a pointer to the printed message, if wait_return() not called. +char *msg_trunc_attr(char *s, bool force, int attr) { int n; @@ -869,7 +867,7 @@ char *msg_trunc_attr(char *s, int force, int attr) * Return a pointer to where the truncated message starts. * Note: May change the message by replacing a character with '<'. */ -char_u *msg_may_trunc(int force, char_u *s) +char_u *msg_may_trunc(bool force, char_u *s) { int room; @@ -2056,7 +2054,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs msg_ext_last_attr = attr; } // Concat pieces with the same highlight - size_t len = strnlen((char *)str, maxlen); // -V781 + size_t len = STRNLEN(str, maxlen); // -V781 ga_concat_len(&msg_ext_last_chunk, (char *)str, len); msg_ext_cur_len += len; return; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f2b272a13f..9332c55b5f 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -31,8 +31,8 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/main.h" @@ -84,12 +84,6 @@ typedef struct normal_state { pos_T old_pos; } NormalState; -/* - * The Visual area is remembered for reselection. - */ -static int resel_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V -static linenr_T resel_VIsual_line_count; // number of lines -static colnr_T resel_VIsual_vcol; // nr of cols or end col static int VIsual_mode_orig = NUL; // saved Visual mode @@ -1455,758 +1449,6 @@ static void set_vcount_ca(cmdarg_T *cap, bool *set_prevcount) *set_prevcount = false; // only set v:prevcount once } -// Handle an operator after Visual mode or when the movement is finished. -// "gui_yank" is true when yanking text for the clipboard. -void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) -{ - oparg_T *oap = cap->oap; - pos_T old_cursor; - bool empty_region_error; - int restart_edit_save; - int lbr_saved = curwin->w_p_lbr; - - - // The visual area is remembered for redo - static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V - static linenr_T redo_VIsual_line_count; // number of lines - static colnr_T redo_VIsual_vcol; // number of cols or end column - static long redo_VIsual_count; // count for Visual operator - static int redo_VIsual_arg; // extra argument - bool include_line_break = false; - - old_cursor = curwin->w_cursor; - - /* - * If an operation is pending, handle it... - */ - if ((finish_op - || VIsual_active) - && oap->op_type != OP_NOP) { - // Yank can be redone when 'y' is in 'cpoptions', but not when yanking - // for the clipboard. - const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; - - // Avoid a problem with unwanted linebreaks in block mode - if (curwin->w_p_lbr) { - curwin->w_valid &= ~VALID_VIRTCOL; - } - curwin->w_p_lbr = false; - oap->is_VIsual = VIsual_active; - if (oap->motion_force == 'V') { - oap->motion_type = kMTLineWise; - } else if (oap->motion_force == 'v') { - // If the motion was linewise, "inclusive" will not have been set. - // Use "exclusive" to be consistent. Makes "dvj" work nice. - if (oap->motion_type == kMTLineWise) { - oap->inclusive = false; - } else if (oap->motion_type == kMTCharWise) { - // If the motion already was charwise, toggle "inclusive" - oap->inclusive = !oap->inclusive; - } - oap->motion_type = kMTCharWise; - } else if (oap->motion_force == Ctrl_V) { - // Change line- or charwise motion into Visual block mode. - if (!VIsual_active) { - VIsual_active = true; - VIsual = oap->start; - } - VIsual_mode = Ctrl_V; - VIsual_select = false; - VIsual_reselect = false; - } - - // Only redo yank when 'y' flag is in 'cpoptions'. - // Never redo "zf" (define fold). - if ((redo_yank || oap->op_type != OP_YANK) - && ((!VIsual_active || oap->motion_force) - // Also redo Operator-pending Visual mode mappings. - || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) - && oap->op_type != OP_COLON)) - && cap->cmdchar != 'D' - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search - /* - * If 'cpoptions' does not contain 'r', insert the search - * pattern to really repeat the same command. - */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) { - AppendToRedobuffLit(cap->searchbuf, -1); - } - AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { - // do_cmdline() has stored the first typed line in - // "repeat_cmdline". When several lines are typed repeating - // won't be possible. - if (repeat_cmdline == NULL) { - ResetRedobuff(); - } else { - AppendToRedobuffLit(repeat_cmdline, -1); - AppendToRedobuff(NL_STR); - XFREE_CLEAR(repeat_cmdline); - } - } - } - - if (redo_VIsual_busy) { - /* Redo of an operation on a Visual area. Use the same size from - * redo_VIsual_line_count and redo_VIsual_vcol. */ - oap->start = curwin->w_cursor; - curwin->w_cursor.lnum += redo_VIsual_line_count - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } - VIsual_mode = redo_VIsual_mode; - if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { - if (VIsual_mode == 'v') { - if (redo_VIsual_line_count <= 1) { - validate_virtcol(); - curwin->w_curswant = - curwin->w_virtcol + redo_VIsual_vcol - 1; - } else { - curwin->w_curswant = redo_VIsual_vcol; - } - } else { - curwin->w_curswant = MAXCOL; - } - coladvance(curwin->w_curswant); - } - cap->count0 = redo_VIsual_count; - cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); - } else if (VIsual_active) { - if (!gui_yank) { - // Save the current VIsual area for '< and '> marks, and "gv" - curbuf->b_visual.vi_start = VIsual; - curbuf->b_visual.vi_end = curwin->w_cursor; - curbuf->b_visual.vi_mode = VIsual_mode; - if (VIsual_mode_orig != NUL) { - curbuf->b_visual.vi_mode = VIsual_mode_orig; - VIsual_mode_orig = NUL; - } - curbuf->b_visual.vi_curswant = curwin->w_curswant; - curbuf->b_visual_mode_eval = VIsual_mode; - } - - // In Select mode, a linewise selection is operated upon like a - // charwise selection. - // Special case: gH<Del> deletes the last line. - if (VIsual_select && VIsual_mode == 'V' - && cap->oap->op_type != OP_DELETE) { - if (lt(VIsual, curwin->w_cursor)) { - VIsual.col = 0; - curwin->w_cursor.col = - (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); - } else { - curwin->w_cursor.col = 0; - VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); - } - VIsual_mode = 'v'; - } - /* If 'selection' is "exclusive", backup one character for - * charwise selections. */ - else if (VIsual_mode == 'v') { - include_line_break = - unadjust_for_sel(); - } - - oap->start = VIsual; - if (VIsual_mode == 'V') { - oap->start.col = 0; - oap->start.coladd = 0; - } - } - - /* - * Set oap->start to the first position of the operated text, oap->end - * to the end of the operated text. w_cursor is equal to oap->start. - */ - if (lt(oap->start, curwin->w_cursor)) { - // Include folded lines completely. - if (!VIsual_active) { - if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { - oap->start.col = 0; - } - if ((curwin->w_cursor.col > 0 - || oap->inclusive - || oap->motion_type == kMTLineWise) - && hasFolding(curwin->w_cursor.lnum, NULL, - &curwin->w_cursor.lnum)) { - curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); - } - } - oap->end = curwin->w_cursor; - curwin->w_cursor = oap->start; - - /* w_virtcol may have been updated; if the cursor goes back to its - * previous position w_virtcol becomes invalid and isn't updated - * automatically. */ - curwin->w_valid &= ~VALID_VIRTCOL; - } else { - // Include folded lines completely. - if (!VIsual_active && oap->motion_type == kMTLineWise) { - if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, - NULL)) { - curwin->w_cursor.col = 0; - } - if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { - oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); - } - } - oap->end = oap->start; - oap->start = curwin->w_cursor; - } - - // Just in case lines were deleted that make the position invalid. - check_pos(curwin->w_buffer, &oap->end); - oap->line_count = oap->end.lnum - oap->start.lnum + 1; - - // Set "virtual_op" before resetting VIsual_active. - virtual_op = virtual_active(); - - if (VIsual_active || redo_VIsual_busy) { - get_op_vcol(oap, redo_VIsual_vcol, true); - - if (!redo_VIsual_busy && !gui_yank) { - /* - * Prepare to reselect and redo Visual: this is based on the - * size of the Visual text - */ - resel_VIsual_mode = VIsual_mode; - if (curwin->w_curswant == MAXCOL) { - resel_VIsual_vcol = MAXCOL; - } else { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->end), - NULL, NULL, &oap->end_vcol); - } - if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { - if (VIsual_mode != Ctrl_V) { - getvvcol(curwin, &(oap->start), - &oap->start_vcol, NULL, NULL); - } - resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; - } else { - resel_VIsual_vcol = oap->end_vcol; - } - } - resel_VIsual_line_count = oap->line_count; - } - - // can't redo yank (unless 'y' is in 'cpoptions') and ":" - if ((redo_yank || oap->op_type != OP_YANK) - && oap->op_type != OP_COLON - && oap->op_type != OP_FOLD - && oap->op_type != OP_FOLDOPEN - && oap->op_type != OP_FOLDOPENREC - && oap->op_type != OP_FOLDCLOSE - && oap->op_type != OP_FOLDCLOSEREC - && oap->op_type != OP_FOLDDEL - && oap->op_type != OP_FOLDDELREC - && oap->motion_force == NUL) { - /* Prepare for redoing. Only use the nchar field for "r", - * otherwise it might be the second char of the operator. */ - if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) { - prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { - int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; - - // reverse what nv_replace() did - if (nchar == REPLACE_CR_NCHAR) { - nchar = CAR; - } else if (nchar == REPLACE_NL_NCHAR) { - nchar = NL; - } - prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), nchar); - } - if (!redo_VIsual_busy) { - redo_VIsual_mode = resel_VIsual_mode; - redo_VIsual_vcol = resel_VIsual_vcol; - redo_VIsual_line_count = resel_VIsual_line_count; - redo_VIsual_count = cap->count0; - redo_VIsual_arg = cap->arg; - } - } - - // oap->inclusive defaults to true. - // If oap->end is on a NUL (empty line) oap->inclusive becomes - // false. This makes "d}P" and "v}dP" work the same. - if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { - oap->inclusive = true; - } - if (VIsual_mode == 'V') { - oap->motion_type = kMTLineWise; - } else if (VIsual_mode == 'v') { - oap->motion_type = kMTCharWise; - if (*ml_get_pos(&(oap->end)) == NUL - && (include_line_break || !virtual_op)) { - oap->inclusive = false; - // Try to include the newline, unless it's an operator - // that works on lines only. - if (*p_sel != 'o' - && !op_on_lines(oap->op_type) - && oap->end.lnum < curbuf->b_ml.ml_line_count) { - oap->end.lnum++; - oap->end.col = 0; - oap->end.coladd = 0; - oap->line_count++; - } - } - } - - redo_VIsual_busy = false; - - /* - * Switch Visual off now, so screen updating does - * not show inverted text when the screen is redrawn. - * With OP_YANK and sometimes with OP_COLON and OP_FILTER there is - * no screen redraw, so it is done here to remove the inverted - * part. - */ - if (!gui_yank) { - VIsual_active = false; - setmouse(); - mouse_dragging = 0; - may_clear_cmdline(); - if ((oap->op_type == OP_YANK - || oap->op_type == OP_COLON - || oap->op_type == OP_FUNCTION - || oap->op_type == OP_FILTER) - && oap->motion_force == NUL) { - // Make sure redrawing is correct. - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - } - } - - // Include the trailing byte of a multi-byte char. - if (oap->inclusive) { - const int l = utfc_ptr2len(ml_get_pos(&oap->end)); - if (l > 1) { - oap->end.col += l - 1; - } - } - curwin->w_set_curswant = true; - - /* - * oap->empty is set when start and end are the same. The inclusive - * flag affects this too, unless yanking and the end is on a NUL. - */ - oap->empty = (oap->motion_type != kMTLineWise - && (!oap->inclusive - || (oap->op_type == OP_YANK - && gchar_pos(&oap->end) == NUL)) - && equalpos(oap->start, oap->end) - && !(virtual_op && oap->start.coladd != oap->end.coladd) - ); - /* - * For delete, change and yank, it's an error to operate on an - * empty region, when 'E' included in 'cpoptions' (Vi compatible). - */ - empty_region_error = (oap->empty - && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); - - /* Force a redraw when operating on an empty Visual region, when - * 'modifiable is off or creating a fold. */ - if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) - || oap->op_type == OP_FOLD - )) { - curwin->w_p_lbr = lbr_saved; - redraw_curbuf_later(INVERTED); - } - - /* - * If the end of an operator is in column one while oap->motion_type - * is kMTCharWise and oap->inclusive is false, we put op_end after the last - * character in the previous line. If op_start is on or before the - * first non-blank in the line, the operator becomes linewise - * (strange, but that's the way vi does it). - */ - if (oap->motion_type == kMTCharWise - && oap->inclusive == false - && !(cap->retval & CA_NO_ADJ_OP_END) - && oap->end.col == 0 - && (!oap->is_VIsual || *p_sel == 'o') - && oap->line_count > 1) { - oap->end_adjusted = true; // remember that we did this - oap->line_count--; - oap->end.lnum--; - if (inindent(0)) { - oap->motion_type = kMTLineWise; - } else { - oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (oap->end.col) { - --oap->end.col; - oap->inclusive = true; - } - } - } else { - oap->end_adjusted = false; - } - - switch (oap->op_type) { - case OP_LSHIFT: - case OP_RSHIFT: - op_shift(oap, true, - oap->is_VIsual ? (int)cap->count1 : - 1); - auto_format(false, true); - break; - - case OP_JOIN_NS: - case OP_JOIN: - if (oap->line_count < 2) { - oap->line_count = 2; - } - if (curwin->w_cursor.lnum + oap->line_count - 1 > - curbuf->b_ml.ml_line_count) { - beep_flush(); - } else { - do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, - true, true, true); - auto_format(false, true); - } - break; - - case OP_DELETE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - (void)op_delete(oap); - // save cursor line for undo if it wasn't saved yet - if (oap->motion_type == kMTLineWise - && has_format_option(FO_AUTO) - && u_save_cursor() == OK) { - auto_format(false, true); - } - } - break; - - case OP_YANK: - if (empty_region_error) { - if (!gui_yank) { - vim_beep(BO_OPER); - CancelRedo(); - } - } else { - curwin->w_p_lbr = lbr_saved; - oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); - } - check_cursor_col(); - break; - - case OP_CHANGE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once and not when typed and - * 'insertmode' isn't set. */ - if (p_im || !KeyTyped) { - restart_edit_save = restart_edit; - } else { - restart_edit_save = 0; - } - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - // Reset finish_op now, don't want it set inside edit(). - finish_op = false; - if (op_change(oap)) { // will call edit() - cap->retval |= CA_COMMAND_BUSY; - } - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } - } - break; - - case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { - AppendToRedobuff("!\r"); // Use any last used !cmd. - } else { - bangredo = true; // do_bang() will put cmd in redo buffer. - } - FALLTHROUGH; - - case OP_INDENT: - case OP_COLON: - - /* - * If 'equalprg' is empty, do the indenting internally. - */ - if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { - if (curbuf->b_p_lisp) { - op_reindent(oap, get_lisp_indent); - break; - } - op_reindent(oap, - *curbuf->b_p_inde != NUL ? get_expr_indent : - get_c_indent); - break; - } - - op_colon(oap); - break; - - case OP_TILDE: - case OP_UPPER: - case OP_LOWER: - case OP_ROT13: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - op_tilde(oap); - } - check_cursor_col(); - break; - - case OP_FORMAT: - if (*curbuf->b_p_fex != NUL) { - op_formatexpr(oap); // use expression - } else { - if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { - op_colon(oap); // use external command - } else { - op_format(oap, false); // use internal function - } - } - break; - - case OP_FORMAT2: - op_format(oap, true); // use internal function - break; - - case OP_FUNCTION: - // Restore linebreak, so that when the user edits it looks as - // before. - curwin->w_p_lbr = lbr_saved; - op_function(oap); // call 'operatorfunc' - break; - - case OP_INSERT: - case OP_APPEND: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - /* This is a new edit command, not a restart. Need to - * remember it to make 'insertmode' work with mappings for - * Visual mode. But do this only once. */ - restart_edit_save = restart_edit; - restart_edit = 0; - - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_insert(oap, cap->count1); - - // Reset linebreak, so that formatting works correctly. - curwin->w_p_lbr = false; - - /* TODO: when inserting in several lines, should format all - * the lines. */ - auto_format(false, true); - - if (restart_edit == 0) { - restart_edit = restart_edit_save; - } else { - cap->retval |= CA_COMMAND_BUSY; - } - } - break; - - case OP_REPLACE: - VIsual_reselect = false; // don't reselect now - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - // Restore linebreak, so that when the user edits it looks as before. - curwin->w_p_lbr = lbr_saved; - - op_replace(oap, cap->nchar); - } - break; - - case OP_FOLD: - VIsual_reselect = false; // don't reselect now - foldCreate(curwin, oap->start, oap->end); - break; - - case OP_FOLDOPEN: - case OP_FOLDOPENREC: - case OP_FOLDCLOSE: - case OP_FOLDCLOSEREC: - VIsual_reselect = false; // don't reselect now - opFoldRange(oap->start, oap->end, - oap->op_type == OP_FOLDOPEN - || oap->op_type == OP_FOLDOPENREC, - oap->op_type == OP_FOLDOPENREC - || oap->op_type == OP_FOLDCLOSEREC, - oap->is_VIsual); - break; - - case OP_FOLDDEL: - case OP_FOLDDELREC: - VIsual_reselect = false; // don't reselect now - deleteFold(curwin, oap->start.lnum, oap->end.lnum, - oap->op_type == OP_FOLDDELREC, oap->is_VIsual); - break; - - case OP_NR_ADD: - case OP_NR_SUB: - if (empty_region_error) { - vim_beep(BO_OPER); - CancelRedo(); - } else { - VIsual_active = true; - curwin->w_p_lbr = lbr_saved; - op_addsub(oap, cap->count1, redo_VIsual_arg); - VIsual_active = false; - } - check_cursor_col(); - break; - default: - clearopbeep(oap); - } - virtual_op = kNone; - if (!gui_yank) { - /* - * if 'sol' not set, go back to old column for some commands - */ - if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted - && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT - || oap->op_type == OP_DELETE)) { - curwin->w_p_lbr = false; - coladvance(curwin->w_curswant = old_col); - } - } else { - curwin->w_cursor = old_cursor; - } - clearop(oap); - motion_force = NUL; - } - curwin->w_p_lbr = lbr_saved; -} - -/* - * Handle indent and format operators and visual mode ":". - */ -static void op_colon(oparg_T *oap) -{ - stuffcharReadbuff(':'); - if (oap->is_VIsual) { - stuffReadbuff("'<,'>"); - } else { - // Make the range look nice, so it can be repeated. - if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else { - stuffnumReadbuff((long)oap->start.lnum); - } - if (oap->end.lnum != oap->start.lnum) { - stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) { - stuffcharReadbuff('.'); - } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { - stuffcharReadbuff('$'); - } else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff(".+"); - stuffnumReadbuff(oap->line_count - 1); - } else { - stuffnumReadbuff((long)oap->end.lnum); - } - } - } - if (oap->op_type != OP_COLON) { - stuffReadbuff("!"); - } - if (oap->op_type == OP_INDENT) { - stuffReadbuff((const char *)get_equalprg()); - stuffReadbuff("\n"); - } else if (oap->op_type == OP_FORMAT) { - if (*curbuf->b_p_fp != NUL) { - stuffReadbuff((const char *)curbuf->b_p_fp); - } else if (*p_fp != NUL) { - stuffReadbuff((const char *)p_fp); - } else { - stuffReadbuff("fmt"); - } - stuffReadbuff("\n']"); - } - - /* - * do_cmdline() does the rest - */ -} - -/* - * Handle the "g@" operator: call 'operatorfunc'. - */ -static void op_function(const oparg_T *oap) - FUNC_ATTR_NONNULL_ALL -{ - const TriState save_virtual_op = virtual_op; - const bool save_finish_op = finish_op; - - if (*p_opfunc == NUL) { - emsg(_("E774: 'operatorfunc' is empty")); - } else { - // Set '[ and '] marks to text to be operated on. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (oap->motion_type != kMTLineWise && !oap->inclusive) { - // Exclude the end position. - decl(&curbuf->b_op_end); - } - - typval_T argv[2]; - argv[0].v_type = VAR_STRING; - argv[1].v_type = VAR_UNKNOWN; - argv[0].vval.v_string = - (char_u *)(((const char *const[]) { - [kMTBlockWise] = "block", - [kMTLineWise] = "line", - [kMTCharWise] = "char", - })[oap->motion_type]); - - // Reset virtual_op so that 'virtualedit' can be changed in the - // function. - virtual_op = kNone; - - // Reset finish_op so that mode() returns the right value. - finish_op = false; - - (void)call_func_retnr(p_opfunc, 1, argv); - - virtual_op = save_virtual_op; - finish_op = save_finish_op; - } -} - // Move the current tab to tab in same column as mouse or to end of the // tabline if there is no tab there. static void move_tab_to_mouse(void) @@ -3096,6 +2338,14 @@ void reset_VIsual(void) } } +void restore_visual_mode(void) +{ + if (VIsual_mode_orig != NUL) { + curbuf->b_visual.vi_mode = VIsual_mode_orig; + VIsual_mode_orig = NUL; + } +} + // Check for a balloon-eval special item to include when searching for an // identifier. When "dir" is BACKWARD "ptr[-1]" must be valid! // Returns true if the character at "*ptr" should be included. @@ -3274,7 +2524,7 @@ static void prep_redo_cmd(cmdarg_T *cap) * Prepare for redo of any command. * Note that only the last argument can be a multi-byte char. */ -static void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) +void prep_redo(int regname, long num, int cmd1, int cmd2, int cmd3, int cmd4, int cmd5) { ResetRedobuff(); if (regname != 0) { // yank from specified buffer @@ -3331,7 +2581,7 @@ static bool checkclearopq(oparg_T *oap) return true; } -static void clearop(oparg_T *oap) +void clearop(oparg_T *oap) { oap->op_type = OP_NOP; oap->regname = 0; @@ -3340,7 +2590,7 @@ static void clearop(oparg_T *oap) motion_force = NUL; } -static void clearopbeep(oparg_T *oap) +void clearopbeep(oparg_T *oap) { clearop(oap); beep_flush(); @@ -3370,7 +2620,7 @@ static void unshift_special(cmdarg_T *cap) /// If the mode is currently displayed clear the command line or update the /// command displayed. -static void may_clear_cmdline(void) +void may_clear_cmdline(void) { if (mode_displayed) { // unshow visual mode later @@ -7738,7 +6988,7 @@ static void adjust_for_sel(cmdarg_T *cap) * Should check VIsual_mode before calling this. * Returns true when backed up to the previous line. */ -static bool unadjust_for_sel(void) +bool unadjust_for_sel(void) { pos_T *pp; @@ -8328,71 +7578,6 @@ static void nv_open(cmdarg_T *cap) } } -/// Calculate start/end virtual columns for operating in block mode. -/// -/// @param initial when true: adjust position for 'selectmode' -static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) -{ - colnr_T start; - colnr_T end; - - if (VIsual_mode != Ctrl_V - || (!initial && oap->end.col < curwin->w_width_inner)) { - return; - } - - oap->motion_type = kMTBlockWise; - - // prevent from moving onto a trail byte - mark_mb_adjustpos(curwin->w_buffer, &oap->end); - - getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); - if (!redo_VIsual_busy) { - getvvcol(curwin, &(oap->end), &start, NULL, &end); - - if (start < oap->start_vcol) { - oap->start_vcol = start; - } - if (end > oap->end_vcol) { - if (initial && *p_sel == 'e' - && start >= 1 - && start - 1 >= oap->end_vcol) { - oap->end_vcol = start - 1; - } else { - oap->end_vcol = end; - } - } - } - - // if '$' was used, get oap->end_vcol from longest line - if (curwin->w_curswant == MAXCOL) { - curwin->w_cursor.col = MAXCOL; - oap->end_vcol = 0; - for (curwin->w_cursor.lnum = oap->start.lnum; - curwin->w_cursor.lnum <= oap->end.lnum; ++curwin->w_cursor.lnum) { - getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); - if (end > oap->end_vcol) { - oap->end_vcol = end; - } - } - } else if (redo_VIsual_busy) { - oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; - } - - // Correct oap->end.col and oap->start.col to be the - // upper-left and lower-right corner of the block area. - // - // (Actually, this does convert column positions into character - // positions) - curwin->w_cursor.lnum = oap->end.lnum; - coladvance(oap->end_vcol); - oap->end = curwin->w_cursor; - - curwin->w_cursor = oap->start; - coladvance(oap->start_vcol); - oap->start = curwin->w_cursor; -} - // Handle an arbitrary event in normal mode static void nv_event(cmdarg_T *cap) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 38de24b8ad..52c382028e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -27,7 +27,9 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -37,6 +39,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/ops.h" @@ -1768,7 +1771,7 @@ static void replace_character(int c) /* * Replace a whole area with one character. */ -int op_replace(oparg_T *oap, int c) +static int op_replace(oparg_T *oap, int c) { int n, numc; int num_chars; @@ -4177,7 +4180,7 @@ static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, in /// Implementation of the format operator 'gq'. /// /// @param keep_cursor keep cursor on same text char -void op_format(oparg_T *oap, int keep_cursor) +static void op_format(oparg_T *oap, int keep_cursor) { long old_line_count = curbuf->b_ml.ml_line_count; @@ -4245,7 +4248,7 @@ void op_format(oparg_T *oap, int keep_cursor) /* * Implementation of the format operator 'gq' for when using 'formatexpr'. */ -void op_formatexpr(oparg_T *oap) +static void op_formatexpr(oparg_T *oap) { if (oap->is_VIsual) { // When there is no change: need to remove the Visual selection @@ -5936,6 +5939,791 @@ void cursor_pos_info(dict_T *dict) } } +// Handle indent and format operators and visual mode ":". +static void op_colon(oparg_T *oap) +{ + stuffcharReadbuff(':'); + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else { + stuffnumReadbuff((long)oap->start.lnum); + } + if (oap->end.lnum != oap->start.lnum) { + stuffcharReadbuff(','); + if (oap->end.lnum == curwin->w_cursor.lnum) { + stuffcharReadbuff('.'); + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { + stuffcharReadbuff('$'); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); + stuffnumReadbuff(oap->line_count - 1); + } else { + stuffnumReadbuff((long)oap->end.lnum); + } + } + } + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } + if (oap->op_type == OP_INDENT) { + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); + } else if (oap->op_type == OP_FORMAT) { + if (*curbuf->b_p_fp != NUL) { + stuffReadbuff((const char *)curbuf->b_p_fp); + } else if (*p_fp != NUL) { + stuffReadbuff((const char *)p_fp); + } else { + stuffReadbuff("fmt"); + } + stuffReadbuff("\n']"); + } + + // do_cmdline() does the rest +} + +// Handle the "g@" operator: call 'operatorfunc'. +static void op_function(const oparg_T *oap) + FUNC_ATTR_NONNULL_ALL +{ + const TriState save_virtual_op = virtual_op; + const bool save_finish_op = finish_op; + + if (*p_opfunc == NUL) { + emsg(_("E774: 'operatorfunc' is empty")); + } else { + // Set '[ and '] marks to text to be operated on. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (oap->motion_type != kMTLineWise && !oap->inclusive) { + // Exclude the end position. + decl(&curbuf->b_op_end); + } + + typval_T argv[2]; + argv[0].v_type = VAR_STRING; + argv[1].v_type = VAR_UNKNOWN; + argv[0].vval.v_string = + (char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]); + + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. + virtual_op = kNone; + + // Reset finish_op so that mode() returns the right value. + finish_op = false; + + (void)call_func_retnr(p_opfunc, 1, argv); + + virtual_op = save_virtual_op; + finish_op = save_finish_op; + } +} + +/// Calculate start/end virtual columns for operating in block mode. +/// +/// @param initial when true: adjust position for 'selectmode' +static void get_op_vcol(oparg_T *oap, colnr_T redo_VIsual_vcol, bool initial) +{ + colnr_T start; + colnr_T end; + + if (VIsual_mode != Ctrl_V + || (!initial && oap->end.col < curwin->w_width_inner)) { + return; + } + + oap->motion_type = kMTBlockWise; + + // prevent from moving onto a trail byte + mark_mb_adjustpos(curwin->w_buffer, &oap->end); + + getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); + if (!redo_VIsual_busy) { + getvvcol(curwin, &(oap->end), &start, NULL, &end); + + if (start < oap->start_vcol) { + oap->start_vcol = start; + } + if (end > oap->end_vcol) { + if (initial && *p_sel == 'e' + && start >= 1 + && start - 1 >= oap->end_vcol) { + oap->end_vcol = start - 1; + } else { + oap->end_vcol = end; + } + } + } + + // if '$' was used, get oap->end_vcol from longest line + if (curwin->w_curswant == MAXCOL) { + curwin->w_cursor.col = MAXCOL; + oap->end_vcol = 0; + for (curwin->w_cursor.lnum = oap->start.lnum; + curwin->w_cursor.lnum <= oap->end.lnum; curwin->w_cursor.lnum++) { + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &end); + if (end > oap->end_vcol) { + oap->end_vcol = end; + } + } + } else if (redo_VIsual_busy) { + oap->end_vcol = oap->start_vcol + redo_VIsual_vcol - 1; + } + + // Correct oap->end.col and oap->start.col to be the + // upper-left and lower-right corner of the block area. + // + // (Actually, this does convert column positions into character + // positions) + curwin->w_cursor.lnum = oap->end.lnum; + coladvance(oap->end_vcol); + oap->end = curwin->w_cursor; + + curwin->w_cursor = oap->start; + coladvance(oap->start_vcol); + oap->start = curwin->w_cursor; +} + +// Handle an operator after Visual mode or when the movement is finished. +// "gui_yank" is true when yanking text for the clipboard. +void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) +{ + oparg_T *oap = cap->oap; + pos_T old_cursor; + bool empty_region_error; + int restart_edit_save; + int lbr_saved = curwin->w_p_lbr; + + + // The visual area is remembered for redo + static int redo_VIsual_mode = NUL; // 'v', 'V', or Ctrl-V + static linenr_T redo_VIsual_line_count; // number of lines + static colnr_T redo_VIsual_vcol; // number of cols or end column + static long redo_VIsual_count; // count for Visual operator + static int redo_VIsual_arg; // extra argument + bool include_line_break = false; + + old_cursor = curwin->w_cursor; + + // If an operation is pending, handle it... + if ((finish_op + || VIsual_active) + && oap->op_type != OP_NOP) { + // Yank can be redone when 'y' is in 'cpoptions', but not when yanking + // for the clipboard. + const bool redo_yank = vim_strchr(p_cpo, CPO_YANK) != NULL && !gui_yank; + + // Avoid a problem with unwanted linebreaks in block mode + if (curwin->w_p_lbr) { + curwin->w_valid &= ~VALID_VIRTCOL; + } + curwin->w_p_lbr = false; + oap->is_VIsual = VIsual_active; + if (oap->motion_force == 'V') { + oap->motion_type = kMTLineWise; + } else if (oap->motion_force == 'v') { + // If the motion was linewise, "inclusive" will not have been set. + // Use "exclusive" to be consistent. Makes "dvj" work nice. + if (oap->motion_type == kMTLineWise) { + oap->inclusive = false; + } else if (oap->motion_type == kMTCharWise) { + // If the motion already was charwise, toggle "inclusive" + oap->inclusive = !oap->inclusive; + } + oap->motion_type = kMTCharWise; + } else if (oap->motion_force == Ctrl_V) { + // Change line- or charwise motion into Visual block mode. + if (!VIsual_active) { + VIsual_active = true; + VIsual = oap->start; + } + VIsual_mode = Ctrl_V; + VIsual_select = false; + VIsual_reselect = false; + } + + // Only redo yank when 'y' flag is in 'cpoptions'. + // Never redo "zf" (define fold). + if ((redo_yank || oap->op_type != OP_YANK) + && ((!VIsual_active || oap->motion_force) + // Also redo Operator-pending Visual mode mappings. + || ((cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) + && oap->op_type != OP_COLON)) + && cap->cmdchar != 'D' + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + if (cap->cmdchar == '/' || cap->cmdchar == '?') { // was a search + // If 'cpoptions' does not contain 'r', insert the search + // pattern to really repeat the same command. + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { + AppendToRedobuffLit(cap->searchbuf, -1); + } + AppendToRedobuff(NL_STR); + } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) { + ResetRedobuff(); + } else { + AppendToRedobuffLit(repeat_cmdline, -1); + AppendToRedobuff(NL_STR); + XFREE_CLEAR(repeat_cmdline); + } + } + } + + if (redo_VIsual_busy) { + // Redo of an operation on a Visual area. Use the same size from + // redo_VIsual_line_count and redo_VIsual_vcol. + oap->start = curwin->w_cursor; + curwin->w_cursor.lnum += redo_VIsual_line_count - 1; + if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + } + VIsual_mode = redo_VIsual_mode; + if (redo_VIsual_vcol == MAXCOL || VIsual_mode == 'v') { + if (VIsual_mode == 'v') { + if (redo_VIsual_line_count <= 1) { + validate_virtcol(); + curwin->w_curswant = + curwin->w_virtcol + redo_VIsual_vcol - 1; + } else { + curwin->w_curswant = redo_VIsual_vcol; + } + } else { + curwin->w_curswant = MAXCOL; + } + coladvance(curwin->w_curswant); + } + cap->count0 = redo_VIsual_count; + cap->count1 = (cap->count0 == 0 ? 1 : cap->count0); + } else if (VIsual_active) { + if (!gui_yank) { + // Save the current VIsual area for '< and '> marks, and "gv" + curbuf->b_visual.vi_start = VIsual; + curbuf->b_visual.vi_end = curwin->w_cursor; + curbuf->b_visual.vi_mode = VIsual_mode; + restore_visual_mode(); + curbuf->b_visual.vi_curswant = curwin->w_curswant; + curbuf->b_visual_mode_eval = VIsual_mode; + } + + // In Select mode, a linewise selection is operated upon like a + // charwise selection. + // Special case: gH<Del> deletes the last line. + if (VIsual_select && VIsual_mode == 'V' + && cap->oap->op_type != OP_DELETE) { + if (lt(VIsual, curwin->w_cursor)) { + VIsual.col = 0; + curwin->w_cursor.col = + (colnr_T)STRLEN(ml_get(curwin->w_cursor.lnum)); + } else { + curwin->w_cursor.col = 0; + VIsual.col = (colnr_T)STRLEN(ml_get(VIsual.lnum)); + } + VIsual_mode = 'v'; + } else if (VIsual_mode == 'v') { + // If 'selection' is "exclusive", backup one character for + // charwise selections. + include_line_break = + unadjust_for_sel(); + } + + oap->start = VIsual; + if (VIsual_mode == 'V') { + oap->start.col = 0; + oap->start.coladd = 0; + } + } + + // Set oap->start to the first position of the operated text, oap->end + // to the end of the operated text. w_cursor is equal to oap->start. + if (lt(oap->start, curwin->w_cursor)) { + // Include folded lines completely. + if (!VIsual_active) { + if (hasFolding(oap->start.lnum, &oap->start.lnum, NULL)) { + oap->start.col = 0; + } + if ((curwin->w_cursor.col > 0 + || oap->inclusive + || oap->motion_type == kMTLineWise) + && hasFolding(curwin->w_cursor.lnum, NULL, + &curwin->w_cursor.lnum)) { + curwin->w_cursor.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } + } + oap->end = curwin->w_cursor; + curwin->w_cursor = oap->start; + + // w_virtcol may have been updated; if the cursor goes back to its + // previous position w_virtcol becomes invalid and isn't updated + // automatically. + curwin->w_valid &= ~VALID_VIRTCOL; + } else { + // Include folded lines completely. + if (!VIsual_active && oap->motion_type == kMTLineWise) { + if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, + NULL)) { + curwin->w_cursor.col = 0; + } + if (hasFolding(oap->start.lnum, NULL, &oap->start.lnum)) { + oap->start.col = (colnr_T)STRLEN(ml_get(oap->start.lnum)); + } + } + oap->end = oap->start; + oap->start = curwin->w_cursor; + } + + // Just in case lines were deleted that make the position invalid. + check_pos(curwin->w_buffer, &oap->end); + oap->line_count = oap->end.lnum - oap->start.lnum + 1; + + // Set "virtual_op" before resetting VIsual_active. + virtual_op = virtual_active(); + + if (VIsual_active || redo_VIsual_busy) { + get_op_vcol(oap, redo_VIsual_vcol, true); + + if (!redo_VIsual_busy && !gui_yank) { + // Prepare to reselect and redo Visual: this is based on the + // size of the Visual text + resel_VIsual_mode = VIsual_mode; + if (curwin->w_curswant == MAXCOL) { + resel_VIsual_vcol = MAXCOL; + } else { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->end), + NULL, NULL, &oap->end_vcol); + } + if (VIsual_mode == Ctrl_V || oap->line_count <= 1) { + if (VIsual_mode != Ctrl_V) { + getvvcol(curwin, &(oap->start), + &oap->start_vcol, NULL, NULL); + } + resel_VIsual_vcol = oap->end_vcol - oap->start_vcol + 1; + } else { + resel_VIsual_vcol = oap->end_vcol; + } + } + resel_VIsual_line_count = oap->line_count; + } + + // can't redo yank (unless 'y' is in 'cpoptions') and ":" + if ((redo_yank || oap->op_type != OP_YANK) + && oap->op_type != OP_COLON + && oap->op_type != OP_FOLD + && oap->op_type != OP_FOLDOPEN + && oap->op_type != OP_FOLDOPENREC + && oap->op_type != OP_FOLDCLOSE + && oap->op_type != OP_FOLDCLOSEREC + && oap->op_type != OP_FOLDDEL + && oap->op_type != OP_FOLDDELREC + && oap->motion_force == NUL) { + // Prepare for redoing. Only use the nchar field for "r", + // otherwise it might be the second char of the operator. + if (cap->cmdchar == 'g' && (cap->nchar == 'n' + || cap->nchar == 'N')) { + prep_redo(oap->regname, cap->count0, + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + } else if (cap->cmdchar != ':' && cap->cmdchar != K_COMMAND) { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) { + nchar = CAR; + } else if (nchar == REPLACE_NL_NCHAR) { + nchar = NL; + } + prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), nchar); + } + if (!redo_VIsual_busy) { + redo_VIsual_mode = resel_VIsual_mode; + redo_VIsual_vcol = resel_VIsual_vcol; + redo_VIsual_line_count = resel_VIsual_line_count; + redo_VIsual_count = cap->count0; + redo_VIsual_arg = cap->arg; + } + } + + // oap->inclusive defaults to true. + // If oap->end is on a NUL (empty line) oap->inclusive becomes + // false. This makes "d}P" and "v}dP" work the same. + if (oap->motion_force == NUL || oap->motion_type == kMTLineWise) { + oap->inclusive = true; + } + if (VIsual_mode == 'V') { + oap->motion_type = kMTLineWise; + } else if (VIsual_mode == 'v') { + oap->motion_type = kMTCharWise; + if (*ml_get_pos(&(oap->end)) == NUL + && (include_line_break || !virtual_op)) { + oap->inclusive = false; + // Try to include the newline, unless it's an operator + // that works on lines only. + if (*p_sel != 'o' + && !op_on_lines(oap->op_type) + && oap->end.lnum < curbuf->b_ml.ml_line_count) { + oap->end.lnum++; + oap->end.col = 0; + oap->end.coladd = 0; + oap->line_count++; + } + } + } + + redo_VIsual_busy = false; + + // Switch Visual off now, so screen updating does + // not show inverted text when the screen is redrawn. + // With OP_YANK and sometimes with OP_COLON and OP_FILTER there is + // no screen redraw, so it is done here to remove the inverted + // part. + if (!gui_yank) { + VIsual_active = false; + setmouse(); + mouse_dragging = 0; + may_clear_cmdline(); + if ((oap->op_type == OP_YANK + || oap->op_type == OP_COLON + || oap->op_type == OP_FUNCTION + || oap->op_type == OP_FILTER) + && oap->motion_force == NUL) { + // Make sure redrawing is correct. + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + } + } + + // Include the trailing byte of a multi-byte char. + if (oap->inclusive) { + const int l = utfc_ptr2len(ml_get_pos(&oap->end)); + if (l > 1) { + oap->end.col += l - 1; + } + } + curwin->w_set_curswant = true; + + // oap->empty is set when start and end are the same. The inclusive + // flag affects this too, unless yanking and the end is on a NUL. + oap->empty = (oap->motion_type != kMTLineWise + && (!oap->inclusive + || (oap->op_type == OP_YANK + && gchar_pos(&oap->end) == NUL)) + && equalpos(oap->start, oap->end) + && !(virtual_op && oap->start.coladd != oap->end.coladd)); + // For delete, change and yank, it's an error to operate on an + // empty region, when 'E' included in 'cpoptions' (Vi compatible). + empty_region_error = (oap->empty + && vim_strchr(p_cpo, CPO_EMPTYREGION) != NULL); + + // Force a redraw when operating on an empty Visual region, when + // 'modifiable is off or creating a fold. + if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf) + || oap->op_type == OP_FOLD)) { + curwin->w_p_lbr = lbr_saved; + redraw_curbuf_later(INVERTED); + } + + // If the end of an operator is in column one while oap->motion_type + // is kMTCharWise and oap->inclusive is false, we put op_end after the last + // character in the previous line. If op_start is on or before the + // first non-blank in the line, the operator becomes linewise + // (strange, but that's the way vi does it). + if (oap->motion_type == kMTCharWise + && oap->inclusive == false + && !(cap->retval & CA_NO_ADJ_OP_END) + && oap->end.col == 0 + && (!oap->is_VIsual || *p_sel == 'o') + && oap->line_count > 1) { + oap->end_adjusted = true; // remember that we did this + oap->line_count--; + oap->end.lnum--; + if (inindent(0)) { + oap->motion_type = kMTLineWise; + } else { + oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (oap->end.col) { + oap->end.col--; + oap->inclusive = true; + } + } + } else { + oap->end_adjusted = false; + } + + switch (oap->op_type) { + case OP_LSHIFT: + case OP_RSHIFT: + op_shift(oap, true, + oap->is_VIsual ? (int)cap->count1 : + 1); + auto_format(false, true); + break; + + case OP_JOIN_NS: + case OP_JOIN: + if (oap->line_count < 2) { + oap->line_count = 2; + } + if (curwin->w_cursor.lnum + oap->line_count - 1 > + curbuf->b_ml.ml_line_count) { + beep_flush(); + } else { + do_join((size_t)oap->line_count, oap->op_type == OP_JOIN, + true, true, true); + auto_format(false, true); + } + break; + + case OP_DELETE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + (void)op_delete(oap); + // save cursor line for undo if it wasn't saved yet + if (oap->motion_type == kMTLineWise + && has_format_option(FO_AUTO) + && u_save_cursor() == OK) { + auto_format(false, true); + } + } + break; + + case OP_YANK: + if (empty_region_error) { + if (!gui_yank) { + vim_beep(BO_OPER); + CancelRedo(); + } + } else { + curwin->w_p_lbr = lbr_saved; + oap->excl_tr_ws = cap->cmdchar == 'z'; + (void)op_yank(oap, !gui_yank, false); + } + check_cursor_col(); + break; + + case OP_CHANGE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once and not when typed and + // 'insertmode' isn't set. + if (p_im || !KeyTyped) { + restart_edit_save = restart_edit; + } else { + restart_edit_save = 0; + } + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + // Reset finish_op now, don't want it set inside edit(). + finish_op = false; + if (op_change(oap)) { // will call edit() + cap->retval |= CA_COMMAND_BUSY; + } + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } + } + break; + + case OP_FILTER: + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } + FALLTHROUGH; + + case OP_INDENT: + case OP_COLON: + + // If 'equalprg' is empty, do the indenting internally. + if (oap->op_type == OP_INDENT && *get_equalprg() == NUL) { + if (curbuf->b_p_lisp) { + op_reindent(oap, get_lisp_indent); + break; + } + op_reindent(oap, + *curbuf->b_p_inde != NUL ? get_expr_indent : + get_c_indent); + break; + } + + op_colon(oap); + break; + + case OP_TILDE: + case OP_UPPER: + case OP_LOWER: + case OP_ROT13: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + op_tilde(oap); + } + check_cursor_col(); + break; + + case OP_FORMAT: + if (*curbuf->b_p_fex != NUL) { + op_formatexpr(oap); // use expression + } else { + if (*p_fp != NUL || *curbuf->b_p_fp != NUL) { + op_colon(oap); // use external command + } else { + op_format(oap, false); // use internal function + } + } + break; + + case OP_FORMAT2: + op_format(oap, true); // use internal function + break; + + case OP_FUNCTION: + // Restore linebreak, so that when the user edits it looks as + // before. + curwin->w_p_lbr = lbr_saved; + op_function(oap); // call 'operatorfunc' + break; + + case OP_INSERT: + case OP_APPEND: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // This is a new edit command, not a restart. Need to + // remember it to make 'insertmode' work with mappings for + // Visual mode. But do this only once. + restart_edit_save = restart_edit; + restart_edit = 0; + + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_insert(oap, cap->count1); + + // Reset linebreak, so that formatting works correctly. + curwin->w_p_lbr = false; + + // TODO(brammool): when inserting in several lines, should format all + // the lines. + auto_format(false, true); + + if (restart_edit == 0) { + restart_edit = restart_edit_save; + } else { + cap->retval |= CA_COMMAND_BUSY; + } + } + break; + + case OP_REPLACE: + VIsual_reselect = false; // don't reselect now + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + // Restore linebreak, so that when the user edits it looks as before. + curwin->w_p_lbr = lbr_saved; + + op_replace(oap, cap->nchar); + } + break; + + case OP_FOLD: + VIsual_reselect = false; // don't reselect now + foldCreate(curwin, oap->start, oap->end); + break; + + case OP_FOLDOPEN: + case OP_FOLDOPENREC: + case OP_FOLDCLOSE: + case OP_FOLDCLOSEREC: + VIsual_reselect = false; // don't reselect now + opFoldRange(oap->start, oap->end, + oap->op_type == OP_FOLDOPEN + || oap->op_type == OP_FOLDOPENREC, + oap->op_type == OP_FOLDOPENREC + || oap->op_type == OP_FOLDCLOSEREC, + oap->is_VIsual); + break; + + case OP_FOLDDEL: + case OP_FOLDDELREC: + VIsual_reselect = false; // don't reselect now + deleteFold(curwin, oap->start.lnum, oap->end.lnum, + oap->op_type == OP_FOLDDELREC, oap->is_VIsual); + break; + + case OP_NR_ADD: + case OP_NR_SUB: + if (empty_region_error) { + vim_beep(BO_OPER); + CancelRedo(); + } else { + VIsual_active = true; + curwin->w_p_lbr = lbr_saved; + op_addsub(oap, cap->count1, redo_VIsual_arg); + VIsual_active = false; + } + check_cursor_col(); + break; + default: + clearopbeep(oap); + } + virtual_op = kNone; + if (!gui_yank) { + // if 'sol' not set, go back to old column for some commands + if (!p_sol && oap->motion_type == kMTLineWise && !oap->end_adjusted + && (oap->op_type == OP_LSHIFT || oap->op_type == OP_RSHIFT + || oap->op_type == OP_DELETE)) { + curwin->w_p_lbr = false; + coladvance(curwin->w_curswant = old_col); + } + } else { + curwin->w_cursor = old_cursor; + } + clearop(oap); + motion_force = NUL; + } + curwin->w_p_lbr = lbr_saved; +} + /// Check if the default register (used in an unnamed paste) should be a /// clipboard register. This happens when `clipboard=unnamed[plus]` is set /// and a provider is available. diff --git a/src/nvim/option.c b/src/nvim/option.c index ee5f62d826..1fe2e1d04c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1301,7 +1301,11 @@ int do_set(char_u *arg, int opt_flags) char_u *oldval = NULL; // previous value if *varp char_u *newval; char_u *origval = NULL; + char_u *origval_l = NULL; + char_u *origval_g = NULL; char *saved_origval = NULL; + char *saved_origval_l = NULL; + char *saved_origval_g = NULL; char *saved_newval = NULL; unsigned newlen; int comma; @@ -1319,10 +1323,21 @@ int do_set(char_u *arg, int opt_flags) // new value is valid. oldval = *(char_u **)varp; + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + origval_l = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + origval_g = *(char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + + // A global-local string option might have an empty + // option as value to indicate that the global + // value should be used. + if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) { + origval_l = origval_g; + } + } + // When setting the local value of a global // option, the old value may be the global value. - if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags - & OPT_LOCAL)) { + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) { origval = *(char_u **)get_varp(&options[opt_idx]); } else { origval = oldval; @@ -1388,6 +1403,12 @@ int do_set(char_u *arg, int opt_flags) if (origval == oldval) { origval = *(char_u **)varp; } + if (origval_l == oldval) { + origval_l = *(char_u **)varp; + } + if (origval_g == oldval) { + origval_g = *(char_u **)varp; + } oldval = *(char_u **)varp; } /* @@ -1596,6 +1617,8 @@ int do_set(char_u *arg, int opt_flags) // origval may be freed by // did_set_string_option(), make a copy. saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0; + saved_origval_l = (origval_l != NULL) ? xstrdup((char *)origval_l) : 0; + saved_origval_g = (origval_g != NULL) ? xstrdup((char *)origval_g) : 0; // newval (and varp) may become invalid if the // buffer is closed by autocommands. @@ -1630,8 +1653,8 @@ int do_set(char_u *arg, int opt_flags) if (errmsg == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_origval, - saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, saved_origval_l, + saved_origval_g, saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -1639,6 +1662,8 @@ int do_set(char_u *arg, int opt_flags) } } xfree(saved_origval); + xfree(saved_origval_l); + xfree(saved_origval_g); xfree(saved_newval); // If error detected, print the error message. @@ -2233,9 +2258,19 @@ static char *set_string_option(const int opt_idx, const char *const value, const ? OPT_GLOBAL : OPT_LOCAL) : opt_flags)); char *const oldval = *varp; + char *oldval_l = NULL; + char *oldval_g = NULL; + + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL); + oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *varp = s; char *const saved_oldval = xstrdup(oldval); + char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0; + char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0; char *const saved_newval = xstrdup(s); int value_checked = false; @@ -2249,7 +2284,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const // call autocommand after handling side effects if (r == NULL) { if (!starting) { - trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g, + saved_newval); } if (options[opt_idx].flags & P_UI_OPTION) { ui_call_option_set(cstr_as_string(options[opt_idx].fullname), @@ -2257,6 +2293,8 @@ static char *set_string_option(const int opt_idx, const char *const value, const } } xfree(saved_oldval); + xfree(saved_oldval_l); + xfree(saved_oldval_g); xfree(saved_newval); return r; @@ -3851,6 +3889,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va const int opt_flags) { int old_value = *(int *)varp; + int old_global_value = 0; // Disallow changing some options from secure mode if ((secure || sandbox != 0) @@ -3858,6 +3897,13 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va return (char *)e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" in fact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + *(int *)varp = value; // set the new value // Remember where the option was set. set_option_sctx_idx(opt_idx, opt_flags, current_sctx); @@ -4134,20 +4180,35 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va // Don't do this while starting up or recursively. if (!starting && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[2]; + char buf_old_global[2]; char buf_new[2]; char buf_type[7]; - vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", - old_value ? true: false); - vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", - value ? true: false); + vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%d", old_value ? true : false); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%d", old_global_value ? true : false); + vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%d", value ? true : false); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4181,7 +4242,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, { char *errmsg = NULL; long old_value = *(long *)varp; - long old_Rows = Rows; // remember old Rows + long old_global_value = 0; // only used when setting a local and global option + long old_Rows = Rows; // remember old Rows long *pp = (long *)varp; // Disallow changing some options from secure mode. @@ -4190,6 +4252,13 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return e_secure; } + // Save the global value before changing anything. This is needed as for + // a global-only option setting the "local value" infact sets the global + // value (since there is only one value). + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); + } + // Many number options assume their value is in the signed int range. if (value < INT_MIN || value > INT_MAX) { return e_invarg; @@ -4534,19 +4603,36 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Don't do this while starting up, failure or recursively. if (!starting && errmsg == NULL && *get_vim_var_str(VV_OPTION_TYPE) == NUL) { char buf_old[NUMBUFLEN]; + char buf_old_global[NUMBUFLEN]; char buf_new[NUMBUFLEN]; char buf_type[7]; vim_snprintf(buf_old, ARRAY_SIZE(buf_old), "%ld", old_value); + vim_snprintf(buf_old_global, ARRAY_SIZE(buf_old_global), "%ld", old_global_value); vim_snprintf(buf_new, ARRAY_SIZE(buf_new), "%ld", value); vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", (opt_flags & OPT_LOCAL) ? "local" : "global"); set_vim_var_string(VV_OPTION_NEW, buf_new, -1); set_vim_var_string(VV_OPTION_OLD, buf_old, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, buf_old_global, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, buf_old, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } @@ -4565,7 +4651,15 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, return (char *)errmsg; } -static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *newval) +/// Trigger the OptionSet autocommand. +/// "opt_idx" is the index of the option being set. +/// "opt_flags" can be OPT_LOCAL etc. +/// "oldval" the old value +/// "oldval_l" the old local value (only non-NULL if global and local value are set) +/// "oldval_g" the old global value (only non-NULL if global and local value are set) +/// "newval" the new value +static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l, + char *oldval_g, char *newval) { // Don't do this recursively. if (oldval != NULL @@ -4578,8 +4672,24 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, set_vim_var_string(VV_OPTION_OLD, oldval, -1); set_vim_var_string(VV_OPTION_NEW, newval, -1); set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, NULL, false, NULL); + if (opt_flags & OPT_LOCAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + if (opt_flags & OPT_GLOBAL) { + set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1); + } + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { + set_vim_var_string(VV_OPTION_COMMAND, "set", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1); + } + if (opt_flags & OPT_MODELINE) { + set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1); + } + apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); } } diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 8049b3b80e..dce4b0c187 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,6 +13,10 @@ # include "nvim/os/unix_defs.h" #endif +#if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX) +#define NAME_MAX _XOPEN_NAME_MAX +#endif + #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 24ecf5c24f..450bc75ffb 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -15,7 +15,7 @@ # include <libutil.h> #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include <util.h> -#else +#elif !defined(__sun) # include <pty.h> #endif @@ -198,7 +198,9 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL termios->c_cflag = CS8|CREAD; termios->c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; - cfsetspeed(termios, 38400); + // not using cfsetspeed, not available on all platforms + cfsetispeed(termios, 38400); + cfsetospeed(termios, 38400); #ifdef IUTF8 termios->c_iflag |= IUTF8; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index bec3bc9648..e67efb8ea0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1760,13 +1760,14 @@ static char_u *regpiece(int *flagp) break; } if (re_multi_type(peekchr()) != NOT_MULTI) { - /* Can't have a multi follow a multi. */ - if (peekchr() == Magic('*')) - sprintf((char *)IObuff, _("E61: Nested %s*"), - reg_magic >= MAGIC_ON ? "" : "\\"); - else - sprintf((char *)IObuff, _("E62: Nested %s%c"), - reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + // Can't have a multi follow a multi. + if (peekchr() == Magic('*')) { + snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"), + reg_magic >= MAGIC_ON ? "" : "\\"); + } else { + snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"), + reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + } EMSG_RET_NULL((char *)IObuff); } @@ -1926,11 +1927,11 @@ static char_u *regatom(int *flagp) case Magic('{'): case Magic('*'): c = no_Magic(c); - sprintf((char *)IObuff, _("E64: %s%c follows nothing"), - (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) - ? "" : "\\", c); + snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) + ? "" : "\\", c); EMSG_RET_NULL((char *)IObuff); - /* NOTREACHED */ + // NOTREACHED case Magic('~'): /* previous substitute pattern */ if (reg_prev_sub != NULL) { @@ -3152,8 +3153,8 @@ static int read_limits(long *minval, long *maxval) regparse++; // Allow either \{...} or \{...\} } if (*regparse != '}') { - sprintf((char *)IObuff, _("E554: Syntax error in %s{...}"), - reg_magic == MAGIC_ALL ? "" : "\\"); + snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"), + reg_magic == MAGIC_ALL ? "" : "\\"); EMSG_RET_FAIL((char *)IObuff); } @@ -7263,9 +7264,10 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags) if (f) { fprintf(f, "Syntax error in \"%s\"\n", expr); fclose(f); - } else + } else { semsg("(NFA) Could not open \"%s\" to write !!!", - BT_REGEXP_DEBUG_LOG_NAME); + BT_REGEXP_DEBUG_LOG_NAME); + } } #endif // If the NFA engine failed, try the backtracking engine. The NFA engine diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2ce2be0bfd..a666b9c8b0 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -451,9 +451,11 @@ int update_screen(int type) // reset cmdline_row now (may have been changed temporarily) compute_cmdrow(); + bool hl_changed = false; // Check for changed highlighting if (need_highlight_changed) { highlight_changed(); + hl_changed = true; } if (type == CLEAR) { // first clear screen @@ -554,7 +556,7 @@ int update_screen(int type) * buffer. Each buffer must only be done once. */ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - update_window_hl(wp, type >= NOT_VALID); + update_window_hl(wp, type >= NOT_VALID || hl_changed); buf_T *buf = wp->w_buffer; if (buf->b_mod_set) { @@ -1692,7 +1694,7 @@ static void win_update(win_T *wp, Providers *providers) if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; j = win_get_fill(wp, wp->w_botline); - if (j > 0 && !wp->w_botfill) { + if (j > 0 && !wp->w_botfill && row < wp->w_grid.Rows) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines foldinfo_T info = FOLDINFO_INIT; @@ -6930,7 +6932,7 @@ int showmode(void) do_mode = ((p_smd && msg_silent == 0) && ((State & TERM_FOCUS) || (State & INSERT) - || restart_edit + || restart_edit != NUL || VIsual_active)); if (do_mode || reg_recording != 0) { // Don't show mode right now, when not redrawing or inside a mapping. @@ -7010,7 +7012,7 @@ int showmode(void) } msg_puts_attr(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' - || restart_edit == 'a') { + || restart_edit == 'a' || restart_edit == 'A') { msg_puts_attr(_(" (insert)"), attr); } else if (restart_edit == 'R') { msg_puts_attr(_(" (replace)"), attr); diff --git a/src/nvim/strings.c b/src/nvim/strings.c index c58e052ae9..27f93fe4ce 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -394,6 +394,18 @@ void del_trailing_spaces(char_u *ptr) } } +#if !defined(HAVE_STRNLEN) +size_t xstrnlen(const char *s, size_t n) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + const char *end = memchr(s, '\0', n); + if (end == NULL) { + return n; + } + return end - s; +} +#endif + #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) /* * Compare two strings, ignoring case, using current locale. diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 1f4d3adc92..483d2df778 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -141,12 +141,12 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc /// type == DT_LTAG: use location list for displaying tag matches /// type == DT_FREE: free cached matches /// -/// for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise +/// for cscope, returns true if we jumped to tag or aborted, false otherwise /// /// @param tag tag (pattern) to jump to /// @param forceit :ta with ! /// @param verbose print "tag not found" message -int do_tag(char_u *tag, int type, int count, int forceit, int verbose) +bool do_tag(char_u *tag, int type, int count, int forceit, int verbose) { taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; @@ -163,7 +163,7 @@ int do_tag(char_u *tag, int type, int count, int forceit, int verbose) int error_cur_match = 0; int save_pos = false; fmark_T saved_fmark; - int jumped_to_tag = false; + bool jumped_to_tag = false; int new_num_matches; char_u **new_matches; int use_tagstack; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 83ade74db1..2d32b6bac4 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -46,6 +46,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/change.h" +#include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/event/loop.h" #include "nvim/event/time.h" @@ -464,9 +465,7 @@ static void terminal_check_cursor(void) row_to_linenr(term, term->cursor.row)); // Nudge cursor when returning to normal-mode. int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); - curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); - curwin->w_cursor.coladd = 0; - mb_check_adjust_col(curwin); + coladvance(MAX(0, term->cursor.col + off)); } // Function executed before each iteration of terminal mode. @@ -1508,6 +1507,13 @@ static void refresh_screen(Terminal *term, buf_T *buf) // Terminal height may have decreased before `invalid_end` reflects it. term->invalid_end = MIN(term->invalid_end, height); + // There are no invalid rows. + if (term->invalid_start >= term->invalid_end) { + term->invalid_start = INT_MAX; + term->invalid_end = -1; + return; + } + for (int r = term->invalid_start, linenr = row_to_linenr(term, r); r < term->invalid_end; r++, linenr++) { fetch_row(term, r, width); diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 49993c03aa..ab047fd2a8 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -197,7 +197,12 @@ func RunTheTest(test) " Close any extra tab pages and windows and make the current one not modified. while tabpagenr('$') > 1 + let winid = win_getid() quit! + if winid == win_getid() + echoerr 'Could not quit window' + break + endif endwhile while 1 diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 49d56349a5..4e1a24af61 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -526,8 +526,7 @@ func Test_autocmd_blast_badd() call writefile(content, 'XblastBall') call system(GetVimCommand() .. ' --clean -S XblastBall') - " call assert_match('OK', readfile('Xerrors')->join()) - call assert_match('OK', join(readfile('Xerrors'))) + call assert_match('OK', readfile('Xerrors')->join()) call delete('XblastBall') call delete('Xerrors') @@ -580,9 +579,10 @@ func Test_empty_doau() endfunc func s:AutoCommandOptionSet(match) + let template = "Option: <%s>, OldVal: <%s>, OldValLocal: <%s>, OldValGlobal: <%s>, NewVal: <%s>, Scope: <%s>, Command: <%s>\n" let item = remove(g:options, 0) - let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3]) - let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type) + let expected = printf(template, item[0], item[1], item[2], item[3], item[4], item[5], item[6]) + let actual = printf(template, a:match, v:option_old, v:option_oldlocal, v:option_oldglobal, v:option_new, v:option_type, v:option_command) let g:opt = [expected, actual] "call assert_equal(expected, actual) endfunc @@ -596,130 +596,593 @@ func Test_OptionSet() au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) " 1: Setting number option" - let g:options = [['number', 0, 1, 'global']] + let g:options = [['number', 0, 0, 0, 1, 'global', 'set']] set nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 2: Setting local number option" - let g:options = [['number', 1, 0, 'local']] + let g:options = [['number', 1, 1, '', 0, 'local', 'setlocal']] setlocal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 3: Setting global number option" - let g:options = [['number', 1, 0, 'global']] + let g:options = [['number', 1, '', 1, 0, 'global', 'setglobal']] setglobal nonu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 4: Setting local autoindent option" - let g:options = [['autoindent', 0, 1, 'local']] + let g:options = [['autoindent', 0, 0, '', 1, 'local', 'setlocal']] setlocal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 5: Setting global autoindent option" - let g:options = [['autoindent', 0, 1, 'global']] + let g:options = [['autoindent', 0, '', 0, 1, 'global', 'setglobal']] setglobal ai call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 6: Setting global autoindent option" - let g:options = [['autoindent', 1, 0, 'global']] + let g:options = [['autoindent', 1, 1, 1, 0, 'global', 'set']] + set ai! + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 6a: Setting global autoindent option" + let g:options = [['autoindent', 1, 1, 0, 0, 'global', 'set']] + noa setlocal ai + noa setglobal noai set ai! call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " Should not print anything, use :noa " 7: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nonu - call assert_equal([['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 8: Setting several global list and number option" - let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + let g:options = [['list', 0, 0, 0, 1, 'global', 'set'], ['number', 0, 0, 0, 1, 'global', 'set']] set list nu call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 9: don't trigger OptionSet" - let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + let g:options = [['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']] noa set nolist nonu - call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) + call assert_equal([['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid'], ['invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid', 'invalid']], g:options) call assert_equal(g:opt[0], g:opt[1]) " 10: Setting global acd" - let g:options = [['autochdir', 0, 1, 'local']] + let g:options = [['autochdir', 0, 0, '', 1, 'local', 'setlocal']] setlocal acd call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 11: Setting global autoread (also sets local value)" - let g:options = [['autoread', 0, 1, 'global']] + let g:options = [['autoread', 0, 0, 0, 1, 'global', 'set']] set ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 12: Setting local autoread" - let g:options = [['autoread', 1, 1, 'local']] + let g:options = [['autoread', 1, 1, '', 1, 'local', 'setlocal']] setlocal ar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 13: Setting global autoread" - let g:options = [['autoread', 1, 0, 'global']] + let g:options = [['autoread', 1, '', 1, 0, 'global', 'setglobal']] setglobal invar call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 14: Setting option backspace through :let" - let g:options = [['backspace', '', 'eol,indent,start', 'global']] + let g:options = [['backspace', '', '', '', 'eol,indent,start', 'global', 'set']] let &bs = "eol,indent,start" call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 15: Setting option backspace through setbufvar()" - let g:options = [['backup', 0, 1, 'local']] + let g:options = [['backup', 0, 0, '', 1, 'local', 'setlocal']] " try twice, first time, shouldn't trigger because option name is invalid, " second time, it should trigger - call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") + let bnum = bufnr('%') + call assert_fails("call setbufvar(bnum, '&l:bk', 1)", 'E355:') " should trigger, use correct option name - call setbufvar(1, '&backup', 1) + call setbufvar(bnum, '&backup', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 16: Setting number option using setwinvar" - let g:options = [['number', 0, 1, 'local']] + let g:options = [['number', 0, 0, '', 1, 'local', 'setlocal']] call setwinvar(0, '&number', 1) call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) " 17: Setting key option, shouldn't trigger" - let g:options = [['key', 'invalid', 'invalid1', 'invalid']] + let g:options = [['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']] setlocal key=blah setlocal key= - call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) + call assert_equal([['key', 'invalid', 'invalid1', 'invalid2', 'invalid3', 'invalid4', 'invalid5']], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 18: Setting string option" + + " 18a: Setting string global option" + let oldval = &backupext + let g:options = [['backupext', oldval, oldval, oldval, 'foo', 'global', 'set']] + set backupext=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18b: Resetting string global option" + let g:options = [['backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set backupext& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18c: Setting global string global option" + let g:options = [['backupext', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal backupext=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18d: Setting local string global option" + " As this is a global option this sets the global value even though + " :setlocal is used! + noa set backupext& " Reset global and local value (without triggering autocmd) + let g:options = [['backupext', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal backupext=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18e: Setting again string global option" + noa setglobal backupext=ext_global " Reset global and local value (without triggering autocmd) + noa setlocal backupext=ext_local " Sets the global(!) value! + let g:options = [['backupext', 'ext_local', 'ext_local', 'ext_local', 'fuu', 'global', 'set']] + set backupext=fuu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 19a: Setting string global-local (to buffer) option" let oldval = &tags - let g:options = [['tags', oldval, 'tagpath', 'global']] + let g:options = [['tags', oldval, oldval, oldval, 'tagpath', 'global', 'set']] set tags=tagpath call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) - " 1l: Resetting string option" - let g:options = [['tags', 'tagpath', oldval, 'global']] + " 19b: Resetting string global-local (to buffer) option" + let g:options = [['tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set']] set tags& call assert_equal([], g:options) call assert_equal(g:opt[0], g:opt[1]) + " 19c: Setting global string global-local (to buffer) option " + let g:options = [['tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal']] + setglobal tags=tagpath1 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19d: Setting local string global-local (to buffer) option" + let g:options = [['tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal']] + setlocal tags=tagpath2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19e: Setting again string global-local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags=tag_local + let g:options = [['tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 19f: Setting string global-local (to buffer) option to an empty string" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa set tags=tag_global " Reset global and local value (without triggering autocmd) + noa setlocal tags= " empty string + let g:options = [['tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 20a: Setting string local (to buffer) option" + let oldval = &spelllang + let g:options = [['spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set']] + set spelllang=elvish,klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20b: Resetting string local (to buffer) option" + let g:options = [['spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set']] + set spelllang& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20c: Setting global string local (to buffer) option" + let g:options = [['spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal']] + setglobal spelllang=elvish + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20d: Setting local string local (to buffer) option" + noa set spelllang& " Reset global and local value (without triggering autocmd) + let g:options = [['spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal']] + setlocal spelllang=klingon + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 20e: Setting again string local (to buffer) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal spelllang=spellglobal " Reset global and local value (without triggering autocmd) + noa setlocal spelllang=spelllocal + let g:options = [['spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set']] + set spelllang=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 21a: Setting string global-local (to window) option" + let oldval = &statusline + let g:options = [['statusline', oldval, oldval, oldval, 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21b: Resetting string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + let g:options = [['statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set']] + set statusline& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21c: Setting global string global-local (to window) option" + let g:options = [['statusline', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal statusline=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21d: Setting local string global-local (to window) option" + noa set statusline& " Reset global and local value (without triggering autocmd) + let g:options = [['statusline', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal statusline=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 21e: Setting again string global-local (to window) option" + " Note: v:option_old is the old global value for global-local string options + " but the old local value for all other kinds of options. + noa setglobal statusline=bar " Reset global and local value (without triggering autocmd) + noa setlocal statusline=baz + let g:options = [['statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set']] + set statusline=foo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 22a: Setting string local (to window) option" + let oldval = &foldignore + let g:options = [['foldignore', oldval, oldval, oldval, 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22b: Resetting string local (to window) option" + let g:options = [['foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set']] + set foldignore& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22c: Setting global string local (to window) option" + let g:options = [['foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal']] + setglobal foldignore=bar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22d: Setting local string local (to window) option" + noa set foldignore& " Reset global and local value (without triggering autocmd) + let g:options = [['foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal']] + setlocal foldignore=baz + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 22e: Setting again string local (to window) option" + noa setglobal foldignore=glob " Reset global and local value (without triggering autocmd) + noa setlocal foldignore=loc + let g:options = [['foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set']] + set foldignore=fo + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 23a: Setting global number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '', '1', '2', 'global', 'setglobal']] + setglobal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23b: Setting local number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '', '2', 'local', 'setlocal']] + setlocal cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23c: Setting again number global option" + noa setglobal cmdheight=8 " Reset global and local value (without triggering autocmd) + noa setlocal cmdheight=1 " Sets the global(!) value! + let g:options = [['cmdheight', '1', '1', '1', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 23d: Setting again number global option" + noa set cmdheight=8 " Reset global and local value (without triggering autocmd) + let g:options = [['cmdheight', '8', '8', '8', '2', 'global', 'set']] + set cmdheight=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 24a: Setting global number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '8', '', '8', '2', 'global', 'setglobal']] + setglobal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24b: Setting local number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '', '2', 'local', 'setlocal']] + setlocal undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24c: Setting again number global-local (to buffer) option" + noa setglobal undolevels=8 " Reset global and local value (without triggering autocmd) + noa setlocal undolevels=1 + let g:options = [['undolevels', '1', '1', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 24d: Setting again global number global-local (to buffer) option" + noa set undolevels=8 " Reset global and local value (without triggering autocmd) + let g:options = [['undolevels', '8', '8', '8', '2', 'global', 'set']] + set undolevels=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 25a: Setting global number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '8', '', '8', '2', 'global', 'setglobal']] + setglobal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25b: Setting local number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '', '2', 'local', 'setlocal']] + setlocal wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25c: Setting again number local (to buffer) option" + noa setglobal wrapmargin=8 " Reset global and local value (without triggering autocmd) + noa setlocal wrapmargin=1 + let g:options = [['wrapmargin', '1', '1', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 25d: Setting again global number local (to buffer) option" + noa set wrapmargin=8 " Reset global and local value (without triggering autocmd) + let g:options = [['wrapmargin', '8', '8', '8', '2', 'global', 'set']] + set wrapmargin=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 26: Setting number global-local (to window) option. + " Such option does currently not exist. + + + " 27a: Setting global number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '8', '', '8', '2', 'global', 'setglobal']] + setglobal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27b: Setting local number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '', '2', 'local', 'setlocal']] + setlocal foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27c: Setting again number local (to window) option" + noa setglobal foldcolumn=8 " Reset global and local value (without triggering autocmd) + noa setlocal foldcolumn=1 + let g:options = [['foldcolumn', '1', '1', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 27d: Setting again global number local (to window) option" + noa set foldcolumn=8 " Reset global and local value (without triggering autocmd) + let g:options = [['foldcolumn', '8', '8', '8', '2', 'global', 'set']] + set foldcolumn=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 28a: Setting global boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '', '1', '0', 'global', 'setglobal']] + setglobal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28b: Setting local boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28c: Setting again boolean global option" + noa setglobal nowrapscan " Reset global and local value (without triggering autocmd) + noa setlocal wrapscan " Sets the global(!) value! + let g:options = [['wrapscan', '1', '1', '1', '0', 'global', 'set']] + set nowrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 28d: Setting again global boolean global option" + noa set nowrapscan " Reset global and local value (without triggering autocmd) + let g:options = [['wrapscan', '0', '0', '0', '1', 'global', 'set']] + set wrapscan + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 29a: Setting global boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '0', '', '0', '1', 'global', 'setglobal']] + setglobal autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29b: Setting local boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '', '0', 'local', 'setlocal']] + setlocal noautoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29c: Setting again boolean global-local (to buffer) option" + noa setglobal noautoread " Reset global and local value (without triggering autocmd) + noa setlocal autoread + let g:options = [['autoread', '1', '1', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 29d: Setting again global boolean global-local (to buffer) option" + noa set noautoread " Reset global and local value (without triggering autocmd) + let g:options = [['autoread', '0', '0', '0', '1', 'global', 'set']] + set autoread + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 30a: Setting global boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30b: Setting local boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30c: Setting again boolean local (to buffer) option" + noa setglobal nocindent " Reset global and local value (without triggering autocmd) + noa setlocal cindent + let g:options = [['cindent', '1', '1', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 30d: Setting again global boolean local (to buffer) option" + noa set nocindent " Reset global and local value (without triggering autocmd) + let g:options = [['cindent', '0', '0', '0', '1', 'global', 'set']] + set cindent + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 31: Setting boolean global-local (to window) option + " Currently no such option exists. + + + " 32a: Setting global boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '0', '', '0', '1', 'global', 'setglobal']] + setglobal cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32b: Setting local boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '', '0', 'local', 'setlocal']] + setlocal nocursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32c: Setting again boolean local (to window) option" + noa setglobal nocursorcolumn " Reset global and local value (without triggering autocmd) + noa setlocal cursorcolumn + let g:options = [['cursorcolumn', '1', '1', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 32d: Setting again global boolean local (to window) option" + noa set nocursorcolumn " Reset global and local value (without triggering autocmd) + let g:options = [['cursorcolumn', '0', '0', '0', '1', 'global', 'set']] + set cursorcolumn + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + + " 33: Test autocommands when an option value is converted internally. + noa set backspace=1 " Reset global and local value (without triggering autocmd) + let g:options = [['backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set']] + set backspace=2 + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Cleanup au! OptionSet " set tags& - for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'tags'] + for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp', 'backupext', 'tags', 'spelllang', 'statusline', 'foldignore', 'cmdheight', 'undolevels', 'wrapmargin', 'foldcolumn', 'wrapscan', 'autoread', 'cindent', 'cursorcolumn'] exe printf(":set %s&vim", opt) endfor call test_override('starting', 0) diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 277050876e..8d592f21ea 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -432,7 +432,7 @@ func Test_breakindent11_vartabs() call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4 vts=4') let text = getline(2) let width = strlen(text[1:]) + 2->indent() + strlen(&sbr) * 3 " text wraps 3 times - call assert_equal(width, strdisplaywidth(text)) + call assert_equal(width, text->strdisplaywidth()) call s:close_windows('set sbr= vts&') endfunc diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index c04c8fbf60..a6eb93b4be 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -20,6 +20,9 @@ function Test_getbufwintabinfo() call assert_equal('vim', l[0].variables.editor) call assert_notequal(-1, index(l[0].windows, '%'->bufwinid())) + let l = '%'->getbufinfo() + call assert_equal(bufnr('%'), l[0].bufnr) + " Test for getbufinfo() with 'bufmodified' call assert_equal(0, len(getbufinfo({'bufmodified' : 1}))) call setbufline('Xtestfile1', 1, ["Line1"]) @@ -48,7 +51,7 @@ function Test_getbufwintabinfo() tabnew | let w3_id = win_getid() new | let w4_id = win_getid() vert new | let w5_id = win_getid() - call setwinvar(0, 'signal', 'green') + eval 'green'->setwinvar(0, 'signal') tabfirst let winlist = getwininfo() call assert_equal(5, len(winlist)) diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 57db0a2544..76a2620be0 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -101,7 +101,7 @@ func Test_chdir_func() call assert_match('^\[global\] .*/Xdir$', trim(execute('verbose pwd'))) call chdir('..') call assert_equal('y', fnamemodify(getcwd(1, 2), ':t')) - call assert_equal('z', fnamemodify(getcwd(3, 2), ':t')) + call assert_equal('z', fnamemodify(3->getcwd(2), ':t')) tabnext | wincmd t call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd'))) call chdir('..') diff --git a/src/nvim/testdir/test_charsearch.vim b/src/nvim/testdir/test_charsearch.vim index 17a49e02be..6f09e85a42 100644 --- a/src/nvim/testdir/test_charsearch.vim +++ b/src/nvim/testdir/test_charsearch.vim @@ -20,7 +20,7 @@ func Test_charsearch() " check that setcharsearch() changes the settings. 3 normal! ylfep - call setcharsearch({'char': 'k'}) + eval {'char': 'k'}->setcharsearch() normal! ;p call setcharsearch({'forward': 0}) normal! $;p diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim index f3db472b03..922803438f 100644 --- a/src/nvim/testdir/test_clientserver.vim +++ b/src/nvim/testdir/test_clientserver.vim @@ -82,7 +82,7 @@ func Test_client_server() call remote_send(name, ":call server2client(expand('<client>'), 'got it')\<CR>", 'g:myserverid') call assert_equal('got it', g:myserverid->remote_read(2)) - call remote_send(name, ":call server2client(expand('<client>'), 'another')\<CR>", 'g:myserverid') + call remote_send(name, ":eval expand('<client>')->server2client('another')\<CR>", 'g:myserverid') let peek_result = 'nothing' let r = g:myserverid->remote_peek('peek_result') " unpredictable whether the result is already available. diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 75e17b47b8..49a5386337 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -905,7 +905,7 @@ func Test_setcmdpos() call assert_equal('"12ab', @:) " setcmdpos() returns 1 when not editing the command line. - call assert_equal(1, setcmdpos(3)) + call assert_equal(1, 3->setcmdpos()) endfunc func Test_cmdline_overstrike() diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index c3de7d0050..aaa2301bca 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -38,10 +38,9 @@ func Test_compiler() endfunc func GetCompilerNames() - " return glob('$VIMRUNTIME/compiler/*.vim', 0, 1) - " \ ->map({i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')}) - " \ ->sort() - return sort(map(glob('$VIMRUNTIME/compiler/*.vim', 0, 1), {i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')})) + return glob('$VIMRUNTIME/compiler/*.vim', 0, 1) + \ ->map({i, v -> substitute(v, '.*[\\/]\([a-zA-Z0-9_\-]*\).vim', '\1', '')}) + \ ->sort() endfunc func Test_compiler_without_arg() @@ -54,8 +53,7 @@ func Test_compiler_without_arg() endfunc func Test_compiler_completion() - " let clist = GetCompilerNames()->join(' ') - let clist = join(GetCompilerNames(), ' ') + let clist = GetCompilerNames()->join(' ') call feedkeys(":compiler \<C-A>\<C-B>\"\<CR>", 'tx') call assert_match('^"compiler ' .. clist .. '$', @:) diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 46847e0663..e8c4a952ee 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -37,7 +37,7 @@ endfunc " Very short version of what matchparen does. function s:Highlight_Matching_Pair() let save_cursor = getcurpos() - call setpos('.', save_cursor) + eval save_cursor->setpos('.') endfunc func Test_curswant_with_autocommand() @@ -82,11 +82,11 @@ func Test_screenpos() call assert_equal({'row': winrow, \ 'col': wincol + 0, \ 'curscol': wincol + 7, - \ 'endcol': wincol + 7}, screenpos(winid, 1, 1)) + \ 'endcol': wincol + 7}, winid->screenpos(1, 1)) call assert_equal({'row': winrow, \ 'col': wincol + 13, \ 'curscol': wincol + 13, - \ 'endcol': wincol + 13}, screenpos(winid, 1, 7)) + \ 'endcol': wincol + 13}, winid->screenpos(1, 7)) call assert_equal({'row': winrow + 2, \ 'col': wincol + 1, \ 'curscol': wincol + 1, diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 61da3cbcaa..3a0c615cf6 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -387,7 +387,7 @@ func Test_diffoff() call setline(1, ['One', '', 'Two', 'Three']) diffthis redraw - call assert_notequal(normattr, screenattr(1, 1)) + call assert_notequal(normattr, 1->screenattr(1)) diffoff! redraw call assert_equal(normattr, screenattr(1, 1)) diff --git a/src/nvim/testdir/test_environ.vim b/src/nvim/testdir/test_environ.vim index cc15b63824..dd34983ee5 100644 --- a/src/nvim/testdir/test_environ.vim +++ b/src/nvim/testdir/test_environ.vim @@ -22,7 +22,7 @@ endfunc func Test_setenv() unlet! $TESTENV - call setenv('TEST ENV', 'foo') + eval 'foo'->setenv('TEST ENV') call assert_equal('foo', getenv('TEST ENV')) call setenv('TEST ENV', v:null) call assert_equal(v:null, getenv('TEST ENV')) diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim index f2c7da0aa9..2cb6d73407 100644 --- a/src/nvim/testdir/test_execute_func.vim +++ b/src/nvim/testdir/test_execute_func.vim @@ -99,7 +99,7 @@ func Test_win_execute() if has('textprop') let popupwin = popup_create('the popup win', {'line': 2, 'col': 3}) redraw - let line = win_execute(popupwin, 'echo getline(1)') + let line = 'echo getline(1)'->win_execute(popupwin) call assert_match('the popup win', line) call popup_close(popupwin) diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 6343c47fde..1d7fd3e385 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -56,7 +56,7 @@ endfunc func Test_strgetchar() call assert_equal(char2nr('a'), strgetchar('axb', 0)) - call assert_equal(char2nr('x'), strgetchar('axb', 1)) + call assert_equal(char2nr('x'), 'axb'->strgetchar(1)) call assert_equal(char2nr('b'), strgetchar('axb', 2)) call assert_equal(-1, strgetchar('axb', -1)) @@ -66,7 +66,7 @@ endfunc func Test_strcharpart() call assert_equal('a', strcharpart('axb', 0, 1)) - call assert_equal('x', strcharpart('axb', 1, 1)) + call assert_equal('x', 'axb'->strcharpart(1, 1)) call assert_equal('b', strcharpart('axb', 2, 1)) call assert_equal('xb', strcharpart('axb', 1)) @@ -493,7 +493,7 @@ func Test_setmatches() let set[0]['conceal'] = 5 let exp[0]['conceal'] = '5' endif - call setmatches(set) + eval set->setmatches() call assert_equal(exp, getmatches()) endfunc diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 69edbc227d..31052ce47d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -502,7 +502,7 @@ let s:filename_checks = { \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], - \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], + \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], \ 'tf': ['file.tf', '.tfrc', 'tfrc'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], @@ -568,6 +568,7 @@ let s:filename_checks = { \ 'yaml': ['file.yaml', 'file.yml'], \ 'raml': ['file.raml'], \ 'z8a': ['file.z8a'], + \ 'zig': ['file.zig'], \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 5586fe2151..6da1b3d4a0 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -809,8 +809,7 @@ func Test_undo_fold_deletion() g/"/d undo redo - " eval getline(1, '$')->assert_equal(['']) - eval assert_equal(getline(1, '$'), ['']) + eval getline(1, '$')->assert_equal(['']) set fdm&vim bwipe! diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 4a2ade5afa..0edbeb420a 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -143,7 +143,7 @@ func Test_str2nr() call assert_equal(-123456789, str2nr('-123456789')) call assert_equal(5, str2nr('101', 2)) - call assert_equal(5, str2nr('0b101', 2)) + call assert_equal(5, '0b101'->str2nr(2)) call assert_equal(5, str2nr('0B101', 2)) call assert_equal(-5, str2nr('-101', 2)) call assert_equal(-5, str2nr('-0b101', 2)) @@ -200,7 +200,7 @@ func Test_strftime() " of strftime() can be 17 or 18, depending on timezone. call assert_match('^2017-01-1[78]$', strftime('%Y-%m-%d', 1484695512)) " - call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', strftime('%Y-%m-%d %H:%M:%S')) + call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', '%Y-%m-%d %H:%M:%S'->strftime()) call assert_fails('call strftime([])', 'E730:') call assert_fails('call strftime("%Y", [])', 'E745:') @@ -307,13 +307,19 @@ func Test_resolve_unix() call assert_equal('/', resolve('/')) endfunc +func s:normalize_fname(fname) + let ret = substitute(a:fname, '\', '/', 'g') + let ret = substitute(ret, '//', '/', 'g') + return ret->tolower() +endfunc + func Test_simplify() call assert_equal('', simplify('')) call assert_equal('/', simplify('/')) call assert_equal('/', simplify('/.')) call assert_equal('/', simplify('/..')) call assert_equal('/...', simplify('/...')) - call assert_equal('./dir/file', simplify('./dir/file')) + call assert_equal('./dir/file', './dir/file'->simplify()) call assert_equal('./dir/file', simplify('.///dir//file')) call assert_equal('./dir/file', simplify('./dir/./file')) call assert_equal('./file', simplify('./dir/../file')) @@ -346,7 +352,7 @@ func Test_setbufvar_options() wincmd h let wh = winheight(0) let dummy_buf = bufnr('dummy_buf2', v:true) - call setbufvar(dummy_buf, '&buftype', 'nofile') + eval 'nofile'->setbufvar(dummy_buf, '&buftype') execute 'belowright vertical split #' . dummy_buf call assert_equal(wh, winheight(0)) @@ -375,7 +381,7 @@ endfunc func Test_strpart() call assert_equal('de', strpart('abcdefg', 3, 2)) call assert_equal('ab', strpart('abcdefg', -2, 4)) - call assert_equal('abcdefg', strpart('abcdefg', -2)) + call assert_equal('abcdefg', 'abcdefg'->strpart(-2)) call assert_equal('fg', strpart('abcdefg', 5, 4)) call assert_equal('defg', strpart('abcdefg', 3)) @@ -469,7 +475,7 @@ func Test_toupper() \ toupper(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')) " Test with a few lowercase diacritics. - call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", toupper("aàáâãäåāăąǎǟǡả")) + call assert_equal("AÀÁÂÃÄÅĀĂĄǍǞǠẢ", "aàáâãäåāăąǎǟǡả"->toupper()) call assert_equal("BḂḆ", toupper("bḃḇ")) call assert_equal("CÇĆĈĊČ", toupper("cçćĉċč")) call assert_equal("DĎĐḊḎḐ", toupper("dďđḋḏḑ")) @@ -532,6 +538,11 @@ func Test_toupper() call toupper("123\xC0\x80\xC0") endfunc +func Test_tr() + call assert_equal('foo', tr('bar', 'bar', 'foo')) + call assert_equal('zxy', 'cab'->tr('abc', 'xyz')) +endfunc + " Tests for the mode() function let current_modes = '' func Save_mode() @@ -809,11 +820,11 @@ endfunc func Test_stridx() call assert_equal(-1, stridx('', 'l')) call assert_equal(0, stridx('', '')) - call assert_equal(0, stridx('hello', '')) + call assert_equal(0, 'hello'->stridx('')) call assert_equal(-1, stridx('hello', 'L')) call assert_equal(2, stridx('hello', 'l', -1)) call assert_equal(2, stridx('hello', 'l', 0)) - call assert_equal(2, stridx('hello', 'l', 1)) + call assert_equal(2, 'hello'->stridx('l', 1)) call assert_equal(3, stridx('hello', 'l', 3)) call assert_equal(-1, stridx('hello', 'l', 4)) call assert_equal(-1, stridx('hello', 'l', 10)) @@ -826,7 +837,7 @@ func Test_strridx() call assert_equal(0, strridx('', '')) call assert_equal(5, strridx('hello', '')) call assert_equal(-1, strridx('hello', 'L')) - call assert_equal(3, strridx('hello', 'l')) + call assert_equal(3, 'hello'->strridx('l')) call assert_equal(3, strridx('hello', 'l', 10)) call assert_equal(3, strridx('hello', 'l', 3)) call assert_equal(2, strridx('hello', 'l', 2)) @@ -1219,7 +1230,7 @@ func Test_shellescape() let save_shell = &shell set shell=bash call assert_equal("'text'", shellescape('text')) - call assert_equal("'te\"xt'", shellescape('te"xt')) + call assert_equal("'te\"xt'", 'te"xt'->shellescape()) call assert_equal("'te'\\''xt'", shellescape("te'xt")) call assert_equal("'te%xt'", shellescape("te%xt")) @@ -1293,7 +1304,7 @@ endfunc func Test_trim() call assert_equal("Testing", trim(" \t\r\r\x0BTesting \t\n\r\n\t\x0B\x0B")) - call assert_equal("Testing", trim(" \t \r\r\n\n\x0BTesting \t\n\r\n\t\x0B\x0B")) + call assert_equal("Testing", " \t \r\r\n\n\x0BTesting \t\n\r\n\t\x0B\x0B"->trim()) call assert_equal("RESERVE", trim("xyz \twwRESERVEzyww \t\t", " wxyz\t")) call assert_equal("wRE \tSERVEzyww", trim("wRE \tSERVEzyww")) call assert_equal("abcd\t xxxx tail", trim(" \tabcd\t xxxx tail")) @@ -1330,7 +1341,7 @@ func Test_func_range_with_edit() " is invalid in that buffer. call writefile(['just one line'], 'Xfuncrange2') new - call setline(1, 10->range()) + eval 10->range()->setline(1) write Xfuncrange1 call assert_fails('5,8call EditAnotherFile()', 'E16:') @@ -1560,7 +1571,7 @@ func Test_bufadd_bufload() call assert_equal([''], getbufline(buf, 1, '$')) let curbuf = bufnr('') - call writefile(['some', 'text'], 'XotherName') + eval ['some', 'text']->writefile('XotherName') let buf = 'XotherName'->bufadd() call assert_notequal(0, buf) eval 'XotherName'->bufexists()->assert_equal(1) diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim index 9906b00222..f4ee539803 100644 --- a/src/nvim/testdir/test_listchars.vim +++ b/src/nvim/testdir/test_listchars.vim @@ -25,7 +25,7 @@ func Test_listchars() redraw! for i in range(1, 5) call cursor(i, 1) - call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + call assert_equal([expected[i - 1]], ScreenLines(i, '$'->virtcol())) endfor set listchars-=trail:< diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 2fd82a4b6d..b3035d73ce 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -227,7 +227,7 @@ func Test_getmarklist() call cursor(2, 2) normal mr call assert_equal({'mark' : "'r", 'pos' : [bufnr(), 2, 2, 0]}, - \ getmarklist(bufnr())[0]) - call assert_equal([], getmarklist({})) + \ bufnr()->getmarklist()[0]) + call assert_equal([], {}->getmarklist()) close! endfunc diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 34c8c49dd5..7bfac13ad8 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -6,7 +6,7 @@ endif function! s:screenline(lnum) abort let line = [] for c in range(1, winwidth(0)) - call add(line, nr2char(screenchar(a:lnum, c))) + call add(line, nr2char(a:lnum->screenchar(c))) endfor return s:trim(join(line, '')) endfunction diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 08586dffe1..2140fe21ea 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -87,7 +87,7 @@ func Test_echoerr() if has('float') call assert_equal("\n1.23 IgNoRe", execute(':echoerr 1.23 "IgNoRe"')) endif - call test_ignore_error('<lambda>') + eval '<lambda>'->test_ignore_error() call assert_match("function('<lambda>\\d*')", execute(':echoerr {-> 1234}')) call test_ignore_error('RESET') endfunc diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index c96c6a9678..057895047d 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -156,8 +156,7 @@ func Test_mksession_zero_winheight() wincmd _ mksession! Xtest_mks_zero set winminheight& - " let text = readfile('Xtest_mks_zero')->join() - let text = join(readfile('Xtest_mks_zero')) + let text = readfile('Xtest_mks_zero')->join() call delete('Xtest_mks_zero') close " check there is no divide by zero diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 41c689849b..7d9cada074 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -235,8 +235,7 @@ func Test_set_completion() call feedkeys(":set filetype=sshdconfi\<Tab>\<C-B>\"\<CR>", 'xt') call assert_equal('"set filetype=sshdconfig', @:) call feedkeys(":set filetype=a\<C-A>\<C-B>\"\<CR>", 'xt') - " call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) - call assert_equal('"set filetype=' .. join(getcompletion('a*', 'filetype')), @:) + call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) endfunc func Test_set_errors() diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim index 3da46eb1a6..8f94a8572b 100644 --- a/src/nvim/testdir/test_prompt_buffer.vim +++ b/src/nvim/testdir/test_prompt_buffer.vim @@ -41,6 +41,10 @@ func WriteScript(name) \ ' set nomodified', \ 'endfunc', \ '', + \ 'func SwitchWindows()', + \ ' call timer_start(0, {-> execute("wincmd p|wincmd p", "")})', + \ 'endfunc', + \ '', \ 'call setline(1, "other buffer")', \ 'set nomodified', \ 'new', @@ -89,9 +93,12 @@ func Test_prompt_editing() call term_sendkeys(buf, left . left . left . bs . '-') call WaitForAssert({-> assert_equal('cmd: -hel', term_getline(buf, 1))}) + call term_sendkeys(buf, "\<C-O>lz") + call WaitForAssert({-> assert_equal('cmd: -hzel', term_getline(buf, 1))}) + let end = "\<End>" call term_sendkeys(buf, end . "x") - call WaitForAssert({-> assert_equal('cmd: -helx', term_getline(buf, 1))}) + call WaitForAssert({-> assert_equal('cmd: -hzelx', term_getline(buf, 1))}) call term_sendkeys(buf, "\<C-U>exit\<CR>") call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) @@ -100,6 +107,28 @@ func Test_prompt_editing() call delete(scriptName) endfunc +func Test_prompt_switch_windows() + throw 'skipped: TODO' + call CanTestPromptBuffer() + let scriptName = 'XpromptSwitchWindows' + call WriteScript(scriptName) + + let buf = RunVimInTerminal('-S ' . scriptName, {'rows': 12}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<C-O>:call SwitchWindows()\<CR>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<Esc>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('^ *$', term_getline(buf, 12))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + func Test_prompt_garbage_collect() func MyPromptCallback(x, text) " NOP @@ -126,6 +155,14 @@ func Test_prompt_garbage_collect() bwipe! endfunc +func Test_prompt_backspace() + new + set buftype=prompt + call feedkeys("A123456\<Left>\<BS>\<Esc>", 'xt') + call assert_equal('% 12346', getline(1)) + bwipe! +endfunc + " Test for editing the prompt buffer func Test_prompt_buffer_edit() new @@ -145,10 +182,9 @@ func Test_prompt_buffer_edit() call assert_beeps("normal! \<C-X>") " pressing CTRL-W in the prompt buffer should trigger the window commands call assert_equal(1, winnr()) - " In Nvim, CTRL-W commands aren't usable from insert mode in a prompt buffer - " exe "normal A\<C-W>\<C-W>" - " call assert_equal(2, winnr()) - " wincmd w + exe "normal A\<C-W>\<C-W>" + call assert_equal(2, winnr()) + wincmd w close! call assert_equal(0, prompt_setprompt([], '')) endfunc @@ -165,9 +201,7 @@ func Test_prompt_buffer_getbufinfo() call assert_equal('This is a test: ', prompt_getprompt('%')) call prompt_setprompt( bufnr( '%' ), '' ) - " Nvim doesn't support method call syntax yet. - " call assert_equal('', '%'->prompt_getprompt()) - call assert_equal('', prompt_getprompt('%')) + call assert_equal('', '%'->prompt_getprompt()) call prompt_setprompt( bufnr( '%' ), 'Another: ' ) call assert_equal('Another: ', prompt_getprompt('%')) @@ -189,4 +223,38 @@ func Test_prompt_buffer_getbufinfo() %bwipe! endfunc +function! Test_prompt_while_writing_to_hidden_buffer() + throw 'skipped: TODO' + call CanTestPromptBuffer() + CheckUnix + + " Make a job continuously write to a hidden buffer, check that the prompt + " buffer is not affected. + let scriptName = 'XpromptscriptHiddenBuf' + let script =<< trim END + set buftype=prompt + call prompt_setprompt( bufnr(), 'cmd:' ) + let job = job_start(['/bin/sh', '-c', + \ 'while true; + \ do echo line; + \ sleep 0.1; + \ done'], #{out_io: 'buffer', out_name: ''}) + startinsert + END + eval script->writefile(scriptName) + + let buf = RunVimInTerminal('-S ' .. scriptName, {}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:test', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtest', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtesttest', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 8d8cc77025..f42b177c50 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -39,7 +39,7 @@ func Test_put_lines() call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1, '$')) " clean up bw! - call setreg('a', a[0], a[1]) + eval a[0]->setreg('a', a[1]) endfunc func Test_put_expr() diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index b38a59e98f..6db679c5f9 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -811,7 +811,7 @@ func Test_locationlist() " NOTE: problem 1: " intentionally not setting 'lnum' so that the quickfix entries are not " valid - call setloclist(0, qflist, ' ') + eval qflist->setloclist(0, ' ') endfor " Test A @@ -1699,7 +1699,7 @@ endfunc func Test_setqflist_invalid_nr() " The following command used to crash Vim - call setqflist([], ' ', {'nr' : $XXX_DOES_NOT_EXIST}) + eval []->setqflist(' ', {'nr' : $XXX_DOES_NOT_EXIST}) endfunc func Test_setqflist_user_sets_buftype() diff --git a/src/nvim/testdir/test_ruby.vim b/src/nvim/testdir/test_ruby.vim index 1a274d1fec..1fbf3392d9 100644 --- a/src/nvim/testdir/test_ruby.vim +++ b/src/nvim/testdir/test_ruby.vim @@ -60,7 +60,7 @@ func Test_ruby_set_cursor() " Check that movement after setting cursor position keeps current column. normal j call assert_equal([2, 6], [line('.'), col('.')]) - call assert_equal([2, 5], rubyeval('$curwin.cursor')) + call assert_equal([2, 5], '$curwin.cursor'->rubyeval()) " call assert_fails('ruby $curwin.cursor = [1]', " \ 'ArgumentError: array length must be 2') diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 7570049e7c..c796f1f676 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1315,7 +1315,7 @@ func Test_search_match_at_curpos() normal gg - call search('foobar', 'c') + eval 'foobar'->search('c') call assert_equal([1, 1], [line('.'), col('.')]) normal j @@ -1354,6 +1354,41 @@ func Test_search_display_pattern() endif endfunc +func Test_searchdecl() + let lines =<< trim END + int global; + + func() + { + int global; + if (cond) { + int local; + } + int local; + // comment + } + END + new + call setline(1, lines) + 10 + call assert_equal(0, searchdecl('local', 0, 0)) + call assert_equal(7, getcurpos()[1]) + + 10 + call assert_equal(0, 'local'->searchdecl(0, 1)) + call assert_equal(9, getcurpos()[1]) + + 10 + call assert_equal(0, searchdecl('global')) + call assert_equal(5, getcurpos()[1]) + + 10 + call assert_equal(0, searchdecl('global', 1)) + call assert_equal(1, getcurpos()[1]) + + bwipe! +endfunc + func Test_search_special() " this was causing illegal memory access and an endless loop set t_PE= diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim index dd4707977e..76d1306836 100644 --- a/src/nvim/testdir/test_sha256.vim +++ b/src/nvim/testdir/test_sha256.vim @@ -6,17 +6,17 @@ endif function Test_sha256() " test for empty string: - call assert_equal(sha256(""), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') + call assert_equal('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', sha256("")) "'test for 1 char: - call assert_equal(sha256("a"), 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb') + call assert_equal('ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb', sha256("a")) " "test for 3 chars: - call assert_equal(sha256("abc"), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + call assert_equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', "abc"->sha256()) " test for contains meta char: - call assert_equal(sha256("foo\nbar"), '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776') + call assert_equal('807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776', sha256("foo\nbar")) " test for contains non-ascii char: - call assert_equal(sha256("\xde\xad\xbe\xef"), '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953') + call assert_equal('5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953', sha256("\xde\xad\xbe\xef")) endfunction diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 9753100375..f287256396 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -393,7 +393,7 @@ func Test_sign_funcs() " Tests for sign_define() let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} - call assert_equal(0, sign_define("sign1", attr)) + call assert_equal(0, "sign1"->sign_define(attr)) call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined()) @@ -404,13 +404,13 @@ func Test_sign_funcs() call Sign_define_ignore_error("sign2", attr) call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], - \ sign_getdefined("sign2")) + \ "sign2"->sign_getdefined()) " Test for a sign name with digits call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'})) call assert_equal([{'name' : '2', 'linehl' : 'StatusLine'}], \ sign_getdefined(0002)) - call sign_undefine(0002) + eval 0002->sign_undefine() " Tests for invalid arguments to sign_define() call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') @@ -434,7 +434,7 @@ func Test_sign_funcs() call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], - \ sign_getplaced('%', {'lnum' : 20})) + \ '%'->sign_getplaced({'lnum' : 20})) call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], @@ -490,10 +490,10 @@ func Test_sign_funcs() \ 'E745:') " Tests for sign_unplace() - call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + eval 20->sign_place('', 'sign2', 'Xsign', {"lnum" : 30}) call assert_equal(0, sign_unplace('', \ {'id' : 20, 'buffer' : 'Xsign'})) - call assert_equal(-1, sign_unplace('', + call assert_equal(-1, ''->sign_unplace( \ {'id' : 30, 'buffer' : 'Xsign'})) call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) call assert_fails("call sign_unplace('', @@ -1693,7 +1693,7 @@ func Test_sign_jump_func() let r = sign_jump(5, '', 'foo') call assert_equal(2, r) call assert_equal(2, line('.')) - let r = sign_jump(6, 'g1', 'foo') + let r = 6->sign_jump('g1', 'foo') call assert_equal(5, r) call assert_equal(5, line('.')) let r = sign_jump(5, '', 'bar') @@ -1921,8 +1921,7 @@ func Test_sign_funcs_multi() \ 'group' : 'g1', 'priority' : 10}], s[0].signs) " Change an existing sign without specifying the group - call assert_equal([5], sign_placelist([ - \ {'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}])) + call assert_equal([5], [{'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}]->sign_placelist()) let s = sign_getplaced('Xsign', {'id' : 5, 'group' : ''}) call assert_equal([{'id' : 5, 'name' : 'sign1', 'lnum' : 11, \ 'group' : '', 'priority' : 10}], s[0].signs) @@ -1955,7 +1954,7 @@ func Test_sign_funcs_multi() \ {'id' : 1, 'group' : 'g1'}, {'id' : 1, 'group' : 'g2'}])) " Invalid arguments - call assert_equal([], sign_unplacelist([])) + call assert_equal([], []->sign_unplacelist()) call assert_fails('call sign_unplacelist({})', "E714:") call assert_fails('call sign_unplacelist([[]])', "E715:") call assert_fails('call sign_unplacelist(["abc"])', "E715:") diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index e525d06ea2..cf0faeee31 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -77,7 +77,7 @@ func Test_spellbadword() set spell call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.')) - call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence')) + call assert_equal(['another', 'caps'], 'A sentence. another sentence'->spellbadword()) call assert_equal(['TheCamelWord', 'bad'], spellbadword('TheCamelWord asdf')) set spelloptions=camel @@ -407,7 +407,7 @@ func Test_zz_basic() \ ) call assert_equal("gebletegek", soundfold('goobledygoook')) - call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) endfunc @@ -711,7 +711,7 @@ func TestGoodBadBase() break endif let prevbad = bad - let lst = spellsuggest(bad, 3) + let lst = bad->spellsuggest(3) normal mm call add(result, [bad, lst]) diff --git a/src/nvim/testdir/test_spell_utf8.vim b/src/nvim/testdir/test_spell_utf8.vim index cafdb97f28..3d159f3352 100644 --- a/src/nvim/testdir/test_spell_utf8.vim +++ b/src/nvim/testdir/test_spell_utf8.vim @@ -512,8 +512,7 @@ func TestGoodBadBase() break endif let prevbad = bad - " let lst = bad->spellsuggest(3) - let lst = spellsuggest(bad, 3) + let lst = bad->spellsuggest(3) normal mm call add(result, [bad, lst]) @@ -552,8 +551,7 @@ func Test_spell_basic() \ ) call assert_equal("gebletegek", soundfold('goobledygoook')) - " call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) - call assert_equal("kepereneven", soundfold('kóopërÿnôven')) + call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold()) call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) endfunc diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index b140077111..d830f5216d 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -905,15 +905,13 @@ func Test_not_a_term() " This will take 2 seconds because of the missing --not-a-term let cmd = GetVimProg() .. ' --cmd quit ' .. redir exe "silent !" . cmd - " call assert_match("\<Esc>", readfile('Xvimout')->join()) - call assert_match("\<Esc>", join(readfile('Xvimout'))) + call assert_match("\<Esc>", readfile('Xvimout')->join()) call delete('Xvimout') " With --not-a-term there are no escape sequences. let cmd = GetVimProg() .. ' --not-a-term --cmd quit ' .. redir exe "silent !" . cmd - " call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) - call assert_notmatch("\<Esc>", join(readfile('Xvimout'))) + call assert_notmatch("\<Esc>", readfile('Xvimout')->join()) call delete('Xvimout') endfunc diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index e7f9bb76f2..113c85acef 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -547,7 +547,7 @@ func Test_sub_replace_5() \ substitute('A123456789', \ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', \ '\=string([submatch(0, 1), submatch(9, 1), ' . - \ 'submatch(8, 1), submatch(7, 1), submatch(6, 1), ' . + \ 'submatch(8, 1), 7->submatch(1), submatch(6, 1), ' . \ 'submatch(5, 1), submatch(4, 1), submatch(3, 1), ' . \ 'submatch(2, 1), submatch(1, 1)])', \ '')) @@ -752,8 +752,7 @@ endfunc func Test_submatch_list_concatenate() let pat = 'A\(.\)' let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])} - " call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]") - call assert_equal(substitute('A1', pat, Rep, ''), "[['A1'], ['1']]") + call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]") endfunc func Test_substitute_skipped_range() diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index e3101d4e44..b3018b2b0d 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -113,7 +113,7 @@ func Test_swapinfo() w let fname = s:swapname() call assert_match('Xswapinfo', fname) - let info = swapinfo(fname) + let info = fname->swapinfo() let ver = printf('VIM %d.%d', v:version / 100, v:version % 100) call assert_equal(ver, info.version) @@ -155,7 +155,7 @@ func Test_swapname() let buf = bufnr('%') let expected = s:swapname() wincmd p - call assert_equal(expected, swapname(buf)) + call assert_equal(expected, buf->swapname()) new Xtest3 setlocal noswapfile diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 914d9c2782..757866f5dc 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -30,23 +30,17 @@ func AssertHighlightGroups(lnum, startcol, expected, trans = 1, msg = "") " If groups are provided as a string, each character is assumed to be a " group and spaces represent no group, useful for visually describing tests. let l:expectedGroups = type(a:expected) == v:t_string - "\ ? a:expected->split('\zs')->map({_, v -> trim(v)}) - \ ? map(split(a:expected, '\zs'), {_, v -> trim(v)}) + \ ? a:expected->split('\zs')->map({_, v -> trim(v)}) \ : a:expected let l:errors = 0 - " let l:msg = (a:msg->empty() ? "" : a:msg .. ": ") - let l:msg = (empty(a:msg) ? "" : a:msg .. ": ") + let l:msg = (a:msg->empty() ? "" : a:msg .. ": ") \ .. "Wrong highlight group at " .. a:lnum .. "," - " for l:i in range(a:startcol, a:startcol + l:expectedGroups->len() - 1) - " let l:errors += synID(a:lnum, l:i, a:trans) - " \ ->synIDattr("name") - " \ ->assert_equal(l:expectedGroups[l:i - 1], - for l:i in range(a:startcol, a:startcol + len(l:expectedGroups) - 1) - let l:errors += - \ assert_equal(synIDattr(synID(a:lnum, l:i, a:trans), "name"), - \ l:expectedGroups[l:i - 1], - \ l:msg .. l:i) + for l:i in range(a:startcol, a:startcol + l:expectedGroups->len() - 1) + let l:errors += synID(a:lnum, l:i, a:trans) + \ ->synIDattr("name") + \ ->assert_equal(l:expectedGroups[l:i - 1], + \ l:msg .. l:i) endfor endfunc diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 7b8ee778cc..1858b48807 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -121,8 +121,7 @@ func Test_system_with_shell_quote() let msg = printf('shell=%s shellxquote=%s', &shell, &shellxquote) try - " let out = 'echo 123'->system() - let out = system('echo 123') + let out = 'echo 123'->system() catch call assert_report(printf('%s: %s', msg, v:exception)) continue diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index b261b96c3b..9869dc7590 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -35,7 +35,7 @@ function Test_tabpage() tabnew tabfirst call settabvar(2, 'val_num', 100) - call settabvar(2, 'val_str', 'SetTabVar test') + eval 'SetTabVar test'->settabvar(2, 'val_str') call settabvar(2, 'val_list', ['red', 'blue', 'green']) " call assert_true(gettabvar(2, 'val_num') == 100 && gettabvar(2, 'val_str') == 'SetTabVar test' && gettabvar(2, 'val_list') == ['red', 'blue', 'green']) @@ -184,7 +184,7 @@ function Test_tabpage_with_autocmd() let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') call assert_equal(['a', 'a'], s:li) let s:li = [] - C call map(copy(winr), 'settabwinvar('.tabn.', v:val, ''a'', v:val*2)') + C call map(copy(winr), '(v:val*2)->settabwinvar(' .. tabn .. ', v:val, ''a'')') let s:li = split(join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")')), '\s\+') call assert_equal(['2', '4'], s:li) diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 15182893e9..2aa04df42a 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -351,7 +351,7 @@ func Test_getsettagstack() " Try to set current index to invalid values call settagstack(1, {'curidx' : -1}) call assert_equal(1, gettagstack().curidx) - call settagstack(1, {'curidx' : 50}) + eval {'curidx' : 50}->settagstack(1) call assert_equal(4, gettagstack().curidx) " Try pushing invalid items onto the stack diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index e830813081..e11815ff33 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -14,7 +14,7 @@ func Test_taglist() split Xtext call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo"), {i, v -> v.name})) - call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xtext"), {i, v -> v.name})) + call assert_equal(['FFoo', 'BFoo'], map("Foo"->taglist("Xtext"), {i, v -> v.name})) call assert_equal(['FFoo', 'BFoo'], map(taglist("Foo", "Xfoo"), {i, v -> v.name})) call assert_equal(['BFoo', 'FFoo'], map(taglist("Foo", "Xbar"), {i, v -> v.name})) diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index ceaa5de92b..5cc0da2586 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -77,7 +77,7 @@ endfunc func Test_info() let id = timer_start(1000, 'MyHandler') - let info = timer_info(id) + let info = id->timer_info() call assert_equal(id, info[0]['id']) call assert_equal(1000, info[0]['time']) call assert_equal("function('MyHandler')", string(info[0]['callback'])) @@ -113,7 +113,7 @@ func Test_paused() let info = timer_info(id) call assert_equal(0, info[0]['paused']) - call timer_pause(id, 1) + eval id->timer_pause(1) let info = timer_info(id) call assert_equal(1, info[0]['paused']) sleep 200m @@ -148,7 +148,7 @@ func Test_delete_myself() endfunc func StopTimer1(timer) - let g:timer2 = timer_start(10, 'StopTimer2') + let g:timer2 = 10->timer_start('StopTimer2') " avoid maxfuncdepth error call timer_pause(g:timer1, 1) sleep 40m @@ -239,7 +239,7 @@ func FeedAndPeek(timer) endfunc func Interrupt(timer) - " call test_feedinput("\<C-C>") + " eval "\<C-C>"->test_feedinput() call nvim_input("\<C-C>") endfunc @@ -251,7 +251,7 @@ func Test_peek_and_get_char() let intr = timer_start(100, 'Interrupt') let c = getchar() call assert_equal(char2nr('a'), c) - call timer_stop(intr) + eval intr->timer_stop() endfunc func Test_getchar_zero() diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index c7dcaa0f36..30e00e7ad4 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -490,7 +490,7 @@ funct Test_undofile() call delete('Xundodir', 'd') " Test undofile() with 'undodir' set to a non-existing directory. - " call assert_equal('', undofile('Xundofoo')) + " call assert_equal('', 'Xundofoo'->undofile()) if isdirectory('/tmp') set undodir=/tmp diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index da72da087f..0818c2e4b0 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -17,7 +17,7 @@ func Test_strchars() let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] for i in range(len(inp)) call assert_equal(exp[i][0], strchars(inp[i])) - call assert_equal(exp[i][1], strchars(inp[i], 0)) + call assert_equal(exp[i][1], inp[i]->strchars(0)) call assert_equal(exp[i][2], strchars(inp[i], 1)) endfor endfunc @@ -69,7 +69,7 @@ func Test_screenchar_utf8() call setline(1, ["ABC\u0308"]) redraw call assert_equal([0x0041], screenchars(1, 1)) - call assert_equal([0x0042], screenchars(1, 2)) + call assert_equal([0x0042], 1->screenchars(2)) call assert_equal([0x0043, 0x0308], screenchars(1, 3)) call assert_equal("A", screenstring(1, 1)) call assert_equal("B", screenstring(1, 2)) diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 2fbf130345..46e0d62313 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -330,7 +330,7 @@ func Test_vartabs_shiftwidth() let lines = ScreenLines([1, 2], winwidth(0)) call s:compare_lines(expect2, lines) call assert_equal(20, shiftwidth(virtcol('.')-2)) - call assert_equal(30, shiftwidth(virtcol('.'))) + call assert_equal(30, virtcol('.')->shiftwidth()) norm! $>> let expect3 = [' ', ' x ', '~ '] let lines = ScreenLines([1, 3], winwidth(0)) diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 039de0c623..a200bf7d42 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -72,7 +72,7 @@ endfunc func Test_window_quit() e Xa split Xb - call assert_equal(2, winnr('$')) + call assert_equal(2, '$'->winnr()) call assert_equal('Xb', bufname(winbufnr(1))) call assert_equal('Xa', bufname(winbufnr(2))) @@ -88,7 +88,7 @@ func Test_window_horizontal_split() 3wincmd s call assert_equal(2, winnr('$')) call assert_equal(3, winheight(0)) - call assert_equal(winwidth(1), winwidth(2)) + call assert_equal(winwidth(1), 2->winwidth()) call assert_fails('botright topleft wincmd s', 'E442:') bw @@ -267,7 +267,7 @@ func Test_window_height() wincmd + call assert_equal(wh1, winheight(1)) - call assert_equal(wh2, winheight(2)) + call assert_equal(wh2, 2->winheight()) 2wincmd _ call assert_equal(2, winheight(1)) @@ -452,7 +452,7 @@ func Test_window_newtab() wincmd T call assert_equal(2, tabpagenr('$')) call assert_equal(['Xb', 'Xa'], map(tabpagebuflist(1), 'bufname(v:val)')) - call assert_equal(['Xc' ], map(tabpagebuflist(2), 'bufname(v:val)')) + call assert_equal(['Xc' ], map(2->tabpagebuflist(), 'bufname(v:val)')) %bw! endfunc @@ -577,8 +577,11 @@ endfunc function! Fun_RenewFile() " Need to wait a bit for the timestamp to be older. - sleep 2 - silent execute '!echo "1" > tmp.txt' + let old_ftime = getftime("tmp.txt") + while getftime("tmp.txt") == old_ftime + sleep 100m + silent execute '!echo "1" > tmp.txt' + endwhile sp wincmd p edit! tmp.txt @@ -814,13 +817,25 @@ func Test_winnr() tabnew call assert_equal(8, tabpagewinnr(1, 'j')) - call assert_equal(2, tabpagewinnr(1, 'k')) + call assert_equal(2, 1->tabpagewinnr('k')) call assert_equal(4, tabpagewinnr(1, 'h')) call assert_equal(6, tabpagewinnr(1, 'l')) only | tabonly endfunc +func Test_winrestview() + split runtest.vim + normal 50% + let view = winsaveview() + close + split runtest.vim + eval view->winrestview() + call assert_equal(view, winsaveview()) + + bwipe! +endfunc + func Test_win_splitmove() edit a leftabove split b diff --git a/src/nvim/testdir/test_window_id.vim b/src/nvim/testdir/test_window_id.vim index d10d831650..8bf4ede350 100644 --- a/src/nvim/testdir/test_window_id.vim +++ b/src/nvim/testdir/test_window_id.vim @@ -67,7 +67,7 @@ func Test_win_getid() call win_gotoid(id2) call assert_equal("two", expand("%")) - call win_gotoid(id4) + eval id4->win_gotoid() call assert_equal("four", expand("%")) call win_gotoid(id1) call assert_equal("one", expand("%")) @@ -75,17 +75,17 @@ func Test_win_getid() call assert_equal("five", expand("%")) call assert_equal(0, win_id2win(9999)) - call assert_equal(nr5, win_id2win(id5)) + call assert_equal(nr5, id5->win_id2win()) call assert_equal(0, win_id2win(id1)) tabnext call assert_equal(nr1, win_id2win(id1)) call assert_equal([0, 0], win_id2tabwin(9999)) - call assert_equal([1, nr2], win_id2tabwin(id2)) + call assert_equal([1, nr2], id2->win_id2tabwin()) call assert_equal([2, nr4], win_id2tabwin(id4)) call assert_equal([], win_findbuf(9999)) - call assert_equal([id2], win_findbuf(bufnr2)) + call assert_equal([id2], bufnr2->win_findbuf()) call win_gotoid(id5) split call assert_equal(sort([id5, win_getid()]), sort(win_findbuf(bufnr5))) @@ -98,7 +98,7 @@ func Test_win_getid_curtab() tabfirst copen only - call assert_equal(win_getid(1), win_getid(1, 1)) + call assert_equal(win_getid(1), 1->win_getid( 1)) tabclose! endfunc @@ -120,4 +120,11 @@ func Test_winlayout() call assert_equal(['col', [['leaf', w3], ['row', [['leaf', w4], ['leaf', w2]]], ['leaf', w1]]], winlayout()) only! + + let w1 = win_getid() + call assert_equal(['leaf', w1], winlayout(1)) + tabnew + let w2 = win_getid() + call assert_equal(['leaf', w2], 2->winlayout()) + tabclose endfunc diff --git a/src/nvim/vim.h b/src/nvim/vim.h index e3539c1a57..2f8ddd1e88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -215,6 +215,11 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext() // (vim_strchr() is now in strings.c) #define STRLEN(s) strlen((char *)(s)) +#ifdef HAVE_STRNLEN +# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n)) +#else +# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n)) +#endif #define STRCPY(d, s) strcpy((char *)(d), (char *)(s)) #define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n)) #define STRLCPY(d, s, n) xstrlcpy((char *)(d), (char *)(s), (size_t)(n)) diff --git a/src/nvim/window.c b/src/nvim/window.c index 3e6e42dec2..be963d8374 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2229,6 +2229,54 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } +static void leaving_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When leaving a prompt window stop Insert mode and perhaps restart + // it when entering that window again. + win->w_buffer->b_prompt_insert = restart_edit; + if (restart_edit != NUL && mode_displayed) { + clear_cmdline = true; // unshow mode later + } + restart_edit = NUL; + + // When leaving the window (or closing the window) was done from a + // callback we need to break out of the Insert mode loop and restart Insert + // mode when entering the window again. + if (State & INSERT) { + stop_insert_mode = true; + if (win->w_buffer->b_prompt_insert == NUL) { + win->w_buffer->b_prompt_insert = 'A'; + } + } +} + +void entering_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When switching to a prompt buffer that was in Insert mode, don't stop + // Insert mode, it may have been set in leaving_window(). + if (win->w_buffer->b_prompt_insert != NUL) { + stop_insert_mode = false; + } + + // When entering the prompt window restart Insert mode if we were in Insert + // mode when we left it and not already in Insert mode. + if ((State & INSERT) == 0) { + restart_edit = win->w_buffer->b_prompt_insert; + } +} + /// Closes all windows for buffer `buf`. /// /// @param keep_curwin don't close `curwin` @@ -2367,6 +2415,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev shell_new_rows(); } } + entering_window(curwin); // Since goto_tabpage_tp above did not trigger *Enter autocommands, do // that now. @@ -2434,10 +2483,10 @@ int win_close(win_T *win, bool free_buf) } if (win == curwin) { - /* - * Guess which window is going to be the new current window. - * This may change because of the autocommands (sigh). - */ + leaving_window(curwin); + + // Guess which window is going to be the new current window. + // This may change because of the autocommands (sigh). if (!win->w_floating) { wp = frame2win(win_altframe(win, NULL)); } else { @@ -3801,6 +3850,8 @@ int win_new_tabpage(int after, char_u *filename) lastused_tabpage = old_curtab; + entering_window(curwin); + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); @@ -3956,6 +4007,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) { tabpage_T *tp = curtab; + leaving_window(curwin); reset_VIsual_and_resel(); // stop Visual mode if (trigger_leave_autocmds) { if (new_curbuf != curbuf) { @@ -4478,6 +4530,10 @@ static void win_enter_ext(win_T *const wp, const int flags) return; } + if (!curwin_invalid) { + leaving_window(curwin); + } + if (!curwin_invalid && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS)) { // Be careful: If autocommands delete the window, return now. if (wp->w_buffer != curbuf) { @@ -4525,6 +4581,7 @@ static void win_enter_ext(win_T *const wp, const int flags) fix_current_dir(); + entering_window(curwin); // Careful: autocommands may close the window and make "wp" invalid if (flags & WEE_TRIGGER_NEW_AUTOCMDS) { apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 50b4b85d2a..45a01be620 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -104,10 +104,10 @@ describe('API/extmarks', function() it("can end extranges past final newline using end_col = 0", function() set_extmark(ns, marks[1], 0, 0, { end_col = 0, - end_line = 1 + end_row = 1 }) eq("end_col value outside range", - pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_line = 1 })) + pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 })) end) it('adds, updates and deletes marks', function() @@ -1424,6 +1424,14 @@ describe('API/extmarks', function() eq({ {1, 0, 0}, {2, 0, 8} }, meths.buf_get_extmarks(0, ns, 0, -1, {})) end) + + it('can accept "end_row" or "end_line" #16548', function() + set_extmark(ns, marks[1], 0, 0, { + end_col = 0, + end_line = 1 + }) + eq({ {1, 0, 0, { end_col = 0, end_row = 1 }} }, get_extmarks(ns, 0, -1, {details=true})) + end) end) describe('Extmarks buffer api with many marks', function() diff --git a/test/functional/legacy/autocmd_option_spec.lua b/test/functional/legacy/autocmd_option_spec.lua index 1914818215..5e586d3a6a 100644 --- a/test/functional/legacy/autocmd_option_spec.lua +++ b/test/functional/legacy/autocmd_option_spec.lua @@ -1,6 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local nvim = helpers.meths -local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq +local clear, eq, neq, eval = helpers.clear, helpers.eq, helpers.neq, helpers.eval local curbuf, buf = helpers.curbuf, helpers.bufmeths local curwin = helpers.curwin local exec_capture = helpers.exec_capture @@ -10,11 +10,14 @@ local function declare_hook_function() source([[ fu! AutoCommand(match, bufnr, winnr) let l:acc = { - \ 'option' : a:match, - \ 'oldval' : v:option_old, - \ 'newval' : v:option_new, - \ 'scope' : v:option_type, - \ 'attr' : { + \ 'option' : a:match, + \ 'oldval' : v:option_old, + \ 'oldval_l' : v:option_oldlocal, + \ 'oldval_g' : v:option_oldglobal, + \ 'newval' : v:option_new, + \ 'scope' : v:option_type, + \ 'cmd' : v:option_command, + \ 'attr' : { \ 'bufnr' : a:bufnr, \ 'winnr' : a:winnr, \ } @@ -42,13 +45,16 @@ local function get_result() return ret end -local function expected_table(option, oldval, newval, scope, attr) +local function expected_table(option, oldval, oldval_l, oldval_g, newval, scope, cmd, attr) return { - option = option, - oldval = tostring(oldval), - newval = tostring(newval), - scope = scope, - attr = attr, + option = option, + oldval = tostring(oldval), + oldval_l = tostring(oldval_l), + oldval_g = tostring(oldval_g), + newval = tostring(newval), + scope = scope, + cmd = cmd, + attr = attr, } end @@ -66,7 +72,7 @@ local function expected_combination(...) end for i, v in ipairs(args) do - local attr = v[5] + local attr = v[8] if not attr then -- remove attr entries ret[i].attr = nil @@ -112,7 +118,7 @@ local function get_new_window_number() end describe('au OptionSet', function() - describe('with any opton (*)', function() + describe('with any option (*)', function() before_each(function() clear() @@ -123,44 +129,44 @@ describe('au OptionSet', function() it('should be called in setting number option', function() command('set nu') - expected_combination({'number', 0, 1, 'global'}) + expected_combination({'number', 0, 0, 0, 1, 'global', 'set'}) command('setlocal nonu') - expected_combination({'number', 1, 0, 'local'}) + expected_combination({'number', 1, 1, '', 0, 'local', 'setlocal'}) command('setglobal nonu') - expected_combination({'number', 1, 0, 'global'}) + expected_combination({'number', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ai') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, '', 0, 1, 'global', 'setglobal'}) command('set noai') - expected_combination({'autoindent', 1, 0, 'global'}) + expected_combination({'autoindent', 1, 1, 1, 0, 'global', 'set'}) end) it('should be called in inverting global autoindent option',function() command('set ai!') - expected_combination({'autoindent', 0, 1, 'global'}) + expected_combination({'autoindent', 0, 0, 0, 1, 'global', 'set'}) end) it('should be called in being unset local autoindent option',function() command('setlocal ai') - expected_combination({'autoindent', 0, 1, 'local'}) + expected_combination({'autoindent', 0, 0, '', 1, 'local', 'setlocal'}) command('setlocal ai<') - expected_combination({'autoindent', 1, 0, 'local'}) + expected_combination({'autoindent', 1, 1, '', 0, 'local', 'setlocal'}) end) it('should be called in setting global list and number option at the same time',function() command('set list nu') expected_combination( - {'list', 0, 1, 'global'}, - {'number', 0, 1, 'global'} + {'list', 0, 0, 0, 1, 'global', 'set'}, + {'number', 0, 0, 0, 1, 'global', 'set'} ) end) @@ -171,25 +177,27 @@ describe('au OptionSet', function() it('should be called in setting local acd', function() command('setlocal acd') - expected_combination({'autochdir', 0, 1, 'local'}) + expected_combination({'autochdir', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in setting autoread', function() command('set noar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, 1, 1, 0, 'global', 'set'}) command('setlocal ar') - expected_combination({'autoread', 0, 1, 'local'}) + expected_combination({'autoread', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should be called in inverting global autoread', function() command('setglobal invar') - expected_combination({'autoread', 1, 0, 'global'}) + expected_combination({'autoread', 1, '', 1, 0, 'global', 'setglobal'}) end) it('should be called in setting backspace option through :let', function() + local oldval = eval('&backspace') + command('let &bs=""') - expected_combination({'backspace', 'indent,eol,start', '', 'global'}) + expected_combination({'backspace', oldval, oldval, oldval, '', 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -200,7 +208,7 @@ describe('au OptionSet', function() it('should trigger using correct option name', function() command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -208,9 +216,339 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) + + it('with string global option', function() + local oldval = eval('&backupext') + + command('set backupext=foo') + expected_combination({'backupext', oldval, oldval, oldval, 'foo', 'global', 'set'}) + + command('set backupext&') + expected_combination({'backupext', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal backupext=bar') + expected_combination({'backupext', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set backupext&') + -- As this is a global option this sets the global value even though :setlocal is used! + command('setlocal backupext=baz') + expected_combination({'backupext', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal backupext=ext_global') + command('noa setlocal backupext=ext_local') -- Sets the global(!) value + command('set backupext=foo') + expected_combination({ + 'backupext', 'ext_local', 'ext_local', 'ext_local', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to buffer) option', function() + local oldval = eval('&tags') + + command('set tags=tagpath') + expected_combination({'tags', oldval, oldval, oldval, 'tagpath', 'global', 'set'}) + + command('set tags&') + expected_combination({'tags', 'tagpath', 'tagpath', 'tagpath', oldval, 'global', 'set'}) + + command('setglobal tags=tagpath1') + expected_combination({'tags', oldval, '', oldval, 'tagpath1', 'global', 'setglobal'}) + + command('setlocal tags=tagpath2') + expected_combination({'tags', 'tagpath1', 'tagpath1', '', 'tagpath2', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal tags=tag_global') + command('noa setlocal tags=tag_local') + command('set tags=tagpath') + expected_combination({ + 'tags', 'tag_global', 'tag_local', 'tag_global', 'tagpath', 'global', 'set' + }) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa set tags=tag_global') + command('noa setlocal tags=') + command('set tags=tagpath') + expected_combination({'tags', 'tag_global', '', 'tag_global', 'tagpath', 'global', 'set'}) + end) + + it('with string local (to buffer) option', function() + local oldval = eval('&spelllang') + + command('set spelllang=elvish,klingon') + expected_combination({'spelllang', oldval, oldval, oldval, 'elvish,klingon', 'global', 'set'}) + + command('set spelllang&') + expected_combination({ + 'spelllang', 'elvish,klingon', 'elvish,klingon', 'elvish,klingon', oldval, 'global', 'set' + }) + + command('setglobal spelllang=elvish') + expected_combination({'spelllang', oldval, '', oldval, 'elvish', 'global', 'setglobal'}) + + command('noa set spelllang&') + command('setlocal spelllang=klingon') + expected_combination({'spelllang', oldval, oldval, '', 'klingon', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal spelllang=spellglobal') + command('noa setlocal spelllang=spelllocal') + command('set spelllang=foo') + expected_combination({ + 'spelllang', 'spelllocal', 'spelllocal', 'spellglobal', 'foo', 'global', 'set' + }) + end) + + it('with string global-local (to window) option', function() + local oldval = eval('&statusline') + + command('set statusline=foo') + expected_combination({'statusline', oldval, oldval, '', 'foo', 'global', 'set'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('set statusline&') + expected_combination({'statusline', 'foo', 'foo', 'foo', oldval, 'global', 'set'}) + + command('setglobal statusline=bar') + expected_combination({'statusline', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set statusline&') + command('setlocal statusline=baz') + expected_combination({'statusline', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + -- Note: v:option_old is the old global value for global-local string options + -- but the old local value for all other kinds of options. + command('noa setglobal statusline=bar') + command('noa setlocal statusline=baz') + command('set statusline=foo') + expected_combination({'statusline', 'bar', 'baz', 'bar', 'foo', 'global', 'set'}) + end) + + it('with string local (to window) option', function() + local oldval = eval('&foldignore') + + command('set foldignore=fo') + expected_combination({'foldignore', oldval, oldval, oldval, 'fo', 'global', 'set'}) + + command('set foldignore&') + expected_combination({'foldignore', 'fo', 'fo', 'fo', oldval, 'global', 'set'}) + + command('setglobal foldignore=bar') + expected_combination({'foldignore', oldval, '', oldval, 'bar', 'global', 'setglobal'}) + + command('noa set foldignore&') + command('setlocal foldignore=baz') + expected_combination({'foldignore', oldval, oldval, '', 'baz', 'local', 'setlocal'}) + + command('noa setglobal foldignore=glob') + command('noa setlocal foldignore=loc') + command('set foldignore=fo') + expected_combination({'foldignore', 'loc', 'loc', 'glob', 'fo', 'global', 'set'}) + end) + + it('with number global option', function() + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setglobal cmdheight=2') + expected_combination({'cmdheight', 1, '', 1, 2, 'global', 'setglobal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('setlocal cmdheight=2') + expected_combination({'cmdheight', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal cmdheight=8') + command('noa setlocal cmdheight=1') -- Sets the global(!) value + command('set cmdheight=2') + expected_combination({'cmdheight', 1, 1, 1, 2, 'global', 'set'}) + + command('noa set cmdheight=8') + command('set cmdheight=2') + expected_combination({'cmdheight', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to buffer) option', function() + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setglobal undolevels=2') + expected_combination({'undolevels', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('setlocal undolevels=2') + expected_combination({'undolevels', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal undolevels=8') + command('noa setlocal undolevels=1') + command('set undolevels=2') + expected_combination({'undolevels', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set undolevels=8') + command('set undolevels=2') + expected_combination({'undolevels', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to buffer) option', function() + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setglobal wrapmargin=2') + expected_combination({'wrapmargin', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('setlocal wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal wrapmargin=8') + command('noa setlocal wrapmargin=1') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set wrapmargin=8') + command('set wrapmargin=2') + expected_combination({'wrapmargin', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number global-local (to window) option', function() + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setglobal scrolloff=2') + expected_combination({'scrolloff', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('setlocal scrolloff=2') + expected_combination({'scrolloff', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal scrolloff=8') + command('noa setlocal scrolloff=1') + command('set scrolloff=2') + expected_combination({'scrolloff', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set scrolloff=8') + command('set scrolloff=2') + expected_combination({'scrolloff', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with number local (to window) option', function() + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setglobal foldcolumn=2') + expected_combination({'foldcolumn', 8, '', 8, 2, 'global', 'setglobal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('setlocal foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, '', 2, 'local', 'setlocal'}) + + command('noa setglobal foldcolumn=8') + command('noa setlocal foldcolumn=1') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 1, 1, 8, 2, 'global', 'set'}) + + command('noa set foldcolumn=8') + command('set foldcolumn=2') + expected_combination({'foldcolumn', 8, 8, 8, 2, 'global', 'set'}) + end) + + it('with boolean global option', function() + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setglobal nowrapscan') + expected_combination({'wrapscan', 1, '', 1, 0, 'global', 'setglobal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('setlocal nowrapscan') + expected_combination({'wrapscan', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nowrapscan') + command('noa setlocal wrapscan') -- Sets the global(!) value + command('set nowrapscan') + expected_combination({'wrapscan', 1, 1, 1, 0, 'global', 'set'}) + + command('noa set nowrapscan') + command('set wrapscan') + expected_combination({'wrapscan', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean global-local (to buffer) option', function() + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setglobal autoread') + expected_combination({'autoread', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('setlocal noautoread') + expected_combination({'autoread', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal noautoread') + command('noa setlocal autoread') + command('set autoread') + expected_combination({'autoread', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set noautoread') + command('set autoread') + expected_combination({'autoread', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to buffer) option', function() + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setglobal cindent') + expected_combination({'cindent', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('setlocal nocindent') + expected_combination({'cindent', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocindent') + command('noa setlocal cindent') + command('set cindent') + expected_combination({'cindent', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocindent') + command('set cindent') + expected_combination({'cindent', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with boolean local (to window) option', function() + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setglobal cursorcolumn') + expected_combination({'cursorcolumn', 0, '', 0, 1, 'global', 'setglobal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('setlocal nocursorcolumn') + expected_combination({'cursorcolumn', 1, 1, '', 0, 'local', 'setlocal'}) + + command('noa setglobal nocursorcolumn') + command('noa setlocal cursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 1, 1, 0, 1, 'global', 'set'}) + + command('noa set nocursorcolumn') + command('set cursorcolumn') + expected_combination({'cursorcolumn', 0, 0, 0, 1, 'global', 'set'}) + end) + + it('with option value converted internally', function() + command('noa set backspace=1') + command('set backspace=2') + expected_combination(({ + 'backspace', 'indent,eol', 'indent,eol', 'indent,eol', '2', 'global', 'set' + })) + end) end) describe('with specific option', function() @@ -228,13 +566,13 @@ describe('au OptionSet', function() expected_empty() command('setlocal ro') - expected_combination({'readonly', 0, 1, 'local'}) + expected_combination({'readonly', 0, 0, '', 1, 'local', 'setlocal'}) command('setglobal ro') - expected_combination({'readonly', 0, 1, 'global'}) + expected_combination({'readonly', 0, '', 0, 1, 'global', 'setglobal'}) command('set noro') - expected_combination({'readonly', 1, 0, 'global'}) + expected_combination({'readonly', 1, 1, 1, 0, 'global', 'set'}) end) describe('being set by setbufvar()', function() @@ -249,7 +587,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setbufvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should trigger if the current buffer is different from the targetted buffer', function() @@ -259,7 +597,7 @@ describe('au OptionSet', function() local new_bufnr = buf.get_number(new_buffer) command('call setbufvar(' .. new_bufnr .. ', "&buftype", "nofile")') - expected_combination({'buftype', '', 'nofile', 'local', {bufnr = new_bufnr}}) + expected_combination({'buftype', '', '', '', 'nofile', 'local', 'setlocal', {bufnr = new_bufnr}}) end) end) @@ -275,7 +613,7 @@ describe('au OptionSet', function() set_hook('backup') command('call setwinvar(1, "&backup", 1)') - expected_combination({'backup', 0, 1, 'local'}) + expected_combination({'backup', 0, 0, '', 1, 'local', 'setlocal'}) end) it('should not trigger if the current window is different from the targetted window', function() @@ -295,7 +633,7 @@ describe('au OptionSet', function() nvim.set_option('autochdir', true) eq(true, nvim.get_option('autochdir')) - expected_combination({'autochdir', '0', '1', 'global'}) + expected_combination({'autochdir', 0, '', 0, 1, 'global', 'setglobal'}) end) it('should trigger if a number option be set globally', function() @@ -303,7 +641,7 @@ describe('au OptionSet', function() nvim.set_option('cmdheight', 5) eq(5, nvim.get_option('cmdheight')) - expected_combination({'cmdheight', 1, 5, 'global'}) + expected_combination({'cmdheight', 1, '', 1, 5, 'global', 'setglobal'}) end) it('should trigger if a string option be set globally', function() @@ -311,7 +649,7 @@ describe('au OptionSet', function() nvim.set_option('ambiwidth', 'double') eq('double', nvim.get_option('ambiwidth')) - expected_combination({'ambiwidth', 'single', 'double', 'global'}) + expected_combination({'ambiwidth', 'single', '', 'single', 'double', 'global', 'setglobal'}) end) end) end) diff --git a/test/functional/legacy/prompt_buffer_spec.lua b/test/functional/legacy/prompt_buffer_spec.lua index 513be807be..47eca19de3 100644 --- a/test/functional/legacy/prompt_buffer_spec.lua +++ b/test/functional/legacy/prompt_buffer_spec.lua @@ -1,9 +1,12 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local feed= helpers.feed +local feed = helpers.feed local source = helpers.source local clear = helpers.clear local feed_command = helpers.feed_command +local poke_eventloop = helpers.poke_eventloop +local meths = helpers.meths +local eq = helpers.eq describe('prompt buffer', function() local screen @@ -28,12 +31,17 @@ describe('prompt buffer', function() func TimerFunc(text) call append(line("$") - 1, 'Result: "' . a:text .'"') endfunc + + func SwitchWindows() + call timer_start(0, {-> execute("wincmd p|wincmd p", "")}) + endfunc ]]) feed_command("set noshowmode | set laststatus=0") feed_command("call setline(1, 'other buffer')") feed_command("new") feed_command("set buftype=prompt") feed_command("call prompt_setcallback(bufnr(''), function('TextEntered'))") + feed_command("eval bufnr('')->prompt_setprompt('cmd: ')") end) after_each(function() @@ -56,10 +64,10 @@ describe('prompt buffer', function() feed("i") feed("hello\n") screen:expect([[ - % hello | + cmd: hello | Command: "hello" | Result: "hello" | - % ^ | + cmd: ^ | [Prompt] [+] | other buffer | ~ | @@ -98,7 +106,7 @@ describe('prompt buffer', function() feed("i") feed("hello<BS><BS>") screen:expect([[ - % hel^ | + cmd: hel^ | ~ | ~ | ~ | @@ -111,7 +119,20 @@ describe('prompt buffer', function() ]]) feed("<Left><Left><Left><BS>-") screen:expect([[ - % -^hel | + cmd: -^hel | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + | + ]]) + feed("<C-O>lz") + screen:expect([[ + cmd: -hz^el | ~ | ~ | ~ | @@ -124,7 +145,7 @@ describe('prompt buffer', function() ]]) feed("<End>x") screen:expect([[ - % -helx^ | + cmd: -hzelx^ | ~ | ~ | ~ | @@ -150,4 +171,58 @@ describe('prompt buffer', function() ]]) end) + it('switch windows', function() + feed_command("set showmode") + feed("i") + screen:expect([[ + cmd: ^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + feed("<C-O>:call SwitchWindows()<CR>") + poke_eventloop() + screen:expect([[ + cmd: ^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + -- INSERT -- | + ]]) + feed("<Esc>") + poke_eventloop() + screen:expect([[ + cmd:^ | + ~ | + ~ | + ~ | + [Prompt] [+] | + other buffer | + ~ | + ~ | + ~ | + | + ]]) + end) + + it('keeps insert mode after aucmd_restbuf in callback', function() + source [[ + let s:buf = nvim_create_buf(1, 1) + call timer_start(0, {-> nvim_buf_set_lines(s:buf, -1, -1, 0, ['walrus'])}) + startinsert + ]] + poke_eventloop() + eq({ mode = "i", blocking = false }, meths.get_mode()) + end) end) diff --git a/test/functional/legacy/searchpos_spec.lua b/test/functional/legacy/searchpos_spec.lua index 60f1edcd3f..fc18341c38 100644 --- a/test/functional/legacy/searchpos_spec.lua +++ b/test/functional/legacy/searchpos_spec.lua @@ -17,7 +17,7 @@ describe('searchpos', function() call('cursor', 1, 1) eq({1, 1, 2}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) call('cursor', 1, 2) - eq({2, 1, 1}, eval([[searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')]])) + eq({2, 1, 1}, eval([['\%(\([a-z]\)\|\_.\)\{-}xyz'->searchpos('pcW')]])) command('set cpo-=c') call('cursor', 1, 2) diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 927ee8060d..a88da63e90 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -1343,7 +1343,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = "We're no strangers to love..."}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = "We're no strangers to love..."}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1355,7 +1355,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = {'You know the rules', 'Search'}}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = {'You know the rules', 'Search'}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1370,7 +1370,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope="buffer"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope="buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1387,7 +1387,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1402,7 +1402,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, pos=1}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, pos=1}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1419,7 +1419,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {2, 2}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1434,7 +1434,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor", pos={1,3}}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={1,3}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1449,7 +1449,7 @@ describe('vim.diagnostic', function() vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) vim.api.nvim_win_set_cursor(0, {1, 1}) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header=false, scope="cursor", pos={0,first_line_len}}) + local float_bufnr, winnr = vim.diagnostic.open_float({header=false, scope="cursor", pos={0,first_line_len}}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1665,7 +1665,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer"}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer"}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1678,7 +1678,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, {header = false, scope = "buffer", prefix = ""}) + local float_bufnr, winnr = vim.diagnostic.open_float({header = false, scope = "buffer", prefix = ""}) local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) vim.api.nvim_win_close(winnr, true) return lines @@ -1691,7 +1691,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, { + local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic @@ -1712,7 +1712,7 @@ describe('vim.diagnostic', function() } vim.api.nvim_win_set_buf(0, diagnostic_bufnr) vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - local float_bufnr, winnr = vim.diagnostic.open_float(0, { + local float_bufnr, winnr = vim.diagnostic.open_float({ header = false, prefix = function(_, i, total) -- Only show a number if there is more than one diagnostic @@ -1728,7 +1728,21 @@ describe('vim.diagnostic', function() ]]) eq("Error executing lua: .../diagnostic.lua:0: prefix: expected 'string' or 'table' or 'function', got 42", - pcall_err(exec_lua, [[ vim.diagnostic.open_float(0, { prefix = 42 }) ]])) + pcall_err(exec_lua, [[ vim.diagnostic.open_float({ prefix = 42 }) ]])) + end) + + it('works with the old signature', function() + eq({'1. Syntax error'}, exec_lua [[ + local diagnostics = { + make_error("Syntax error", 0, 1, 0, 3), + } + vim.api.nvim_win_set_buf(0, diagnostic_bufnr) + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + local float_bufnr, winnr = vim.diagnostic.open_float(0, { header = false }) + local lines = vim.api.nvim_buf_get_lines(float_bufnr, 0, -1, false) + vim.api.nvim_win_close(winnr, true) + return lines + ]]) end) end) @@ -1918,5 +1932,27 @@ describe('vim.diagnostic', function() return {show_called, hide_called} ]]) end) + + it('triggers the autocommand when diagnostics are set', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, { + make_error('Diagnostic', 0, 0, 0, 0) + }) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) + + it('triggers the autocommand when diagnostics are cleared', function() + eq(1, exec_lua [[ + vim.g.diagnostic_autocmd_triggered = 0 + vim.cmd('autocmd DiagnosticChanged * let g:diagnostic_autocmd_triggered = 1') + vim.api.nvim_buf_set_name(diagnostic_bufnr, "test | test") + vim.diagnostic.reset(diagnostic_ns, diagnostic_bufnr) + return vim.g.diagnostic_autocmd_triggered + ]]) + end) end) end) diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 7dcca231ee..f25cfa2039 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -6,9 +6,11 @@ local poke_eventloop = helpers.poke_eventloop local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file -local command= helpers.command +local command = helpers.command local exc_exec = helpers.exc_exec local matches = helpers.matches +local exec_lua = helpers.exec_lua +local sleep = helpers.sleep describe(':terminal buffer', function() local screen @@ -328,3 +330,37 @@ describe('No heap-buffer-overflow when', function() assert_alive() end) end) + +describe('on_lines does not emit out-of-bounds line indexes when', function() + before_each(function() + clear() + exec_lua([[ + function _G.register_callback(bufnr) + _G.cb_error = '' + vim.api.nvim_buf_attach(bufnr, false, { + on_lines = function(_, bufnr, _, firstline, _, _) + local status, msg = pcall(vim.api.nvim_buf_get_offset, bufnr, firstline) + if not status then + _G.cb_error = msg + end + end + }) + end + ]]) + end) + + it('creating a terminal buffer #16394', function() + feed_command([[autocmd TermOpen * ++once call v:lua.register_callback(expand("<abuf>"))]]) + feed_command('terminal') + sleep(500) + eq('', exec_lua([[return _G.cb_error]])) + end) + + it('deleting a terminal buffer #16394', function() + feed_command('terminal') + sleep(500) + feed_command('lua _G.register_callback(0)') + feed_command('bdelete!') + eq('', exec_lua([[return _G.cb_error]])) + end) +end) diff --git a/test/functional/terminal/cursor_spec.lua b/test/functional/terminal/cursor_spec.lua index 8d70ebf679..e9495f45a2 100644 --- a/test/functional/terminal/cursor_spec.lua +++ b/test/functional/terminal/cursor_spec.lua @@ -3,6 +3,8 @@ local Screen = require('test.functional.ui.screen') local thelpers = require('test.functional.terminal.helpers') local feed, clear, nvim = helpers.feed, helpers.clear, helpers.nvim local nvim_dir, command = helpers.nvim_dir, helpers.command +local nvim_prog = helpers.nvim_prog +local eq, eval = helpers.eq, helpers.eval local feed_command = helpers.feed_command local hide_cursor = thelpers.hide_cursor local show_cursor = thelpers.show_cursor @@ -173,3 +175,705 @@ describe('cursor with customized highlighting', function() end) end) +describe('buffer cursor position is correct in terminal without number column', function() + local screen + + local function setup_ex_register(str) + screen = thelpers.screen_setup(0, '["'..nvim_prog + ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + -- <Left> and <Right> don't always work + ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :{1: } | + {3:-- TERMINAL --} | + ]]) + end + + before_each(clear) + + describe('in a line with no multibyte characters or trailing spaces,', function() + before_each(function() + setup_ex_register('aaaaaaaa') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaaaa{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 9}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaaa^a{2: } | + | + ]]) + eq({6, 8}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaaa{1:a}a | + {3:-- TERMINAL --} | + ]]) + eq({6, 7}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :aaaaa^a{2:a}a | + | + ]]) + eq({6, 6}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :a{1:a}aaaaaa | + {3:-- TERMINAL --} | + ]]) + eq({6, 2}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^a{2:a}aaaaaa | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + before_each(function() + setup_ex_register('µµµµµµµµ') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµµµ{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 17}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµµ^µ{2: } | + | + ]]) + eq({6, 15}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµµ{1:µ}µ | + {3:-- TERMINAL --} | + ]]) + eq({6, 13}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µµµµµ^µ{2:µ}µ | + | + ]]) + eq({6, 11}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ{1:µ}µµµµµµ | + {3:-- TERMINAL --} | + ]]) + eq({6, 3}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^µ{2:µ}µµµµµµ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 33}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{2: } | + | + ]]) + eq({6, 29}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳µ̳µ̳µ̳µ̳^µ̳{2:µ̳}µ̳ | + | + ]]) + eq({6, 21}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 5}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^µ̳{2:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with double-cell multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('哦哦哦哦哦哦哦哦') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦哦哦{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦哦^哦{2: } | + | + ]]) + eq({6, 22}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦哦{1:哦}哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 19}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦哦哦哦哦^哦{2:哦}哦 | + | + ]]) + eq({6, 16}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :哦{1:哦}哦哦哦哦哦哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 4}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + | + | + | + | + Entering Ex mode. Type "visual" to go to Normal mode. | + :^哦{2:哦}哦哦哦哦哦哦 | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) +end) + +describe('buffer cursor position is correct in terminal with number column', function() + local screen + + local function setup_ex_register(str) + screen = thelpers.screen_setup(0, '["'..nvim_prog + ..[[", "-u", "NONE", "-i", "NONE", "-E", "--cmd", "let @r = ']]..str..[['", ]] + -- <Left> and <Right> don't always work + ..[["--cmd", "cnoremap <C-X> <Left>", "--cmd", "cnoremap <C-O> <Right>"]]..']', 70) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:{1: } | + {3:-- TERMINAL --} | + ]]) + end + + before_each(function() + clear() + command('set number') + end) + + describe('in a line with no multibyte characters or trailing spaces,', function() + before_each(function() + setup_ex_register('aaaaaaaa') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaaaa{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 9}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaaa^a{2: } | + | + ]]) + eq({6, 8}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaaa{1:a}a | + {3:-- TERMINAL --} | + ]]) + eq({6, 7}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:aaaaa^a{2:a}a | + | + ]]) + eq({6, 6}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:a{1:a}aaaaaa | + {3:-- TERMINAL --} | + ]]) + eq({6, 2}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^a{2:a}aaaaaa | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell multibyte characters and no trailing spaces,', function() + before_each(function() + setup_ex_register('µµµµµµµµ') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµµµ{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 17}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµµ^µ{2: } | + | + ]]) + eq({6, 15}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµµ{1:µ}µ | + {3:-- TERMINAL --} | + ]]) + eq({6, 13}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µµµµµ^µ{2:µ}µ | + | + ]]) + eq({6, 11}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ{1:µ}µµµµµµ | + {3:-- TERMINAL --} | + ]]) + eq({6, 3}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^µ{2:µ}µµµµµµ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with single-cell composed multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳µ̳{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 33}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳µ̳^µ̳{2: } | + | + ]]) + eq({6, 29}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳µ̳{1:µ̳}µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳µ̳µ̳µ̳µ̳^µ̳{2:µ̳}µ̳ | + | + ]]) + eq({6, 21}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:µ̳{1:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + {3:-- TERMINAL --} | + ]]) + eq({6, 5}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^µ̳{2:µ̳}µ̳µ̳µ̳µ̳µ̳µ̳ | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) + + describe('in a line with double-cell multibyte characters and no trailing spaces,', function() + if helpers.pending_win32(pending) then return end -- These tests fail on Windows. Encoding problem? + + before_each(function() + setup_ex_register('哦哦哦哦哦哦哦哦') + end) + + it('at the end', function() + feed('<C-R>r') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦哦哦{1: } | + {3:-- TERMINAL --} | + ]]) + eq({6, 25}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦哦^哦{2: } | + | + ]]) + eq({6, 22}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the end', function() + feed('<C-R>r<C-X><C-X>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦哦{1:哦}哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 19}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦哦哦哦哦^哦{2:哦}哦 | + | + ]]) + eq({6, 16}, eval('nvim_win_get_cursor(0)')) + end) + + it('near the start', function() + feed('<C-R>r<C-B><C-O>') + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:哦{1:哦}哦哦哦哦哦哦 | + {3:-- TERMINAL --} | + ]]) + eq({6, 4}, eval('nvim_win_get_cursor(0)')) + feed([[<C-\><C-N>]]) + screen:expect([[ + {7: 1 } | + {7: 2 } | + {7: 3 } | + {7: 4 } | + {7: 5 }Entering Ex mode. Type "visual" to go to Normal mode. | + {7: 6 }:^哦{2:哦}哦哦哦哦哦哦 | + | + ]]) + eq({6, 1}, eval('nvim_win_get_cursor(0)')) + end) + end) +end) diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index dce6384b9b..1575cab591 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1031,6 +1031,69 @@ if (h->n_buckets < new_n_buckets) { // expand | ]]} + screen:try_resize(50, 11) + feed('gg') + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + feed('G<C-E>') + screen:expect{grid=[[ + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + + feed('gg') + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + screen:try_resize(50, 12) + feed('G') + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + meths.buf_del_extmark(0, ns, id) screen:expect{grid=[[ if (h->n_buckets < new_n_buckets) { // expand | diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index 13949b0756..bd2692d19a 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -186,6 +186,19 @@ describe('Diff mode screen', function() {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) + + screen:try_resize(40, 9) + screen:expect([[ + {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}| + {1: }5 {3:│}{1: }5 | + {1: }6 {3:│}{1: }6 | + {1: }7 {3:│}{1: }7 | + {1: }8 {3:│}{1: }8 | + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + | + ]]) end) it('Add a line at the end of file 1', function() @@ -232,6 +245,19 @@ describe('Diff mode screen', function() {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| :set diffopt+=internal | ]]) + + screen:try_resize(40, 9) + screen:expect([[ + {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}| + {1: }5 {3:│}{1: }5 | + {1: }6 {3:│}{1: }6 | + {1: }7 {3:│}{1: }7 | + {1: }8 {3:│}{1: }8 | + {1: }9 {3:│}{1: }9 | + {1: }10 {3:│}{1: }10 | + {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }| + | + ]]) end) it('Add a line in the middle of file 2, remove on at the end of file 1', function() diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index c00d30fe32..0983d0d4ad 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -276,6 +276,24 @@ describe('highlight defaults', function() ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) end) + it('linking updates window highlight immediately #16552', function() + screen:try_resize(53, 4) + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + | + ]], {[0] = {bold=true, foreground=Screen.colors.Blue}}) + feed_command("hi NonTextAlt guifg=Red") + feed_command("hi! link NonText NonTextAlt") + screen:expect([[ + ^ | + {0:~ }| + {0:~ }| + :hi! link NonText NonTextAlt | + ]], {[0] = {foreground=Screen.colors.Red}}) + end) + it('Cursor after `:hi clear|syntax reset` #6508', function() command('highlight clear|syntax reset') eq('guifg=bg guibg=fg', eval([[matchstr(execute('hi Cursor'), '\v(gui|cterm).*$')]])) diff --git a/test/functional/vimscript/timer_spec.lua b/test/functional/vimscript/timer_spec.lua index 9ee0735e40..e45b64422f 100644 --- a/test/functional/vimscript/timer_spec.lua +++ b/test/functional/vimscript/timer_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local feed, eq, eval, ok = helpers.feed, helpers.eq, helpers.eval, helpers.ok local source, nvim_async, run = helpers.source, helpers.nvim_async, helpers.run local clear, command, funcs = helpers.clear, helpers.command, helpers.funcs +local exc_exec = helpers.exc_exec local curbufmeths = helpers.curbufmeths local load_adjust = helpers.load_adjust local retry = helpers.retry @@ -262,4 +263,13 @@ describe('timers', function() eq(2, eval('g:val')) end) + + it("timer_start can't be used in the sandbox", function() + source [[ + function! Scary(timer) abort + call execute('echo ''execute() should be disallowed''', '') + endfunction + ]] + eq("Vim(call):E48: Not allowed in sandbox", exc_exec("sandbox call timer_start(0, 'Scary')")) + end) end) diff --git a/test/helpers.lua b/test/helpers.lua index 09b113c01d..87431e4342 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -741,9 +741,20 @@ function module.read_file_list(filename, start) if not file then return nil end + + -- There is no need to read more than the last 2MB of the log file, so seek + -- to that. + local file_size = file:seek("end") + local offset = file_size - 2000000 + if offset < 0 then + offset = 0 + end + file:seek("set", offset) + local lines = {} local i = 1 - for line in file:lines() do + local line = file:read("*l") + while line ~= nil do if i >= start then table.insert(lines, line) if #lines > maxlines then @@ -751,6 +762,7 @@ function module.read_file_list(filename, start) end end i = i + 1 + line = file:read("*l") end file:close() return lines |