diff options
-rw-r--r-- | cmake.deps/deps.txt | 4 | ||||
-rw-r--r-- | runtime/doc/builtin.txt | 69 | ||||
-rw-r--r-- | runtime/doc/luvref.txt | 46 | ||||
-rw-r--r-- | runtime/doc/news.txt | 7 | ||||
-rw-r--r-- | runtime/doc/treesitter.txt | 2 | ||||
-rw-r--r-- | runtime/doc/usr_41.txt | 1 | ||||
-rw-r--r-- | runtime/lua/vim/_defaults.lua | 46 | ||||
-rw-r--r-- | runtime/lua/vim/_meta/vimfn.lua | 42 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter.lua | 3 | ||||
-rw-r--r-- | runtime/lua/vim/treesitter/dev.lua | 5 | ||||
-rw-r--r-- | runtime/syntax/java.vim | 6 | ||||
-rw-r--r-- | runtime/syntax/spec.vim | 2 | ||||
-rw-r--r-- | runtime/syntax/tmux.vim | 8 | ||||
-rw-r--r-- | runtime/syntax/vim.vim | 9 | ||||
-rwxr-xr-x | scripts/gen_eval_files.lua | 22 | ||||
-rw-r--r-- | src/nvim/eval.lua | 45 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 144 | ||||
-rw-r--r-- | src/nvim/ops.c | 141 | ||||
-rw-r--r-- | src/nvim/ops.h | 19 | ||||
-rw-r--r-- | test/functional/options/defaults_spec.lua | 19 | ||||
-rw-r--r-- | test/old/testdir/test_visual.vim | 164 |
21 files changed, 633 insertions, 171 deletions
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt index bd9f9d4247..716a892263 100644 --- a/cmake.deps/deps.txt +++ b/cmake.deps/deps.txt @@ -16,8 +16,8 @@ UNIBILIUM_SHA256 9c4747c862ab5e3076dcf8fa8f0ea7a6b50f20ec5905618b953665559679748 LIBVTERM_URL https://github.com/neovim/libvterm/archive/v0.3.3.tar.gz LIBVTERM_SHA256 0babe3ab42c354925dadede90d352f054aa9c4ae6842ea803a20c9741e172e56 -LUV_URL https://github.com/luvit/luv/archive/1.47.0-0.tar.gz -LUV_SHA256 f9911d9d33808514e9ddc1e74667a57c427efa14fefecfa5fabe80ce95a3150a +LUV_URL https://github.com/luvit/luv/archive/v1.48.0-0.tar.gz +LUV_SHA256 c9c12e414f8045aea11e5dbb44cd6c68196c10e02e1294dad971c367976cc1f9 LPEG_URL https://github.com/neovim/deps/raw/d495ee6f79e7962a53ad79670cb92488abe0b9b4/opt/lpeg-1.1.0.tar.gz LPEG_SHA256 4b155d67d2246c1ffa7ad7bc466c1ea899bbc40fef0257cc9c03cecbaed4352a diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index fc1c770de7..d6a14d9227 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -965,8 +965,8 @@ ctxset({context} [, {index}]) *ctxset()* ctxsize() *ctxsize()* Returns the size of the |context-stack|. -cursor({lnum}, {col} [, {off}]) -cursor({list}) *cursor()* +cursor({lnum}, {col} [, {off}]) *cursor()* +cursor({list}) Positions the cursor at the column (byte count) {col} in the line {lnum}. The first column is one. @@ -2055,8 +2055,8 @@ get({func}, {what}) "args" The list with arguments Returns zero on error. -getbufinfo([{buf}]) -getbufinfo([{dict}]) *getbufinfo()* +getbufinfo([{buf}]) *getbufinfo()* +getbufinfo([{dict}]) Get information about buffers as a List of Dictionaries. Without an argument information about all the buffers is @@ -2918,6 +2918,43 @@ getreginfo([{regname}]) *getreginfo()* If {regname} is not specified, |v:register| is used. The returned Dictionary can be passed to |setreg()|. +getregion({pos1}, {pos2}, {type}) *getregion()* + Returns the list of strings from {pos1} to {pos2} as if it's + selected in visual mode of {type}. + For possible values of {pos1} and {pos2} see |line()|. + {type} is the selection type: + "v" for |charwise| mode + "V" for |linewise| mode + "<CTRL-V>" for |blockwise-visual| mode + You can get the last selection type by |visualmode()|. + If Visual mode is active, use |mode()| to get the Visual mode + (e.g., in a |:vmap|). + This function uses the line and column number from the + specified position. + It is useful to get text starting and ending in different + columns, such as |charwise-visual| selection. + + Note that: + - Order of {pos1} and {pos2} doesn't matter, it will always + return content from the upper left position to the lower + right position. + - If 'virtualedit' is enabled and selection is past the end of + line, resulting lines are filled with blanks. + - If the selection starts or ends in the middle of a multibyte + character, it is not included but its selected part is + substituted with spaces. + - If {pos1} or {pos2} equals "v" (see |line()|) and it is not in + |visual-mode|, an empty list is returned. + - If {pos1}, {pos2} or {type} is an invalid string, an empty + list is returned. + - If {pos1} or {pos2} is a mark in different buffer, an empty + list is returned. + + Examples: > + :xnoremap <CR> + \ <Cmd>echom getregion('v', '.', mode())<CR> +< + getregtype([{regname}]) *getregtype()* The result is a String, which is type of register {regname}. The value will be one of: @@ -4310,8 +4347,8 @@ mapnew({expr1}, {expr2}) *mapnew()* unchanged. Items can still be changed by {expr2}, if you don't want that use |deepcopy()| first. -mapset({mode}, {abbr}, {dict}) -mapset({dict}) *mapset()* +mapset({mode}, {abbr}, {dict}) *mapset()* +mapset({dict}) Restore a mapping from a dictionary, possibly returned by |maparg()| or |maplist()|. A buffer mapping, when dict.buffer is true, is set on the current buffer; it is up to the caller @@ -5740,9 +5777,9 @@ reg_recording() *reg_recording()* Returns the single letter name of the register being recorded. Returns an empty string when not recording. See |q|. -reltime() +reltime() *reltime()* reltime({start}) -reltime({start}, {end}) *reltime()* +reltime({start}, {end}) Return an item that represents a time value. The item is a list with items that depend on the system. The item can be passed to |reltimestr()| to convert it to a @@ -5787,8 +5824,8 @@ reltimestr({time}) *reltimestr()* < Also see |profiling|. If there is an error an empty string is returned -remove({list}, {idx}) -remove({list}, {idx}, {end}) *remove()* +remove({list}, {idx}) *remove()* +remove({list}, {idx}, {end}) Without {end}: Remove the item at {idx} from |List| {list} and return the item. With {end}: Remove items from {idx} to {end} (inclusive) and @@ -6517,8 +6554,8 @@ setcmdpos({pos}) *setcmdpos()* Returns 0 when successful, 1 when not editing the command line. -setcursorcharpos({lnum}, {col} [, {off}]) -setcursorcharpos({list}) *setcursorcharpos()* +setcursorcharpos({lnum}, {col} [, {off}]) *setcursorcharpos()* +setcursorcharpos({list}) Same as |cursor()| but uses the specified column number as the character index instead of the byte index in the line. @@ -6944,8 +6981,8 @@ shiftwidth([{col}]) *shiftwidth()* 'vartabstop' feature. If no {col} argument is given, column 1 will be assumed. -sign_define({name} [, {dict}]) -sign_define({list}) *sign_define()* +sign_define({name} [, {dict}]) *sign_define()* +sign_define({list}) Define a new sign named {name} or modify the attributes of an existing sign. This is similar to the |:sign-define| command. @@ -7212,8 +7249,8 @@ sign_placelist({list}) *sign_placelist()* \ ]) < -sign_undefine([{name}]) -sign_undefine({list}) *sign_undefine()* +sign_undefine([{name}]) *sign_undefine()* +sign_undefine({list}) Deletes a previously defined sign {name}. This is similar to the |:sign-undefine| command. If {name} is not supplied, then deletes all the defined signs. diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt index 1a811a361b..2be73f0412 100644 --- a/runtime/doc/luvref.txt +++ b/runtime/doc/luvref.txt @@ -837,7 +837,7 @@ uv.new_prepare() *uv.new_prepare()* Creates and initializes a new |uv_prepare_t|. Returns the Lua userdata wrapping it. - Returns: `uv_prepare_t userdata` or `fail` + Returns: `uv_prepare_t userdata` uv.prepare_start({prepare}, {callback}) *uv.prepare_start()* @@ -882,7 +882,7 @@ uv.new_check() *uv.new_check()* Creates and initializes a new |uv_check_t|. Returns the Lua userdata wrapping it. - Returns: `uv_check_t userdata` or `fail` + Returns: `uv_check_t userdata` uv.check_start({check}, {callback}) *uv.check_start()* @@ -934,7 +934,7 @@ uv.new_idle() *uv.new_idle()* Creates and initializes a new |uv_idle_t|. Returns the Lua userdata wrapping it. - Returns: `uv_idle_t userdata` or `fail` + Returns: `uv_idle_t userdata` uv.idle_start({idle}, {callback}) *uv.idle_start()* @@ -2077,7 +2077,7 @@ uv.pipe_bind2({pipe}, {name}, {flags}) *uv.pipe_bind2()* - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. - If `type(flags)` is `table`, it must be `{}` or - `{ no_trunate = true|false }`. + `{ no_truncate = true|false }`. - If `type(flags)` is `nil`, it use default value `0`. - Returns `EINVAL` for unsupported flags without performing the bind. @@ -2110,7 +2110,7 @@ uv.pipe_connect2(pipe, name, [flags], [callback]) *uv.pipe_connect2()* - If `type(flags)` is `number`, it must be `0` or `uv.constants.PIPE_NO_TRUNCATE`. - If `type(flags)` is `table`, it must be `{}` or - `{ no_trunate = true|false }`. + `{ no_truncate = true|false }`. - If `type(flags)` is `nil`, it use default value `0`. - Returns `EINVAL` for unsupported flags without performing the bind operation. @@ -3630,6 +3630,42 @@ uv.thread_getcpu() *uv.thread_getcpu()* Returns: `integer` or `fail` +uv.thread_setpriority({thread}, {priority}) *uv.thread.setpriority()* + + > method form `thread:setpriority(priority)` + + Parameters: + - `thread`: `luv_thread_t userdata` + - `priority`: `number` + + Sets the specified thread's scheduling priority setting. It + requires elevated privilege to set specific priorities on some + platforms. The priority can be set to the following constants: + - `uv.constants.THREAD_PRIORITY_HIGHEST` + - `uv.constants.THREAD_PRIORITY_ABOVE_NORMAL` + - `uv.constants.THREAD_PRIORITY_NORMAL` + - `uv.constants.THREAD_PRIORITY_BELOW_NORMAL` + - `uv.constants.THREAD_PRIORITY_LOWEST` + + Returns: `boolean` or `fail` + +uv.thread_getpriority({thread}) *uv.thread.getpriority()* + + > method form `thread:getpriority()` + + Parameters: + - `thread`: `luv_thread_t userdata` + + Gets the thread's priority setting. + + Retrieves the scheduling priority of the specified thread. The + returned priority value is platform dependent. + + For Linux, when schedule policy is SCHED_OTHER (default), + priority is 0. + + Returns: `number` or `fail` + uv.thread_self() *uv.thread_self()* Returns the handle for the thread in which this is called. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index a5a13602e2..9ce96b7a67 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -231,9 +231,10 @@ The following new APIs and features were added. • |vim.treesitter.query.edit()| allows live editing of treesitter queries. • Improved error messages for query parsing. - • `:InspectTree` (|vim.treesitter.inspect_tree()|) shows node ranges in - 0-based indexing instead of 1-based indexing. - • `:InspectTree` (|vim.treesitter.inspect_tree()|) shows root nodes + • |:InspectTree| shows node ranges in 0-based indexing instead of 1-based + indexing. + • |:InspectTree| shows root nodes + • |:InspectTree| now supports |folding| • |vim.ui.open()| opens URIs using the system default handler (macOS `open`, Windows `explorer`, Linux `xdg-open`, etc.) diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index f92955ee48..fd23f0c84a 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -821,7 +821,7 @@ inspect_tree({opts}) *vim.treesitter.inspect_tree()* While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the display of the source language of each node, "o" to toggle the query editor, and press <Enter> to jump to the node under the cursor - in the source buffer. + in the source buffer. Folding also works (try |zo|, |zc|, etc.). Can also be shown with `:InspectTree`. *:InspectTree* diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 2dae9333b6..56750420e9 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -793,6 +793,7 @@ Cursor and mark position: *cursor-functions* *mark-functions* Working with text in the current buffer: *text-functions* getline() get a line or list of lines from the buffer + getregion() get a region of text from the buffer setline() replace a line in the buffer append() append line or list of lines in the buffer indent() indent of a specific line diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 07850a5a47..32534a89b4 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -4,27 +4,9 @@ do --- --- See |v_star-default| and |v_#-default| do - local function region_chunks(region) - local chunks = {} - local maxcol = vim.v.maxcol - for line, cols in vim.spairs(region) do - local endcol = cols[2] == maxcol and -1 or cols[2] - local chunk = vim.api.nvim_buf_get_text(0, line, cols[1], line, endcol, {})[1] - table.insert(chunks, chunk) - end - return chunks - end - local function _visual_search(cmd) assert(cmd == '/' or cmd == '?') - local region = vim.region( - 0, - '.', - 'v', - vim.api.nvim_get_mode().mode:sub(1, 1), - vim.o.selection == 'inclusive' - ) - local chunks = region_chunks(region) + local chunks = vim.fn.getregion('.', 'v', vim.fn.mode()) local esc_chunks = vim .iter(chunks) :map(function(v) @@ -143,14 +125,16 @@ do vim.api.nvim_create_autocmd({ 'TermClose' }, { group = nvim_terminal_augroup, + nested = true, desc = 'Automatically close terminal buffers when started with no arguments and exiting without an error', callback = function(args) - if vim.v.event.status == 0 then - local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel) - local argv = info.argv or {} - if #argv == 1 and argv[1] == vim.o.shell then - vim.cmd({ cmd = 'bdelete', args = { args.buf }, bang = true }) - end + if vim.v.event.status ~= 0 then + return + end + local info = vim.api.nvim_get_chan_info(vim.bo[args.buf].channel) + local argv = info.argv or {} + if #argv == 1 and argv[1] == vim.o.shell then + vim.api.nvim_buf_delete(args.buf, { force = true }) end end, }) @@ -343,17 +327,7 @@ if tty then end, }) - local query = '\027]11;?\007' - - -- tmux 3.3a and earlier do not query the parent terminal for background color. As of the - -- writing of this comment, 3.3a is the latest release, so a passthrough sequence is necessary. - -- The passthrough should be removed as soon as a tmux version later than 3.3a is released. - -- See: https://github.com/neovim/neovim/pull/26557 - if os.getenv('TMUX') then - query = string.format('\027Ptmux;%s\027\\', query:gsub('\027', '\027\027')) - end - - io.stdout:write(query) + io.stdout:write('\027]11;?\007') timer:start(1000, 0, function() -- Delete the autocommand if no response was received diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 224cecf144..623ce2bc0f 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -3525,6 +3525,48 @@ function vim.fn.getreg(regname, list) end --- @return table function vim.fn.getreginfo(regname) end +--- Returns the list of strings from {pos1} to {pos2} as if it's +--- selected in visual mode of {type}. +--- For possible values of {pos1} and {pos2} see |line()|. +--- {type} is the selection type: +--- "v" for |charwise| mode +--- "V" for |linewise| mode +--- "<CTRL-V>" for |blockwise-visual| mode +--- You can get the last selection type by |visualmode()|. +--- If Visual mode is active, use |mode()| to get the Visual mode +--- (e.g., in a |:vmap|). +--- This function uses the line and column number from the +--- specified position. +--- It is useful to get text starting and ending in different +--- columns, such as |charwise-visual| selection. +--- +--- Note that: +--- - Order of {pos1} and {pos2} doesn't matter, it will always +--- return content from the upper left position to the lower +--- right position. +--- - If 'virtualedit' is enabled and selection is past the end of +--- line, resulting lines are filled with blanks. +--- - If the selection starts or ends in the middle of a multibyte +--- character, it is not included but its selected part is +--- substituted with spaces. +--- - If {pos1} or {pos2} equals "v" (see |line()|) and it is not in +--- |visual-mode|, an empty list is returned. +--- - If {pos1}, {pos2} or {type} is an invalid string, an empty +--- list is returned. +--- - If {pos1} or {pos2} is a mark in different buffer, an empty +--- list is returned. +--- +--- Examples: > +--- :xnoremap <CR> +--- \ <Cmd>echom getregion('v', '.', mode())<CR> +--- < +--- +--- @param pos1 string +--- @param pos2 string +--- @param type string +--- @return string[] +function vim.fn.getregion(pos1, pos2, type) end + --- The result is a String, which is type of register {regname}. --- The value will be one of: --- "v" for |charwise| text diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 88b68ff658..2aa46ceebd 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -461,7 +461,8 @@ end --- --- While in the window, press "a" to toggle display of anonymous nodes, "I" to toggle the --- display of the source language of each node, "o" to toggle the query editor, and press ---- <Enter> to jump to the node under the cursor in the source buffer. +--- <Enter> to jump to the node under the cursor in the source buffer. Folding also works +--- (try |zo|, |zc|, etc.). --- --- Can also be shown with `:InspectTree`. *:InspectTree* --- diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 551067533a..4c8f6e466f 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -159,7 +159,10 @@ end local function set_dev_properties(w, b) vim.wo[w].scrolloff = 5 vim.wo[w].wrap = false - vim.wo[w].foldmethod = 'manual' -- disable folding + vim.wo[w].foldmethod = 'expr' + vim.wo[w].foldexpr = 'v:lua.vim.treesitter.foldexpr()' -- explicitly set foldexpr + vim.wo[w].foldenable = false -- Don't fold on first open InspectTree + vim.wo[w].foldlevel = 99 vim.bo[b].buflisted = false vim.bo[b].buftype = 'nofile' vim.bo[b].bufhidden = 'wipe' diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim index 00d6071944..44fbfa8398 100644 --- a/runtime/syntax/java.vim +++ b/runtime/syntax/java.vim @@ -2,7 +2,7 @@ " Language: Java " Maintainer: Claudio Fleiner <claudio@fleiner.com> " URL: https://github.com/fleiner/vim/blob/master/runtime/syntax/java.vim -" Last Change: 2022 Jun 08 +" Last Change: 2023 Aug 13 " Please check :help java.vim for comments on some of the options available. @@ -39,7 +39,7 @@ syn keyword javaTypedef this super syn keyword javaOperator var new instanceof syn keyword javaType boolean char byte short int long float double syn keyword javaType void -syn keyword javaStatement return +syn keyword javaStatement return yield syn keyword javaStorageClass static synchronized transient volatile final strictfp serializable syn keyword javaExceptions throw try catch finally syn keyword javaAssert assert @@ -142,7 +142,7 @@ if exists("java_space_errors") endif endif -syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" end="->" matchgroup=NONE end=":" contains=javaNumber,javaCharacter,javaString +syn region javaLabelRegion transparent matchgroup=javaLabel start="\<case\>" matchgroup=NONE end=":\|->" contains=javaNumber,javaCharacter,javaString syn match javaUserLabel "^\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\s*:"he=e-1 contains=javaLabel syn keyword javaLabel default diff --git a/runtime/syntax/spec.vim b/runtime/syntax/spec.vim index aed04bc900..12ce8d5ac1 100644 --- a/runtime/syntax/spec.vim +++ b/runtime/syntax/spec.vim @@ -102,7 +102,7 @@ syn case ignore "%% PreAmble Section %% "Copyright and Serial were deprecated by License and Epoch syn region specPreAmbleDeprecated oneline matchgroup=specError start='^\(Copyright\|Serial\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier -syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Recommends\|Suggests\|Supplements\|Enhances\|Icon\|URL\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier +syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Recommends\|Suggests\|Supplements\|Enhances\|Icon\|URL\|SourceLicense\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier "%% Description Section %% syn region specDescriptionArea matchgroup=specSection start='^%description' end='^%'me=e-1 contains=specDescriptionOpts,specEmail,specURL,specNumber,specMacroIdentifier,specComment diff --git a/runtime/syntax/tmux.vim b/runtime/syntax/tmux.vim index d2b31e874c..9766ed55d7 100644 --- a/runtime/syntax/tmux.vim +++ b/runtime/syntax/tmux.vim @@ -1,5 +1,5 @@ " Language: tmux(1) configuration file -" Version: 3.3a (git-e7c829fc) +" Version: 3.4 (git-608d1134) " URL: https://github.com/ericpruitt/tmux.vim/ " Maintainer: Eric Pruitt <eric.pruitt@gmail.com> " License: 2-Clause BSD (http://opensource.org/licenses/BSD-2-Clause) @@ -164,9 +164,9 @@ syn keyword tmuxCommands syn keyword tmuxEnums \ absolute-centre all always any arrows bar blinking-bar blinking-block \ blinking-underline block both bottom centre color colour current default -\ double emacs external failed heavy largest latest left manual next -\ no-detached none number off on other padded previous right rounded simple -\ single smallest top underline vi +\ double emacs external failed heavy keep-group keep-last largest latest left +\ manual next no-detached none number off on other padded previous right +\ rounded simple single smallest top underline vi let &cpo = s:original_cpo unlet! s:original_cpo s:bg s:i diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 846a3be905..d2430add19 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -416,7 +416,7 @@ syn keyword vimFor for skipwhite nextgroup=vimVar,vimVarList " Abbreviations: {{{2 " ============= " GEN_SYN_VIM: vimCommand abbrev, START_STR='syn keyword vimAbb', END_STR='skipwhite nextgroup=vimMapMod,vimMapLhs' -syn keyword vimAbb ab[breviate] ca[bbrev] cnorea[bbrev] ia[bbrev] inorea[bbrev] norea[bbrev] skipwhite nextgroup=vimMapMod,vimMapLhs +syn keyword vimAbb ab[breviate] ca[bbrev] cnorea[bbrev] cuna[bbrev] ia[bbrev] inorea[bbrev] iuna[bbrev] norea[bbrev] una[bbreviate] skipwhite nextgroup=vimMapMod,vimMapLhs " Autocmd: {{{2 " ======= @@ -441,12 +441,13 @@ syn match vimMap "\<map\>\ze\s*(\@!" skipwhite nextgroup=vimMapMod,vimMapL syn match vimMap "\<map!" contains=vimMapBang skipwhite nextgroup=vimMapMod,vimMapLhs " GEN_SYN_VIM: vimCommand map, START_STR='syn keyword vimMap', END_STR='skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs' syn keyword vimMap cm[ap] cno[remap] im[ap] ino[remap] lm[ap] ln[oremap] nm[ap] nn[oremap] no[remap] om[ap] ono[remap] smap snor[emap] tma[p] tno[remap] vm[ap] vn[oremap] xm[ap] xn[oremap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs -" GEN_SYN_VIM: vimCommand mapclear, START_STR='syn keyword vimMap', END_STR='' -syn keyword vimMap cmapc[lear] imapc[lear] lmapc[lear] mapc[lear] nmapc[lear] omapc[lear] smapc[lear] tmapc[lear] vmapc[lear] xmapc[lear] +" GEN_SYN_VIM: vimCommand mapclear, START_STR='syn keyword vimMap', END_STR='skipwhite nextgroup=vimMapMod' +syn keyword vimMap cmapc[lear] imapc[lear] lmapc[lear] nmapc[lear] omapc[lear] smapc[lear] tmapc[lear] vmapc[lear] xmapc[lear] skipwhite nextgroup=vimMapMod +syn keyword vimMap mapc[lear] skipwhite nextgroup=vimMapBang,vimMapMod " GEN_SYN_VIM: vimCommand unmap, START_STR='syn keyword vimUnmap', END_STR='skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs' syn keyword vimUnmap cu[nmap] iu[nmap] lu[nmap] nun[map] ou[nmap] sunm[ap] tunma[p] unm[ap] vu[nmap] xu[nmap] skipwhite nextgroup=vimMapBang,vimMapMod,vimMapLhs syn match vimMapLhs contained "\S\+" contains=vimNotation,vimCtrlChar skipwhite nextgroup=vimMapRhs -syn match vimMapBang contained "!" skipwhite nextgroup=vimMapMod,vimMapLhs +syn match vimMapBang contained "\a\@1<=!" skipwhite nextgroup=vimMapMod,vimMapLhs syn match vimMapMod contained "\%#=1\c<\(buffer\|expr\|\(local\)\=leader\|nowait\|plug\|script\|sid\|unique\|silent\)\+>" contains=vimMapModKey,vimMapModErr skipwhite nextgroup=vimMapMod,vimMapLhs syn match vimMapRhs contained ".*" contains=vimNotation,vimCtrlChar skipnl nextgroup=vimMapRhsExtend syn match vimMapRhsExtend contained "^\s*\\.*$" contains=vimContinue diff --git a/scripts/gen_eval_files.lua b/scripts/gen_eval_files.lua index 78fc1c7d4f..b7f17a2d58 100755 --- a/scripts/gen_eval_files.lua +++ b/scripts/gen_eval_files.lua @@ -411,9 +411,6 @@ local function render_eval_meta(f, fun, write) write(render_fun_sig(funname, params)) end ---- @type table<string,true> -local rendered_tags = {} - --- @param name string --- @param fun vim.EvalFn --- @param write fun(line: string) @@ -455,24 +452,17 @@ local function render_eval_doc(f, fun, write) return end - local desc = fun.desc - - if not desc then + if f:find('__%d+$') then write(fun.signature) - return + else + render_sig_and_tag(fun.name or f, fun, write) end - local name = fun.name or f - - if rendered_tags[name] then - write(fun.signature) - else - render_sig_and_tag(name, fun, write) - rendered_tags[name] = true + if not fun.desc then + return end - desc = vim.trim(desc) - local desc_l = split(desc) + local desc_l = split(vim.trim(fun.desc)) for _, l in ipairs(desc_l) do l = l:gsub('^ ', '') if vim.startswith(l, '<') and not l:match('^<[^ \t]+>') then diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 2630241077..9cca9c588a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -4355,6 +4355,51 @@ M.funcs = { returns = 'table', signature = 'getreginfo([{regname}])', }, + getregion = { + args = 3, + base = 1, + desc = [=[ + Returns the list of strings from {pos1} to {pos2} as if it's + selected in visual mode of {type}. + For possible values of {pos1} and {pos2} see |line()|. + {type} is the selection type: + "v" for |charwise| mode + "V" for |linewise| mode + "<CTRL-V>" for |blockwise-visual| mode + You can get the last selection type by |visualmode()|. + If Visual mode is active, use |mode()| to get the Visual mode + (e.g., in a |:vmap|). + This function uses the line and column number from the + specified position. + It is useful to get text starting and ending in different + columns, such as |charwise-visual| selection. + + Note that: + - Order of {pos1} and {pos2} doesn't matter, it will always + return content from the upper left position to the lower + right position. + - If 'virtualedit' is enabled and selection is past the end of + line, resulting lines are filled with blanks. + - If the selection starts or ends in the middle of a multibyte + character, it is not included but its selected part is + substituted with spaces. + - If {pos1} or {pos2} equals "v" (see |line()|) and it is not in + |visual-mode|, an empty list is returned. + - If {pos1}, {pos2} or {type} is an invalid string, an empty + list is returned. + - If {pos1} or {pos2} is a mark in different buffer, an empty + list is returned. + + Examples: > + :xnoremap <CR> + \ <Cmd>echom getregion('v', '.', mode())<CR> + < + ]=], + name = 'getregion', + params = { { 'pos1', 'string' }, { 'pos2', 'string' }, { 'type', 'string' } }, + returns = 'string[]', + signature = 'getregion({pos1}, {pos2}, {type})', + }, getregtype = { args = { 0, 1 }, base = 1, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 01e666887a..2e2640604b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -130,6 +130,7 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -2801,6 +2802,149 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) getpos_both(argvars, rettv, false, false); } +/// Convert from block_def to string +static char *block_def2str(struct block_def *bd) +{ + size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen; + char *ret = xmalloc(size + 1); + char *p = ret; + memset(p, ' ', (size_t)bd->startspaces); + p += bd->startspaces; + memmove(p, bd->textstart, (size_t)bd->textlen); + p += bd->textlen; + memset(p, ' ', (size_t)bd->endspaces); + *(p + bd->endspaces) = NUL; + return ret; +} + +/// "getregion()" function +static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (tv_check_for_string_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_string_arg(argvars, 2) == FAIL) { + return; + } + + int fnum = -1; + // NOTE: var2fpos() returns static pointer. + pos_T *fp = var2fpos(&argvars[0], true, &fnum, false); + if (fp == NULL || (fnum >= 0 && fnum != curbuf->b_fnum)) { + return; + } + pos_T p1 = *fp; + + fp = var2fpos(&argvars[1], true, &fnum, false); + if (fp == NULL || (fnum >= 0 && fnum != curbuf->b_fnum)) { + return; + } + pos_T p2 = *fp; + + const char *pos1 = tv_get_string(&argvars[0]); + const char *pos2 = tv_get_string(&argvars[1]); + const char *type = tv_get_string(&argvars[2]); + + const bool is_visual + = (pos1[0] == 'v' && pos1[1] == NUL) || (pos2[0] == 'v' && pos2[1] == NUL); + + if (is_visual && !VIsual_active) { + return; + } + + MotionType region_type = kMTUnknown; + if (type[0] == 'v' && type[1] == NUL) { + region_type = kMTCharWise; + } else if (type[0] == 'V' && type[1] == NUL) { + region_type = kMTLineWise; + } else if (type[0] == Ctrl_V && type[1] == NUL) { + region_type = kMTBlockWise; + } else { + return; + } + + const TriState save_virtual = virtual_op; + virtual_op = virtual_active(); + + if (!lt(p1, p2)) { + // swap position + pos_T p = p1; + p1 = p2; + p2 = p; + } + + oparg_T oa; + bool inclusive = true; + + if (region_type == kMTCharWise) { + // handle 'selection' == "exclusive" + if (*p_sel == 'e' && !equalpos(p1, p2)) { + if (p2.coladd > 0) { + p2.coladd--; + } else if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } else if (p2.lnum > 1) { + p2.lnum--; + p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } + } + } + // if fp2 is on NUL (empty line) inclusive becomes false + if (*ml_get_pos(&p2) == NUL && !virtual_op) { + inclusive = false; + } + } else if (region_type == kMTBlockWise) { + colnr_T sc1, ec1, sc2, ec2; + getvvcol(curwin, &p1, &sc1, NULL, &ec1); + getvvcol(curwin, &p2, &sc2, NULL, &ec2); + oa.motion_type = kMTBlockWise; + oa.inclusive = true; + oa.op_type = OP_NOP; + oa.start = p1; + oa.end = p2; + oa.start_vcol = MIN(sc1, sc2); + if (*p_sel == 'e' && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { + oa.end_vcol = sc2 - 1; + } else { + oa.end_vcol = MAX(ec1, ec2); + } + } + + // Include the trailing byte of a multi-byte char. + int l = utfc_ptr2len(ml_get_pos(&p2)); + if (l > 1) { + p2.col += l - 1; + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + char *akt = NULL; + + if (region_type == kMTLineWise) { + akt = xstrdup(ml_get(lnum)); + } else if (region_type == kMTBlockWise) { + struct block_def bd; + block_prep(&oa, &bd, lnum, false); + akt = block_def2str(&bd); + } else if (p1.lnum < lnum && lnum < p2.lnum) { + akt = xstrdup(ml_get(lnum)); + } else { + struct block_def bd; + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + akt = block_def2str(&bd); + } + + assert(akt != NULL); + tv_list_append_allocated_string(rettv->vval.v_list, akt); + } + + virtual_op = save_virtual; +} + /// Common between getreg(), getreginfo() and getregtype(): get the register /// name from the first argument. /// Returns zero on error. diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e7a3aa29aa..a4af2a54be 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -84,25 +84,6 @@ static bool clipboard_delay_update = false; // delay clipboard update static bool clipboard_needs_update = false; // clipboard was updated static bool clipboard_didwarn = false; -// structure used by block_prep, op_delete and op_yank for blockwise operators -// also op_change, op_shift, op_insert, op_replace - AKelly -struct block_def { - int startspaces; // 'extra' cols before first char - int endspaces; // 'extra' cols after last char - int textlen; // chars in block - char *textstart; // pointer to 1st char (partially) in block - colnr_T textcol; // index of chars (partially) in block - colnr_T start_vcol; // start col of 1st char wholly inside block - colnr_T end_vcol; // start col of 1st char wholly after block - int is_short; // true if line is too short to fit in block - int is_MAX; // true if curswant==MAXCOL when starting - int is_oneChar; // true if block within one character - int pre_whitesp; // screen cols of ws before block - int pre_whitesp_c; // chars of ws before block - colnr_T end_char_vcols; // number of vcols of post-block char - colnr_T start_char_vcols; // number of vcols of pre-block char -}; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ops.c.generated.h" #endif @@ -2655,66 +2636,11 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) reg->y_array[y_idx] = xstrdup(ml_get(lnum)); break; - case kMTCharWise: { - colnr_T startcol = 0; - colnr_T endcol = MAXCOL; - bool is_oneChar = false; - colnr_T cs, ce; - char *p = ml_get(lnum); - bd.startspaces = 0; - bd.endspaces = 0; - - if (lnum == oap->start.lnum) { - startcol = oap->start.col; - if (virtual_op) { - getvcol(curwin, &oap->start, &cs, NULL, &ce); - if (ce != cs && oap->start.coladd > 0) { - // Part of a tab selected -- but don't double-count it. - bd.startspaces = (ce - cs + 1) - oap->start.coladd; - if (bd.startspaces < 0) { - bd.startspaces = 0; - } - startcol++; - } - } - } - - if (lnum == oap->end.lnum) { - endcol = oap->end.col; - if (virtual_op) { - getvcol(curwin, &oap->end, &cs, NULL, &ce); - if (p[endcol] == NUL || (cs + oap->end.coladd < ce - // Don't add space for double-wide - // char; endcol will be on last byte - // of multi-byte char. - && utf_head_off(p, p + endcol) == 0)) { - if (oap->start.lnum == oap->end.lnum - && oap->start.col == oap->end.col) { - // Special case: inside a single char - is_oneChar = true; - bd.startspaces = oap->end.coladd - - oap->start.coladd + oap->inclusive; - endcol = startcol; - } else { - bd.endspaces = oap->end.coladd - + oap->inclusive; - endcol -= oap->inclusive; - } - } - } - } - if (endcol == MAXCOL) { - endcol = (colnr_T)strlen(p); - } - if (startcol > endcol || is_oneChar) { - bd.textlen = 0; - } else { - bd.textlen = endcol - startcol + oap->inclusive; - } - bd.textstart = p + startcol; + case kMTCharWise: + charwise_block_prep(oap->start, oap->end, &bd, lnum, oap->inclusive); yank_copy_line(reg, &bd, y_idx, false); break; - } + // NOTREACHED case kMTUnknown: abort(); @@ -4203,7 +4129,7 @@ static void restore_lbr(bool lbr_saved) /// - textlen includes the first/last char to be wholly yanked /// - start/endspaces is the number of columns of the first/last yanked char /// that are to be yanked. -static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) +void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool is_del) { int incr = 0; // Avoid a problem with unwanted linebreaks in block mode. @@ -4326,6 +4252,65 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool restore_lbr(lbr_saved); } +/// Get block text from "start" to "end" +void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T lnum, + bool inclusive) +{ + colnr_T startcol = 0; + colnr_T endcol = MAXCOL; + bool is_oneChar = false; + colnr_T cs, ce; + char *p = ml_get(lnum); + bdp->startspaces = 0; + bdp->endspaces = 0; + + if (lnum == start.lnum) { + startcol = start.col; + if (virtual_op) { + getvcol(curwin, &start, &cs, NULL, &ce); + if (ce != cs && start.coladd > 0) { + // Part of a tab selected -- but don't double-count it. + bdp->startspaces = (ce - cs + 1) - start.coladd; + if (bdp->startspaces < 0) { + bdp->startspaces = 0; + } + startcol++; + } + } + } + + if (lnum == end.lnum) { + endcol = end.col; + if (virtual_op) { + getvcol(curwin, &end, &cs, NULL, &ce); + if (p[endcol] == NUL || (cs + end.coladd < ce + // Don't add space for double-wide + // char; endcol will be on last byte + // of multi-byte char. + && utf_head_off(p, p + endcol) == 0)) { + if (start.lnum == end.lnum && start.col == end.col) { + // Special case: inside a single char + is_oneChar = true; + bdp->startspaces = end.coladd - start.coladd + inclusive; + endcol = startcol; + } else { + bdp->endspaces = end.coladd + inclusive; + endcol -= inclusive; + } + } + } + } + if (endcol == MAXCOL) { + endcol = (colnr_T)strlen(p); + } + if (startcol > endcol || is_oneChar) { + bdp->textlen = 0; + } else { + bdp->textlen = endcol - startcol + inclusive; + } + bdp->textstart = p + startcol; +} + /// Handle the add/subtract operator. /// /// @param[in] oap Arguments of operator. diff --git a/src/nvim/ops.h b/src/nvim/ops.h index a070db7a3b..1a708fab03 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -15,6 +15,25 @@ #include "nvim/pos_defs.h" #include "nvim/types_defs.h" +/// structure used by block_prep, op_delete and op_yank for blockwise operators +/// also op_change, op_shift, op_insert, op_replace - AKelly +struct block_def { + int startspaces; ///< 'extra' cols before first char + int endspaces; ///< 'extra' cols after last char + int textlen; ///< chars in block + char *textstart; ///< pointer to 1st char (partially) in block + colnr_T textcol; ///< index of chars (partially) in block + colnr_T start_vcol; ///< start col of 1st char wholly inside block + colnr_T end_vcol; ///< start col of 1st char wholly after block + int is_short; ///< true if line is too short to fit in block + int is_MAX; ///< true if curswant==MAXCOL when starting + int is_oneChar; ///< true if block within one character + int pre_whitesp; ///< screen cols of ws before block + int pre_whitesp_c; ///< chars of ws before block + colnr_T end_char_vcols; ///< number of vcols of post-block char + colnr_T start_char_vcols; ///< number of vcols of pre-block char +}; + typedef int (*Indenter)(void); /// flags for do_put() diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 10a0baa30b..d27fa375ee 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -1232,3 +1232,22 @@ describe('stdpath()', function() end) end) end) + +describe('autocommands', function() + it('closes terminal with default shell on success', function() + api.nvim_set_option_value('shell', helpers.testprg('shell-test'), {}) + command('set shellcmdflag=EXIT shellredir= shellpipe= shellquote= shellxquote=') + + -- Should not block other events + command('let g:n=0') + command('au BufEnter * let g:n = g:n + 1') + + command('terminal') + eq(eval('get(g:, "n", 0)'), 1) + + helpers.retry(nil, 1000, function() + neq(api.nvim_get_option_value('buftype', { buf = 0 }), 'terminal') + eq(eval('get(g:, "n", 0)'), 2) + end) + end) +end) diff --git a/test/old/testdir/test_visual.vim b/test/old/testdir/test_visual.vim index 262a3a4fd9..88f587a83a 100644 --- a/test/old/testdir/test_visual.vim +++ b/test/old/testdir/test_visual.vim @@ -1634,4 +1634,168 @@ func Test_visual_substitute_visual() bwipe! endfunc +func Test_visual_getregion() + new + + call setline(1, ['one', 'two', 'three']) + + " Visual mode + call cursor(1, 1) + call feedkeys("\<ESC>vjl", 'tx') + call assert_equal(['one', 'tw'], 'v'->getregion('.', 'v')) + call assert_equal(['one', 'tw'], '.'->getregion('v', 'v')) + call assert_equal(['o'], 'v'->getregion('v', 'v')) + call assert_equal(['w'], '.'->getregion('.', 'v')) + call assert_equal(['one', 'two'], '.'->getregion('v', 'V')) + call assert_equal(['on', 'tw'], '.'->getregion('v', "\<C-v>")) + + " Line visual mode + call cursor(1, 1) + call feedkeys("\<ESC>Vl", 'tx') + call assert_equal(['one'], getregion('v', '.', 'V')) + call assert_equal(['one'], getregion('.', 'v', 'V')) + call assert_equal(['one'], getregion('v', 'v', 'V')) + call assert_equal(['one'], getregion('.', '.', 'V')) + call assert_equal(['on'], '.'->getregion('v', 'v')) + call assert_equal(['on'], '.'->getregion('v', "\<C-v>")) + + " Block visual mode + call cursor(1, 1) + call feedkeys("\<ESC>\<C-v>ll", 'tx') + call assert_equal(['one'], getregion('v', '.', "\<C-v>")) + call assert_equal(['one'], getregion('.', 'v', "\<C-v>")) + call assert_equal(['o'], getregion('v', 'v', "\<C-v>")) + call assert_equal(['e'], getregion('.', '.', "\<C-v>")) + call assert_equal(['one'], '.'->getregion('v', 'V')) + call assert_equal(['one'], '.'->getregion('v', 'v')) + + " Using Marks + call setpos("'a", [0, 2, 3, 0]) + call cursor(1, 1) + call assert_equal(['one', 'two'], "'a"->getregion('.', 'v')) + call assert_equal(['one', 'two'], "."->getregion("'a", 'v')) + call assert_equal(['one', 'two'], "."->getregion("'a", 'V')) + call assert_equal(['two'], "'a"->getregion("'a", 'V')) + call assert_equal(['one', 'two'], "."->getregion("'a", "\<c-v>")) + + " Multiline with line visual mode + call cursor(1, 1) + call feedkeys("\<ESC>Vjj", 'tx') + call assert_equal(['one', 'two', 'three'], getregion('v', '.', 'V')) + + " Multiline with block visual mode + call cursor(1, 1) + call feedkeys("\<ESC>\<C-v>jj", 'tx') + call assert_equal(['o', 't', 't'], getregion('v', '.', "\<C-v>")) + + call cursor(1, 1) + call feedkeys("\<ESC>\<C-v>jj$", 'tx') + call assert_equal(['one', 'two', 'three'], getregion('v', '.', "\<C-v>")) + + " 'virtualedit' + set virtualedit=all + call cursor(1, 1) + call feedkeys("\<ESC>\<C-v>10ljj$", 'tx') + call assert_equal(['one ', 'two ', 'three '], + \ getregion('v', '.', "\<C-v>")) + set virtualedit& + + " Invalid position + call cursor(1, 1) + call feedkeys("\<ESC>vjj$", 'tx') + call assert_fails("call getregion(1, 2, 'v')", 'E1174:') + call assert_fails("call getregion('.', {}, 'v')", 'E1174:') + call assert_equal([], getregion('', '.', 'v')) + call assert_equal([], getregion('.', '.', '')) + call feedkeys("\<ESC>", 'tx') + call assert_equal([], getregion('v', '.', 'v')) + + " using an unset mark + call assert_equal([], "'z"->getregion(".", 'V')) + " using the wrong type + call assert_fails(':echo "."->getregion([],"V")', 'E1174:') + call assert_fails(':echo "."->getregion("$", {})', 'E1174:') + call assert_fails(':echo [0, 1, 1, 0]->getregion("$", "v")', 'E1174:') + " using a mark in another buffer + new + let newbuf = bufnr() + call setline(1, range(10)) + normal! GmA + wincmd p + call assert_equal([newbuf, 10, 1, 0], getpos("'A")) + call assert_equal([], getregion(".", "'A", 'v')) + call assert_equal([], getregion("'A", ".", 'v')) + exe newbuf .. 'bwipe!' + + bwipe! + " Selection in starts or ends in the middle of a multibyte character + new + call setline(1, [ + \ "abcdefghijk\u00ab", + \ "\U0001f1e6\u00ab\U0001f1e7\u00ab\U0001f1e8\u00ab\U0001f1e9", + \ "1234567890" + \ ]) + call cursor(1, 3) + call feedkeys("\<Esc>\<C-v>ljj", 'xt') + call assert_equal(['cd', "\u00ab ", '34'], + \ getregion('v', '.', "\<C-v>")) + call cursor(1, 4) + call feedkeys("\<Esc>\<C-v>ljj", 'xt') + call assert_equal(['de', "\U0001f1e7", '45'], + \ getregion('v', '.', "\<C-v>")) + call cursor(1, 5) + call feedkeys("\<Esc>\<C-v>jj", 'xt') + call assert_equal(['e', ' ', '5'], getregion('v', '.', "\<C-v>")) + call cursor(1, 1) + call feedkeys("\<Esc>vj", 'xt') + call assert_equal(['abcdefghijk«', "\U0001f1e6"], getregion('v', '.', "v")) + " marks on multibyte chars + set selection=exclusive + call setpos("'a", [0, 1, 11, 0]) + call setpos("'b", [0, 2, 16, 0]) + call setpos("'c", [0, 2, 0, 0]) + call cursor(1, 1) + call assert_equal(['ghijk', '🇨«🇩'], getregion("'a", "'b", "\<c-v>")) + call assert_equal(['k«', '🇦«🇧«🇨'], getregion("'a", "'b", "v")) + call assert_equal(['k«'], getregion("'a", "'c", "v")) + + bwipe! + + " Exclusive selection + new + set selection=exclusive + call setline(1, ["a\tc", "x\tz", '', '']) + call cursor(1, 1) + call feedkeys("\<Esc>v2l", 'xt') + call assert_equal(["a\t"], getregion('v', '.', 'v')) + call cursor(1, 1) + call feedkeys("\<Esc>v$G", 'xt') + call assert_equal(["a\tc", "x\tz", ''], getregion('v', '.', 'v')) + call cursor(1, 1) + call feedkeys("\<Esc>v$j", 'xt') + call assert_equal(["a\tc", "x\tz"], getregion('v', '.', 'v')) + call cursor(1, 1) + call feedkeys("\<Esc>\<C-v>$j", 'xt') + call assert_equal(["a\tc", "x\tz"], getregion('v', '.', "\<C-v>")) + call cursor(1, 1) + call feedkeys("\<Esc>\<C-v>$G", 'xt') + call assert_equal(["a", "x", '', ''], getregion('v', '.', "\<C-v>")) + call cursor(1, 1) + call feedkeys("\<Esc>wv2j", 'xt') + call assert_equal(["c", "x\tz"], getregion('v', '.', 'v')) + + " virtualedit + set virtualedit=all + call cursor(1, 1) + call feedkeys("\<Esc>2lv2lj", 'xt') + call assert_equal([' c', 'x '], getregion('v', '.', 'v')) + call cursor(1, 1) + call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt') + call assert_equal([' ', ' ', ' '], getregion('v', '.', "\<C-v>")) + set virtualedit& + set selection& + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab |