aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/api.txt24
-rw-r--r--runtime/doc/change.txt8
-rw-r--r--runtime/doc/index.txt2
-rw-r--r--runtime/doc/lsp.txt166
-rw-r--r--runtime/doc/lua.txt112
-rw-r--r--runtime/lua/vim/lsp/util.lua67
-rw-r--r--src/nvim/api/private/helpers.c31
-rw-r--r--src/nvim/api/vim.c7
-rw-r--r--src/nvim/api/window.c32
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/normal.c12
-rw-r--r--src/nvim/ops.c33
-rw-r--r--src/nvim/ops.h15
-rw-r--r--src/nvim/regexp.c6
-rw-r--r--src/nvim/testdir/test_blockedit.vim1
-rw-r--r--src/nvim/testdir/test_normal.vim38
-rw-r--r--src/nvim/testdir/test_search.vim20
-rw-r--r--src/nvim/testdir/test_visual.vim270
-rw-r--r--src/nvim/window.c39
-rw-r--r--test/functional/api/window_spec.lua14
20 files changed, 716 insertions, 185 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 0d85d6b539..fe69d9076f 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -993,7 +993,7 @@ nvim_get_option_info({name}) *nvim_get_option_info()*
Resulting dictionary has keys:
• name: Name of the option (like 'filetype')
• shortname: Shortened name of the option (like 'ft')
- • type: type of option ("string", "integer" or "boolean")
+ • type: type of option ("string", "number" or "boolean")
• default: The default value for the option
• was_set: Whether the option was set.
• last_set_sid: Last set script id (if any)
@@ -1299,6 +1299,17 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
an external top-level window. Currently
accepts no other positioning configuration
together with this.
+ • `zindex`: Stacking order. floats with higher`zindex`go on top on floats with lower indices. Must
+ be larger than zero. The following screen
+ elements have hard-coded z-indices:
+ • 100: insert completion popupmenu
+ • 200: message scrollback
+ • 250: cmdline completion popupmenu (when
+ wildoptions+=pum) The default value for
+ floats are 50. In general, values below 100
+ are recommended, unless there is a good
+ reason to overshadow builtin elements.
+
• `style`: Configure the appearance of the window.
Currently only takes one non-empty value:
• "minimal" Nvim will display the window with
@@ -1705,6 +1716,12 @@ nvim_set_hl({ns_id}, {name}, {val}) *nvim_set_hl()*
|nvim_get_hl_by_name|. in addition the following
keys are also recognized: `default` : don't
override existing definition, like `hi default`
+ `ctermfg` : sets foreground of cterm color
+ `ctermbg` : sets background of cterm color
+ `cterm` : cterm attribute map. sets attributed
+ for cterm colors. similer to `hi cterm` Note: by
+ default cterm attributes are same as attributes
+ of gui color
nvim_set_keymap({mode}, {lhs}, {rhs}, {opts}) *nvim_set_keymap()*
Sets a global |mapping| for the given mode.
@@ -2269,7 +2286,12 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
• "overlay": display over the specified
column, without shifting the underlying
text.
+ • "right_align": display right aligned in the
+ window.
+ • virt_text_win_col : position the virtual text
+ at a fixed window column (starting from the
+ first text column)
• virt_text_hide : hide the virtual text when
the background text is selected or hidden due
to horizontal scroll 'nowrap'
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index b2e910a834..924401be74 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -1091,6 +1091,11 @@ inside of strings can change! Also see 'softtabstop' option. >
Using the mouse only works when 'mouse' contains 'n'
or 'a'.
+["x]zp or *zp* *zP*
+["x]zP Like "p" and "P", except without adding trailing spaces
+ when pasting a block. Thus the inserted text will not
+ always be a rectangle.
+
You can use these commands to copy text from one place to another. Do this
by first getting the text into a register with a yank, delete or change
command, then inserting the register contents with a put command. You can
@@ -1130,6 +1135,9 @@ a register, a paste on a visual selected area will paste that single line on
each of the selected lines (thus replacing the blockwise selected region by a
block of the pasted line).
+Use |zP|/|zp| to paste a blockwise yanked register without appending trailing
+spaces.
+
*blockwise-register*
If you use a blockwise Visual mode command to get the text into the register,
the block of text will be inserted before ("P") or after ("p") the cursor
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index e7d891bc33..17258f896d 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -851,6 +851,8 @@ tag char note action in Normal mode ~
|zm| zm subtract one from 'foldlevel'
|zn| zn reset 'foldenable'
|zo| zo open fold
+|zp| zp paste in block-mode without trailing spaces
+|zP| zP paste in block-mode without trailing spaces
|zr| zr add one to 'foldlevel'
|zs| zs when 'wrap' off scroll horizontally to
position the cursor at the start (left
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 5c2ee568c5..d5e8db4dd7 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -619,15 +619,15 @@ buf_request_sync({bufnr}, {method}, {params}, {timeout_ms})
Calls |vim.lsp.buf_request_all()| but blocks Nvim while
awaiting the result. Parameters are the same as
|vim.lsp.buf_request()| but the return result is different.
- Wait maximum of {timeout_ms} (default 100) ms.
+ Wait maximum of {timeout_ms} (default 1000) ms.
Parameters: ~
{bufnr} (number) Buffer handle, or 0 for current.
{method} (string) LSP method name
{params} (optional, table) Parameters to send to the
server
- {timeout_ms} (optional, number, default=100) Maximum time
- in milliseconds to wait for a result.
+ {timeout_ms} (optional, number, default=1000) Maximum
+ time in milliseconds to wait for a result.
Return: ~
Map of client_id:request_result. On timeout, cancel or
@@ -651,6 +651,14 @@ client() *vim.lsp.client*
{status} is `true` , the function returns {request_id} as
the second result. You can use this with
`client.cancel_request(request_id)` to cancel the request.
+ • request_sync(method, params, timeout_ms, bufnr) Sends a
+ request to the server and synchronously waits for the
+ response. This is a wrapper around {client.request}
+ Returns: { err=err, result=result }, a dictionary, where
+ `err` and `result` come from the |lsp-handler|. On
+ timeout, cancel or error, returns `(nil, err)` where `err`
+ is a string describing the failure reason. If the request
+ was unsuccessful returns `nil` .
• notify(method, params) Sends a notification to an LSP
server. Returns: a boolean to indicate if the notification
was successful. If it is false, then it will always be
@@ -939,6 +947,9 @@ add_workspace_folder({workspace_folder})
not provided, the user will be prompted for a path using
|input()|.
+call_hierarchy({method}) *vim.lsp.buf.call_hierarchy()*
+ TODO: Documentation
+
clear_references() *vim.lsp.buf.clear_references()*
Removes document highlights from current buffer.
@@ -1017,6 +1028,32 @@ formatting({options}) *vim.lsp.buf.formatting()*
See also: ~
https://microsoft.github.io/language-server-protocol/specification#textDocument_formatting
+ *vim.lsp.buf.formatting_seq_sync()*
+formatting_seq_sync({options}, {timeout_ms}, {order})
+ Formats the current buffer by sequentially requesting
+ formatting from attached clients.
+
+ Useful when multiple clients with formatting capability are
+ attached.
+
+ Since it's synchronous, can be used for running on save, to
+ make sure buffer is formatted prior to being saved.
+ {timeout_ms} is passed on to the |vim.lsp.client| `request_sync` method. Example: >
+
+ vim.api.nvim_command[[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_seq_sync()]]
+<
+
+ Parameters: ~
+ {options} (optional, table) `FormattingOptions`
+ entries
+ {timeout_ms} (optional, number) Request timeout
+ {order} (optional, table) List of client names.
+ Formatting is requested from clients in the
+ following order: first all clients that are
+ not in the `order` list, then the remaining
+ clients in the order as they occur in the
+ `order` list.
+
*vim.lsp.buf.formatting_sync()*
formatting_sync({options}, {timeout_ms})
Performs |vim.lsp.buf.formatting()| synchronously.
@@ -1033,6 +1070,9 @@ formatting_sync({options}, {timeout_ms})
{options} Table with valid `FormattingOptions` entries
{timeout_ms} (number) Request timeout
+ See also: ~
+ |vim.lsp.buf.formatting_seq_sync|
+
hover() *vim.lsp.buf.hover()*
Displays hover information about the symbol under the cursor
in a floating window. Calling the function twice will jump
@@ -1585,6 +1625,9 @@ buf_highlight_references({bufnr}, {references})
{references} List of `DocumentHighlight` objects to
highlight
+buf_lines({bufnr}) *vim.lsp.util.buf_lines()*
+ TODO: Documentation
+
character_offset({buf}, {row}, {col}) *vim.lsp.util.character_offset()*
Returns the UTF-32 and UTF-16 offsets for a position in a
certain buffer.
@@ -1650,12 +1693,15 @@ convert_input_to_markdown_lines({input}, {contents})
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover
*vim.lsp.util.convert_signature_help_to_markdown_lines()*
-convert_signature_help_to_markdown_lines({signature_help})
+convert_signature_help_to_markdown_lines({signature_help}, {ft})
Converts `textDocument/SignatureHelp` response to markdown
lines.
Parameters: ~
{signature_help} Response of `textDocument/SignatureHelp`
+ {ft} optional filetype that will be use as
+ the `lang` for the label markdown code
+ block
Return: ~
list of lines of converted markdown.
@@ -1683,65 +1729,6 @@ extract_completion_items({result})
See also: ~
https://microsoft.github.io/language-server-protocol/specification#textDocument_completion
- *vim.lsp.util.fancy_floating_markdown()*
-fancy_floating_markdown({contents}, {opts})
- Converts markdown into syntax highlighted regions by stripping
- the code blocks and converting them into highlighted code.
- This will by default insert a blank line separator after those
- code block regions to improve readability. The result is shown
- in a floating preview.
-
- Parameters: ~
- {contents} table of lines to show in window
- {opts} dictionary with optional fields
- • height of floating window
- • width of floating window
- • wrap_at character to wrap at for computing
- height
- • max_width maximal width of floating window
- • max_height maximal height of floating window
- • pad_left number of columns to pad contents
- at left
- • pad_right number of columns to pad contents
- at right
- • pad_top number of lines to pad contents at
- top
- • pad_bottom number of lines to pad contents
- at bottom
- • separator insert separator after code block
-
- Return: ~
- width,height size of float
-
-focusable_float({unique_name}, {fn}) *vim.lsp.util.focusable_float()*
- Parameters: ~
- {unique_name} (string) Window variable
- {fn} (function) should return create a new
- window and return a tuple of
- ({focusable_buffer_id}, {window_id}). if
- {focusable_buffer_id} is a valid buffer id,
- the newly created window will be the new
- focus associated with the current buffer
- via the tag `unique_name` .
-
- Return: ~
- (pbufnr, pwinnr) if `fn()` has created a new window; nil
- otherwise
-
- *vim.lsp.util.focusable_preview()*
-focusable_preview({unique_name}, {fn})
- Focuses/unfocuses the floating preview window associated with
- the current buffer via the window variable `unique_name` . If
- no such preview window exists, makes a new one.
-
- Parameters: ~
- {unique_name} (string) Window variable
- {fn} (function) The return values of this
- function will be passed directly to
- |vim.lsp.util.open_floating_preview()|, in
- the case that a new floating window should
- be created
-
get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
Returns visual width of tabstop.
@@ -1755,6 +1742,22 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()*
See also: ~
|softtabstop|
+get_line({uri}, {row}) *vim.lsp.util.get_line()*
+ Parameters: ~
+ {uri} string uri of the resource to get the line from
+ {row} number zero-indexed line number
+
+ Return: ~
+ string the line at row in filename
+
+get_lines({uri}, {rows}) *vim.lsp.util.get_lines()*
+ Parameters: ~
+ {uri} string uri of the resource to get the lines from
+ {rows} number[] zero-indexed line numbers
+
+ Return: ~
+ table<number string> a table mapping rows to lines
+
get_progress_messages() *vim.lsp.util.get_progress_messages()*
TODO: Documentation
@@ -1897,7 +1900,7 @@ parse_snippet({input}) *vim.lsp.util.parse_snippet()*
Return: ~
(string) parsed snippet
-preview_location({location}) *vim.lsp.util.preview_location()*
+preview_location({location}, {opts}) *vim.lsp.util.preview_location()*
Previews a location in a floating window
behavior depends on type of location:
@@ -1946,6 +1949,41 @@ set_qflist({items}) *vim.lsp.util.set_qflist()*
Parameters: ~
{items} (table) list of items
+ *vim.lsp.util.stylize_markdown()*
+stylize_markdown({bufnr}, {contents}, {opts})
+ Converts markdown into syntax highlighted regions by stripping
+ the code blocks and converting them into highlighted code.
+ This will by default insert a blank line separator after those
+ code block regions to improve readability.
+
+ This method configures the given buffer and returns the lines
+ to set.
+
+ If you want to open a popup with fancy markdown, use
+ `open_floating_preview` instead
+
+ Parameters: ~
+ {contents} table of lines to show in window
+ {opts} dictionary with optional fields
+ • height of floating window
+ • width of floating window
+ • wrap_at character to wrap at for computing
+ height
+ • max_width maximal width of floating window
+ • max_height maximal height of floating window
+ • pad_left number of columns to pad contents
+ at left
+ • pad_right number of columns to pad contents
+ at right
+ • pad_top number of lines to pad contents at
+ top
+ • pad_bottom number of lines to pad contents
+ at bottom
+ • separator insert separator after code block
+
+ Return: ~
+ width,height size of float
+
symbols_to_items({symbols}, {bufnr}) *vim.lsp.util.symbols_to_items()*
Converts symbols to quickfix list items.
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index c3893d05c0..05404a2e35 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -650,42 +650,6 @@ vim.empty_dict() *vim.empty_dict()*
Note: if numeric keys are added to the table, the metatable will be
ignored and the dict converted to a list/array anyway.
-vim.region({bufnr}, {pos1}, {pos2}, {type}, {inclusive}) *vim.region()*
- Converts a selection specified by the buffer ({bufnr}), starting
- position ({pos1}, a zero-indexed pair `{line1,column1}`), ending
- position ({pos2}, same format as {pos1}), the type of the register
- for the selection ({type}, see |regtype|), and a boolean indicating
- whether the selection is inclusive or not, into a zero-indexed table
- of linewise selections of the form `{linenr = {startcol, endcol}}` .
-
- *vim.register_keystroke_callback()*
-vim.register_keystroke_callback({fn}, {ns_id})
- Register a lua {fn} with an {ns_id} to be run after every keystroke.
-
- Parameters: ~
- {fn}: (function): Function to call on keystroke.
- It should take one argument, which is a string.
- The string will contain the literal keys typed.
- See |i_CTRL-V|
-
- If {fn} is `nil`, it removes the callback for the
- associated {ns_id}.
-
- {ns_id}: (number) Namespace ID. If not passed or 0, will generate
- and return a new namespace ID from |nvim_create_namespace()|
-
- Return: ~
- (number) Namespace ID associated with {fn}
-
- NOTE: {fn} will be automatically removed if an error occurs while
- calling. This is to prevent the annoying situation of every keystroke
- erroring while trying to remove a broken callback.
-
- NOTE: {fn} will receive the keystrokes after mappings have been
- evaluated
-
- NOTE: {fn} will *NOT* be cleared from |nvim_buf_clear_namespace()|
-
vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels.
@@ -1118,6 +1082,23 @@ vim.wo *vim.wo*
==============================================================================
Lua module: vim *lua-vim*
+defer_fn({fn}, {timeout}) *vim.defer_fn()*
+ Defers calling `fn` until `timeout` ms passes.
+
+ Use to do a one-shot timer that calls `fn` Note: The {fn} is |schedule_wrap|ped automatically, so API
+ functions are safe to call.
+
+ Parameters: ~
+ {fn} Callback to call once `timeout` expires
+ {timeout} Number of milliseconds to wait before calling
+ `fn`
+
+ Return: ~
+ timer luv timer object
+
+insert_keys({obj}) *vim.insert_keys()*
+ TODO: Documentation
+
inspect({object}, {options}) *vim.inspect()*
Return a human-readable representation of the given object.
@@ -1125,9 +1106,19 @@ inspect({object}, {options}) *vim.inspect()*
https://github.com/kikito/inspect.lua
https://github.com/mpeterv/vinspect
-make_meta_accessor({get}, {set}, {del}) *vim.make_meta_accessor()*
+make_dict_accessor({scope}) *vim.make_dict_accessor()*
TODO: Documentation
+notify({msg}, {log_level}, {_opts}) *vim.notify()*
+ Notification provider without a runtime, writes to :Messages
+
+ Parameters: ~
+ {msg} Content of the notification to show to the
+ user
+ {log_level} Optional log level
+ {opts} Dictionary with optional options (timeout,
+ etc)
+
paste({lines}, {phase}) *vim.paste()*
Paste handler, invoked by |nvim_paste()| when a conforming UI
(such as the |TUI|) pastes text into the editor.
@@ -1160,6 +1151,53 @@ paste({lines}, {phase}) *vim.paste()*
See also: ~
|paste|
+region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()*
+ Get a table of lines with start, end columns for a region
+ marked by two points
+
+ Parameters: ~
+ {bufnr} number of buffer
+ {pos1} (line, column) tuple marking beginning of
+ region
+ {pos2} (line, column) tuple marking end of region
+ {regtype} type of selection (:help setreg)
+ {inclusive} boolean indicating whether the selection is
+ end-inclusive
+
+ Return: ~
+ region lua table of the form {linenr = {startcol,endcol}}
+
+ *vim.register_keystroke_callback()*
+register_keystroke_callback({fn}, {ns_id})
+ Register a lua {fn} with an {id} to be run after every
+ keystroke.
+
+ If {fn} is nil, it removes the callback for the associated
+ {ns_id}
+ Note:
+ {fn} will not be cleared from |nvim_buf_clear_namespace()|
+
+ Note:
+ {fn} will receive the keystrokes after mappings have been
+ evaluated
+
+ Parameters: ~
+ {fn} function: Function to call. It should take one
+ argument, which is a string. The string will contain
+ the literal keys typed. See |i_CTRL-V|
+ {ns_id} number? Namespace ID. If not passed or 0, will
+ generate and return a new namespace ID from
+ |nvim_create_namesapce()|
+
+ Return: ~
+ number Namespace ID associated with {fn}
+
+ Note:
+ {fn} will be automatically removed if an error occurs
+ while calling. This is to prevent the annoying situation
+ of every keystroke erroring while trying to remove a
+ broken callback.
+
schedule_wrap({cb}) *vim.schedule_wrap()*
Defers callback `cb` until the Nvim API is safe to call.
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index a7b43cfa6b..0fa6518c0b 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1462,12 +1462,33 @@ end)
--@param row number zero-indexed line number
--@return string the line at row in filename
function M.get_line(uri, row)
+ return M.get_lines(uri, { row })[row]
+end
+
+-- Gets the zero-indexed lines from the given uri.
+-- For non-file uris, we load the buffer and get the lines.
+-- If a loaded buffer exists, then that is used.
+-- Otherwise we get the lines using libuv which is a lot faster than loading the buffer.
+--@param uri string uri of the resource to get the lines from
+--@param rows number[] zero-indexed line numbers
+--@return table<number string> a table mapping rows to lines
+function M.get_lines(uri, rows)
+ rows = type(rows) == "table" and rows or { rows }
+
+ local function buf_lines(bufnr)
+ local lines = {}
+ for _, row in pairs(rows) do
+ lines[row] = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
+ end
+ return lines
+ end
+
-- load the buffer if this is not a file uri
-- Custom language server protocol extensions can result in servers sending URIs with custom schemes. Plugins are able to load these via `BufReadCmd` autocmds.
if uri:sub(1, 4) ~= "file" then
local bufnr = vim.uri_to_bufnr(uri)
vim.fn.bufload(bufnr)
- return (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
+ return buf_lines(bufnr)
end
local filename = vim.uri_to_fname(uri)
@@ -1475,22 +1496,44 @@ function M.get_line(uri, row)
-- use loaded buffers if available
if vim.fn.bufloaded(filename) == 1 then
local bufnr = vim.fn.bufnr(filename, false)
- return (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
+ return buf_lines(bufnr)
end
+ -- get the data from the file
local fd = uv.fs_open(filename, "r", 438)
- -- TODO: what should we do in this case?
if not fd then return "" end
local stat = uv.fs_fstat(fd)
local data = uv.fs_read(fd, stat.size, 0)
uv.fs_close(fd)
+ local lines = {} -- rows we need to retrieve
+ local need = 0 -- keep track of how many unique rows we need
+ for _, row in pairs(rows) do
+ if not lines[row] then
+ need = need + 1
+ end
+ lines[row] = true
+ end
+
+ local found = 0
local lnum = 0
+
for line in string.gmatch(data, "([^\n]*)\n?") do
- if lnum == row then return line end
+ if lines[lnum] == true then
+ lines[lnum] = line
+ found = found + 1
+ if found == need then break end
+ end
lnum = lnum + 1
end
- return ""
+
+ -- change any lines we didn't find to the empty string
+ for i, line in pairs(lines) do
+ if line == true then
+ lines[i] = ""
+ end
+ end
+ return lines
end
--- Returns the items with the byte position calculated correctly and in sorted
@@ -1522,10 +1565,22 @@ function M.locations_to_items(locations)
local rows = grouped[uri]
table.sort(rows, position_sort)
local filename = vim.uri_to_fname(uri)
+
+ -- list of row numbers
+ local uri_rows = {}
+ for _, temp in ipairs(rows) do
+ local pos = temp.start
+ local row = pos.line
+ table.insert(uri_rows, row)
+ end
+
+ -- get all the lines for this uri
+ local lines = M.get_lines(uri, uri_rows)
+
for _, temp in ipairs(rows) do
local pos = temp.start
local row = pos.line
- local line = M.get_line(uri, row)
+ local line = lines[row] or ""
local col = pos.character
table.insert(items, {
filename = filename,
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 5abdc33709..4b1c2d4baa 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1645,7 +1645,7 @@ bool api_object_to_bool(Object obj, const char *what,
} else if (obj.type == kObjectTypeNil) {
return nil_value; // caller decides what NIL (missing retval in lua) means
} else {
- api_set_error(err, kErrorTypeValidation, "%s is not an boolean", what);
+ api_set_error(err, kErrorTypeValidation, "%s is not a boolean", what);
return false;
}
}
@@ -1868,7 +1868,7 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err)
}
bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
- Error *err)
+ bool new_win, Error *err)
{
// TODO(bfredl): use a get/has_key interface instead and get rid of extra
// flags
@@ -1968,24 +1968,15 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
}
has_bufpos = true;
} else if (!strcmp(key, "external")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->external = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->external = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'external' key must be Boolean");
+ has_external = fconfig->external
+ = api_object_to_bool(val, "'external' key", false, err);
+ if (ERROR_SET(err)) {
return false;
}
- has_external = fconfig->external;
} else if (!strcmp(key, "focusable")) {
- if (val.type == kObjectTypeInteger) {
- fconfig->focusable = val.data.integer;
- } else if (val.type == kObjectTypeBoolean) {
- fconfig->focusable = val.data.boolean;
- } else {
- api_set_error(err, kErrorTypeValidation,
- "'focusable' key must be Boolean");
+ fconfig->focusable
+ = api_object_to_bool(val, "'focusable' key", true, err);
+ if (ERROR_SET(err)) {
return false;
}
} else if (strequal(key, "zindex")) {
@@ -2015,6 +2006,12 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf,
api_set_error(err, kErrorTypeValidation,
"Invalid value of 'style' key");
}
+ } else if (strequal(key, "noautocmd") && new_win) {
+ fconfig->noautocmd
+ = api_object_to_bool(val, "'noautocmd' key", false, err);
+ if (ERROR_SET(err)) {
+ return false;
+ }
} else {
api_set_error(err, kErrorTypeValidation,
"Invalid key '%s'", key);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index a76cefe294..99a41f4f6f 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -1459,6 +1459,9 @@ void nvim_chan_send(Integer chan, String data, Error *err)
/// By default `FloatBorder` highlight is used which links to `VertSplit`
/// when not defined. It could also be specified by character:
/// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]
+/// - `noautocmd`: If true then no buffer-related autocommand events such as
+/// |BufEnter|, |BufLeave| or |BufWinEnter| may fire from
+/// calling this function.
///
/// @param[out] err Error details, if any
///
@@ -1469,7 +1472,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
FUNC_API_CHECK_TEXTLOCK
{
FloatConfig fconfig = FLOAT_CONFIG_INIT;
- if (!parse_float_config(config, &fconfig, false, err)) {
+ if (!parse_float_config(config, &fconfig, false, true, err)) {
return 0;
}
win_T *wp = win_new_float(NULL, fconfig, err);
@@ -1484,7 +1487,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config,
return 0;
}
if (buffer > 0) {
- nvim_win_set_buf(wp->handle, buffer, err);
+ win_set_buf(wp->handle, buffer, fconfig.noautocmd, err);
}
if (fconfig.style == kWinStyleMinimal) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 158e149628..a26213af98 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -46,35 +46,7 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
FUNC_API_SINCE(5)
FUNC_API_CHECK_TEXTLOCK
{
- win_T *win = find_window_by_handle(window, err), *save_curwin = curwin;
- buf_T *buf = find_buffer_by_handle(buffer, err);
- tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab;
-
- if (!win || !buf) {
- return;
- }
-
- if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- window);
- }
-
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to set buffer %d",
- buffer);
- }
-
- // If window is not current, state logic will not validate its cursor.
- // So do it now.
- validate_cursor();
-
- restore_win_noblock(save_curwin, save_curtab, false);
+ win_set_buf(window, buffer, false, err);
}
/// Gets the (1,0)-indexed cursor position in the window. |api-indexing|
@@ -423,7 +395,7 @@ void nvim_win_set_config(Window window, Dictionary config, Error *err)
// reuse old values, if not overriden
FloatConfig fconfig = new_float ? FLOAT_CONFIG_INIT : win->w_float_config;
- if (!parse_float_config(config, &fconfig, !new_float, err)) {
+ if (!parse_float_config(config, &fconfig, !new_float, false, err)) {
return;
}
if (new_float) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 0c839ba12a..e3e538bd12 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1090,6 +1090,7 @@ typedef struct {
schar_T border_chars[8];
int border_hl_ids[8];
int border_attr[8];
+ bool noautocmd;
} FloatConfig;
#define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \
@@ -1098,7 +1099,8 @@ typedef struct {
.relative = 0, .external = false, \
.focusable = true, \
.zindex = kZIndexFloatDefault, \
- .style = kWinStyleUnused })
+ .style = kWinStyleUnused, \
+ .noautocmd = false })
// Structure to store last cursor position and topline. Used by check_lnums()
// and reset_lnums().
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 13706fb14a..173d8d46d1 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -4384,6 +4384,12 @@ dozet:
}
break;
+ // "zp", "zP" in block mode put without addind trailing spaces
+ case 'P':
+ case 'p':
+ nv_put(cap);
+ break;
+
/* "zF": create fold command */
/* "zf": create fold operator */
case 'F':
@@ -7913,12 +7919,14 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
flags |= PUT_FIXINDENT;
} else {
dir = (cap->cmdchar == 'P'
- || (cap->cmdchar == 'g' && cap->nchar == 'P'))
- ? BACKWARD : FORWARD;
+ || ((cap->cmdchar == 'g' || cap->cmdchar == 'z')
+ && cap->nchar == 'P')) ? BACKWARD : FORWARD;
}
prep_redo_cmd(cap);
if (cap->cmdchar == 'g') {
flags |= PUT_CURSEND;
+ } else if (cap->cmdchar == 'z') {
+ flags |= PUT_BLOCK_INNER;
}
if (VIsual_active) {
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 0ed116c17f..2c8c7f0567 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2788,13 +2788,13 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
recursive = false;
}
-/*
- * Put contents of register "regname" into the text.
- * Caller must check "regname" to be valid!
- * "flags": PUT_FIXINDENT make indent look nice
- * PUT_CURSEND leave cursor after end of new text
- * PUT_LINE force linewise put (":put")
- dir: BACKWARD for 'P', FORWARD for 'p' */
+// Put contents of register "regname" into the text.
+// Caller must check "regname" to be valid!
+// "flags": PUT_FIXINDENT make indent look nice
+// PUT_CURSEND leave cursor after end of new text
+// PUT_LINE force linewise put (":put")
+// PUT_BLOCK_INNER in block mode, do not add trailing spaces
+// dir: BACKWARD for 'P', FORWARD for 'p'
void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
{
char_u *ptr;
@@ -3126,7 +3126,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
curwin->w_cursor.coladd = 0;
bd.textcol = 0;
for (i = 0; i < y_size; i++) {
- int spaces;
+ int spaces = 0;
char shortline;
// can just be 0 or 1, needed for blockwise paste beyond the current
// buffer end
@@ -3177,13 +3177,16 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
yanklen = (int)STRLEN(y_array[i]);
- // calculate number of spaces required to fill right side of block
- spaces = y_width + 1;
- for (long j = 0; j < yanklen; j++) {
- spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0);
- }
- if (spaces < 0) {
- spaces = 0;
+ if ((flags & PUT_BLOCK_INNER) == 0) {
+ // calculate number of spaces required to fill right side of
+ // block
+ spaces = y_width + 1;
+ for (int j = 0; j < yanklen; j++) {
+ spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0);
+ }
+ if (spaces < 0) {
+ spaces = 0;
+ }
}
// insert the new text
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 77d6b4435f..112ffbeaba 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -14,13 +14,14 @@
typedef int (*Indenter)(void);
-/* flags for do_put() */
-#define PUT_FIXINDENT 1 /* make indent look nice */
-#define PUT_CURSEND 2 /* leave cursor after end of new text */
-#define PUT_CURSLINE 4 /* leave cursor on last line of new text */
-#define PUT_LINE 8 /* put register as lines */
-#define PUT_LINE_SPLIT 16 /* split line for linewise register */
-#define PUT_LINE_FORWARD 32 /* put linewise register below Visual sel. */
+// flags for do_put()
+#define PUT_FIXINDENT 1 // make indent look nice
+#define PUT_CURSEND 2 // leave cursor after end of new text
+#define PUT_CURSLINE 4 // leave cursor on last line of new text
+#define PUT_LINE 8 // put register as lines
+#define PUT_LINE_SPLIT 16 // split line for linewise register
+#define PUT_LINE_FORWARD 32 // put linewise register below Visual sel.
+#define PUT_BLOCK_INNER 64 // in block mode, do not add trailing spaces
/*
* Registers:
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index accf9b0bb5..6b84bb3207 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -3755,6 +3755,7 @@ static bool reg_match_visual(void)
int mode;
colnr_T start, end;
colnr_T start2, end2;
+ colnr_T curswant;
// Check if the buffer is the current buffer.
if (rex.reg_buf != curbuf || VIsual.lnum == 0) {
@@ -3770,6 +3771,7 @@ static bool reg_match_visual(void)
bot = VIsual;
}
mode = VIsual_mode;
+ curswant = wp->w_curswant;
} else {
if (lt(curbuf->b_visual.vi_start, curbuf->b_visual.vi_end)) {
top = curbuf->b_visual.vi_start;
@@ -3779,6 +3781,7 @@ static bool reg_match_visual(void)
bot = curbuf->b_visual.vi_start;
}
mode = curbuf->b_visual.vi_mode;
+ curswant = curbuf->b_visual.vi_curswant;
}
lnum = rex.lnum + rex.reg_firstlnum;
if (lnum < top.lnum || lnum > bot.lnum) {
@@ -3798,8 +3801,9 @@ static bool reg_match_visual(void)
start = start2;
if (end2 > end)
end = end2;
- if (top.col == MAXCOL || bot.col == MAXCOL)
+ if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL) {
end = MAXCOL;
+ }
unsigned int cols_u = win_linetabsize(wp, rex.line,
(colnr_T)(rex.input - rex.line));
assert(cols_u <= MAXCOL);
diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim
index 527224ccd2..180524cd73 100644
--- a/src/nvim/testdir/test_blockedit.vim
+++ b/src/nvim/testdir/test_blockedit.vim
@@ -1,6 +1,5 @@
" Test for block inserting
"
-" TODO: rewrite test39.in into this new style test
func Test_blockinsert_indent()
new
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 7a846e5ea0..4a00999c45 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -512,6 +512,12 @@ func Test_normal14_page_eol()
bw!
endfunc
+" Test for errors with z command
+func Test_normal_z_error()
+ call assert_beeps('normal! z2p')
+ call assert_beeps('normal! zq')
+endfunc
+
func Test_normal15_z_scroll_vert()
" basic test for z commands that scroll the window
call Setup_NewWindow()
@@ -2877,3 +2883,35 @@ func Test_normal_gk()
bw!
set cpoptions& number& numberwidth&
endfunc
+
+" Some commands like yy, cc, dd, >>, << and !! accept a count after
+" typing the first letter of the command.
+func Test_normal_count_after_operator()
+ new
+ setlocal shiftwidth=4 tabstop=8 autoindent
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ let @a = ''
+ normal! j"ay4y
+ call assert_equal("two\nthree\nfour\nfive\n", @a)
+ normal! 3G>2>
+ call assert_equal(['one', 'two', ' three', ' four', 'five'],
+ \ getline(1, '$'))
+ exe "normal! 3G0c2cred\nblue"
+ call assert_equal(['one', 'two', ' red', ' blue', 'five'],
+ \ getline(1, '$'))
+ exe "normal! gg<8<"
+ call assert_equal(['one', 'two', 'red', 'blue', 'five'],
+ \ getline(1, '$'))
+ exe "normal! ggd3d"
+ call assert_equal(['blue', 'five'], getline(1, '$'))
+ call setline(1, range(1, 4))
+ call feedkeys("gg!3!\<C-B>\"\<CR>", 'xt')
+ call assert_equal('".,.+2!', @:)
+ call feedkeys("gg!1!\<C-B>\"\<CR>", 'xt')
+ call assert_equal('".!', @:)
+ call feedkeys("gg!9!\<C-B>\"\<CR>", 'xt')
+ call assert_equal('".,$!', @:)
+ bw!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index b391663e0f..5ba24d047c 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -824,6 +824,26 @@ func Test_incsearch_search_dump()
call delete('Xis_search_script')
endfunc
+func Test_hlsearch_block_visual_match()
+ CheckScreendump
+
+ let lines =<< trim END
+ set hlsearch
+ call setline(1, ['aa', 'bbbb', 'cccccc'])
+ END
+ call writefile(lines, 'Xhlsearch_block')
+ let buf = RunVimInTerminal('-S Xhlsearch_block', {'rows': 9, 'cols': 60})
+
+ call term_sendkeys(buf, "G\<C-V>$kk\<Esc>")
+ sleep 100m
+ call term_sendkeys(buf, "/\\%V\<CR>")
+ sleep 100m
+ call VerifyScreenDump(buf, 'Test_hlsearch_block_visual_match', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xhlsearch_block')
+endfunc
+
func Test_incsearch_substitute()
CheckFunction test_override
CheckOption incsearch
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index 73c7960579..21fd57b791 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1,4 +1,4 @@
-" Tests for various Visual mode.
+" Tests for various Visual modes.
func Test_block_shift_multibyte()
" Uses double-wide character.
@@ -738,4 +738,272 @@ func Test_select_mode_gv()
bwipe!
endfunc
+" Tests for the visual block mode commands
+func Test_visual_block_mode()
+ new
+ call append(0, '')
+ call setline(1, ['abcdefghijklm', 'abcdefghijklm', 'abcdefghijklm',
+ \ 'abcdefghijklm', 'abcdefghijklm'])
+ call cursor(1, 1)
+
+ " Test shift-right of a block
+ exe "normal jllll\<C-V>jj>wll\<C-V>jlll>"
+ " Test shift-left of a block
+ exe "normal G$hhhh\<C-V>kk<"
+ " Test block-insert
+ exe "normal Gkl\<C-V>kkkIxyz"
+ " Test block-replace
+ exe "normal Gllll\<C-V>kkklllrq"
+ " Test block-change
+ exe "normal G$khhh\<C-V>hhkkcmno"
+ call assert_equal(['axyzbcdefghijklm',
+ \ 'axyzqqqq mno ghijklm',
+ \ 'axyzqqqqef mno ghijklm',
+ \ 'axyzqqqqefgmnoklm',
+ \ 'abcdqqqqijklm'], getline(1, 5))
+
+ " Test from ':help v_b_I_example'
+ %d _
+ setlocal tabstop=8 shiftwidth=4
+ let lines =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call setline(1, lines)
+ exe "normal ggfo\<C-V>3jISTRING"
+ let expected =<< trim END
+ abcdefghijklmnSTRINGopqrstuvwxyz
+ abc STRING defghijklmnopqrstuvwxyz
+ abcdef ghi STRING jklmnopqrstuvwxyz
+ abcdefghijklmnSTRINGopqrstuvwxyz
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Test from ':help v_b_A_example'
+ %d _
+ let lines =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call setline(1, lines)
+ exe "normal ggfo\<C-V>3j$ASTRING"
+ let expected =<< trim END
+ abcdefghijklmnopqrstuvwxyzSTRING
+ abc defghijklmnopqrstuvwxyzSTRING
+ abcdef ghi jklmnopqrstuvwxyzSTRING
+ abcdefghijklmnopqrstuvwxyzSTRING
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Test from ':help v_b_<_example'
+ %d _
+ let lines =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call setline(1, lines)
+ exe "normal ggfo\<C-V>3j3l<.."
+ let expected =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Test from ':help v_b_>_example'
+ %d _
+ let lines =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call setline(1, lines)
+ exe "normal ggfo\<C-V>3j>.."
+ let expected =<< trim END
+ abcdefghijklmn opqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmn opqrstuvwxyz
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ " Test from ':help v_b_r_example'
+ %d _
+ let lines =<< trim END
+ abcdefghijklmnopqrstuvwxyz
+ abc defghijklmnopqrstuvwxyz
+ abcdef ghi jklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ END
+ call setline(1, lines)
+ exe "normal ggfo\<C-V>5l3jrX"
+ let expected =<< trim END
+ abcdefghijklmnXXXXXXuvwxyz
+ abc XXXXXXhijklmnopqrstuvwxyz
+ abcdef ghi XXXXXX jklmnopqrstuvwxyz
+ abcdefghijklmnXXXXXXuvwxyz
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ bwipe!
+ set tabstop& shiftwidth&
+endfunc
+
+" Test block-insert using cursor keys for movement
+func Test_visual_block_insert_cursor_keys()
+ new
+ call append(0, ['aaaaaa', 'bbbbbb', 'cccccc', 'dddddd'])
+ call cursor(1, 1)
+
+ exe "norm! l\<C-V>jjjlllI\<Right>\<Right> \<Esc>"
+ call assert_equal(['aaa aaa', 'bbb bbb', 'ccc ccc', 'ddd ddd'],
+ \ getline(1, 4))
+
+ call deletebufline('', 1, '$')
+ call setline(1, ['xaaa', 'bbbb', 'cccc', 'dddd'])
+ call cursor(1, 1)
+ exe "norm! \<C-V>jjjI<>\<Left>p\<Esc>"
+ call assert_equal(['<p>xaaa', '<p>bbbb', '<p>cccc', '<p>dddd'],
+ \ getline(1, 4))
+ bwipe!
+endfunc
+
+func Test_visual_block_create()
+ new
+ call append(0, '')
+ " Test for Visual block was created with the last <C-v>$
+ call setline(1, ['A23', '4567'])
+ call cursor(1, 1)
+ exe "norm! l\<C-V>j$Aab\<Esc>"
+ call assert_equal(['A23ab', '4567ab'], getline(1, 2))
+
+ " Test for Visual block was created with the middle <C-v>$ (1)
+ call deletebufline('', 1, '$')
+ call setline(1, ['B23', '4567'])
+ call cursor(1, 1)
+ exe "norm! l\<C-V>j$hAab\<Esc>"
+ call assert_equal(['B23 ab', '4567ab'], getline(1, 2))
+
+ " Test for Visual block was created with the middle <C-v>$ (2)
+ call deletebufline('', 1, '$')
+ call setline(1, ['C23', '4567'])
+ call cursor(1, 1)
+ exe "norm! l\<C-V>j$hhAab\<Esc>"
+ call assert_equal(['C23ab', '456ab7'], getline(1, 2))
+ bwipe!
+endfunc
+
+" Test for Visual block insert when virtualedit=all
+func Test_virtualedit_visual_block()
+ set ve=all
+ new
+ call append(0, ["\t\tline1", "\t\tline2", "\t\tline3"])
+ call cursor(1, 1)
+ exe "norm! 07l\<C-V>jjIx\<Esc>"
+ call assert_equal([" x \tline1",
+ \ " x \tline2",
+ \ " x \tline3"], getline(1, 3))
+
+ " Test for Visual block append when virtualedit=all
+ exe "norm! 012l\<C-v>jjAx\<Esc>"
+ call assert_equal([' x x line1',
+ \ ' x x line2',
+ \ ' x x line3'], getline(1, 3))
+ set ve=
+ bwipe!
+endfunc
+
+" Test for changing case
+func Test_visual_change_case()
+ new
+ " gUe must uppercase a whole word, also when ß changes to SS
+ exe "normal Gothe youtußeuu end\<Esc>Ypk0wgUe\r"
+ " gUfx must uppercase until x, inclusive.
+ exe "normal O- youßtußexu -\<Esc>0fogUfx\r"
+ " VU must uppercase a whole line
+ exe "normal YpkVU\r"
+ " same, when it's the last line in the buffer
+ exe "normal YPGi111\<Esc>VUddP\r"
+ " Uppercase two lines
+ exe "normal Oblah di\rdoh dut\<Esc>VkUj\r"
+ " Uppercase part of two lines
+ exe "normal ddppi333\<Esc>k0i222\<Esc>fyllvjfuUk"
+ call assert_equal(['the YOUTUSSEUU end', '- yOUSSTUSSEXu -',
+ \ 'THE YOUTUSSEUU END', '111THE YOUTUSSEUU END', 'BLAH DI', 'DOH DUT',
+ \ '222the yoUTUSSEUU END', '333THE YOUTUßeuu end'], getline(2, '$'))
+ bwipe!
+endfunc
+
+" Test for Visual replace using Enter or NL
+func Test_visual_replace_crnl()
+ new
+ exe "normal G3o123456789\e2k05l\<C-V>2jr\r"
+ exe "normal G3o98765\e2k02l\<C-V>2jr\<C-V>\r\n"
+ exe "normal G3o123456789\e2k05l\<C-V>2jr\n"
+ exe "normal G3o98765\e2k02l\<C-V>2jr\<C-V>\n"
+ call assert_equal(['12345', '789', '12345', '789', '12345', '789', "98\r65",
+ \ "98\r65", "98\r65", '12345', '789', '12345', '789', '12345', '789',
+ \ "98\n65", "98\n65", "98\n65"], getline(2, '$'))
+ bwipe!
+endfunc
+
+func Test_ve_block_curpos()
+ new
+ " Test cursor position. When ve=block and Visual block mode and $gj
+ call append(0, ['12345', '789'])
+ call cursor(1, 3)
+ set virtualedit=block
+ exe "norm! \<C-V>$gj\<Esc>"
+ call assert_equal([0, 2, 4, 0], getpos("'>"))
+ set virtualedit=
+ bwipe!
+endfunc
+
+" Test for block_insert when replacing spaces in front of the a with tabs
+func Test_block_insert_replace_tabs()
+ new
+ set ts=8 sts=4 sw=4
+ call append(0, ["#define BO_ALL\t 0x0001",
+ \ "#define BO_BS\t 0x0002",
+ \ "#define BO_CRSR\t 0x0004"])
+ call cursor(1, 1)
+ exe "norm! f0\<C-V>2jI\<tab>\<esc>"
+ call assert_equal([
+ \ "#define BO_ALL\t\t0x0001",
+ \ "#define BO_BS\t \t0x0002",
+ \ "#define BO_CRSR\t \t0x0004", ''], getline(1, '$'))
+ set ts& sts& sw&
+ bwipe!
+endfunc
+
+func Test_visual_put_in_block_using_zp()
+ new
+ " paste using zP
+ call setline(1, ['/path;text', '/path;text', '/path;text', '',
+ \ '/subdir',
+ \ '/longsubdir',
+ \ '/longlongsubdir'])
+ exe "normal! 5G\<c-v>2j$y"
+ norm! 1Gf;zP
+ call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3))
+ %d
+ " paste using zP
+ call setline(1, ['/path;text', '/path;text', '/path;text', '',
+ \ '/subdir',
+ \ '/longsubdir',
+ \ '/longlongsubdir'])
+ exe "normal! 5G\<c-v>2j$y"
+ norm! 1Gf;hzp
+ call assert_equal(['/path/subdir;text', '/path/longsubdir;text', '/path/longlongsubdir;text'], getline(1, 3))
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 936bfa8c5b..cdeae2e294 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -570,6 +570,45 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize,
}
}
+void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err)
+{
+ win_T *win = find_window_by_handle(window, err), *save_curwin = curwin;
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab;
+
+ if (!win || !buf) {
+ return;
+ }
+
+ if (noautocmd) {
+ block_autocmds();
+ }
+ if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) {
+ api_set_error(err,
+ kErrorTypeException,
+ "Failed to switch to window %d",
+ window);
+ }
+
+ try_start();
+ int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ if (!try_end(err) && result == FAIL) {
+ api_set_error(err,
+ kErrorTypeException,
+ "Failed to set buffer %d",
+ buffer);
+ }
+
+ // If window is not current, state logic will not validate its cursor.
+ // So do it now.
+ validate_cursor();
+
+ restore_win_noblock(save_curwin, save_curtab, false);
+ if (noautocmd) {
+ unblock_autocmds();
+ }
+}
+
/// Create a new float.
///
/// if wp == NULL allocate a new window, otherwise turn existing window into a
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index ceeb84cec9..a57826f7e7 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -387,4 +387,18 @@ describe('API/win', function()
eq({oldbuf}, meths.list_bufs())
end)
end)
+
+ describe('open_win', function()
+ it('noautocmd option works', function()
+ command('autocmd BufEnter,BufLeave,BufWinEnter * let g:fired = 1')
+ meths.open_win(meths.create_buf(true, true), true, {
+ relative='win', row=3, col=3, width=12, height=3, noautocmd=true
+ })
+ eq(0, funcs.exists('g:fired'))
+ meths.open_win(meths.create_buf(true, true), true, {
+ relative='win', row=3, col=3, width=12, height=3
+ })
+ eq(1, funcs.exists('g:fired'))
+ end)
+ end)
end)