aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/change.txt8
-rw-r--r--runtime/doc/index.txt2
-rw-r--r--runtime/doc/lsp.txt22
-rw-r--r--runtime/lua/vim/lsp.lua2
-rw-r--r--runtime/lua/vim/lsp/util.lua56
-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
19 files changed, 517 insertions, 95 deletions
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 d5e8db4dd7..3b8a9d94fe 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1886,6 +1886,28 @@ open_floating_preview({contents}, {syntax}, {opts})
{contents} table of lines to show in window
{syntax} string of syntax to set for opened buffer
{opts} dictionary with optional fields
+ • height of floating window
+ • width of floating window
+ • wrap boolean enable wrapping of long lines
+ (defaults to true)
+ • wrap_at character to wrap at for computing
+ height when wrap is enabled
+ • max_width maximal width of floating window
+ • max_height maximal height of floating window
+ • pad_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
+ • focus_id if a popup with this id is opened,
+ then focus it
+ • close_events list of events that closes the
+ floating window
+ • focusable (boolean, default true): Make
+ float focusable
Return: ~
bufnr,winnr buffer and window number of the newly created
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 93ec9ed624..5a606188dd 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -1421,7 +1421,7 @@ function lsp.omnifunc(findstart, base)
local items = {}
lsp.buf_request(bufnr, 'textDocument/completion', params, function(err, _, result)
- if err or not result then return end
+ if err or not result or vim.fn.mode() ~= "i" then return end
local matches = util.text_document_completion_list_to_complete_items(result, prefix)
-- TODO(ashkan): is this the best way to do this?
vim.list_extend(items, matches)
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index e16b02fa6c..a986bd6f81 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -44,10 +44,30 @@ local function get_border_size(opts)
height = 2
width = 2
else
- height = height + vim.fn.strdisplaywidth(border[2][1]) -- top
- height = height + vim.fn.strdisplaywidth(border[6][1]) -- bottom
- width = width + vim.fn.strdisplaywidth(border[4][1]) -- right
- width = width + vim.fn.strdisplaywidth(border[8][1]) -- left
+ local function border_width(id)
+ if type(border[id]) == "table" then
+ -- border specified as a table of <character, highlight group>
+ return vim.fn.strdisplaywidth(border[id][1])
+ elseif type(border[id]) == "string" then
+ -- border specified as a list of border characters
+ return vim.fn.strdisplaywidth(border[id])
+ end
+ error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|" .. vim.inspect(border))
+ end
+ local function border_height(id)
+ if type(border[id]) == "table" then
+ -- border specified as a table of <character, highlight group>
+ return #border[id][1] > 0 and 1 or 0
+ elseif type(border[id]) == "string" then
+ -- border specified as a list of border characters
+ return #border[id] > 0 and 1 or 0
+ end
+ error("floating preview border is not correct. Please refer to the docs |vim.api.nvim_open_win()|" .. vim.inspect(border))
+ end
+ height = height + border_height(2) -- top
+ height = height + border_height(6) -- bottom
+ width = width + border_width(4) -- right
+ width = width + border_width(8) -- left
end
return { height = height, width = width }
@@ -915,6 +935,7 @@ function M.make_floating_popup_options(width, height, opts)
anchor = anchor,
col = col + (opts.offset_x or 0),
height = height,
+ focusable = opts.focusable,
relative = 'cursor',
row = row + (opts.offset_y or 0),
style = 'minimal',
@@ -1304,18 +1325,19 @@ end
--@param contents table of lines to show in window
--@param syntax string of syntax to set for opened buffer
--@param opts dictionary with optional fields
--- - height of floating window
--- - width of floating window
--- - wrap boolean enable wrapping of long lines (defaults to true)
--- - wrap_at character to wrap at for computing height when wrap is enabled
--- - max_width maximal width of floating window
--- - max_height maximal height of floating window
--- - pad_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
--- - focus_id if a popup with this id is opened, then focus it
--- - close_events list of events that closes the floating window
+--- - height of floating window
+--- - width of floating window
+--- - wrap boolean enable wrapping of long lines (defaults to true)
+--- - wrap_at character to wrap at for computing height when wrap is enabled
+--- - max_width maximal width of floating window
+--- - max_height maximal height of floating window
+--- - pad_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
+--- - focus_id if a popup with this id is opened, then focus it
+--- - close_events list of events that closes the floating window
+--- - focusable (boolean, default true): Make float focusable
--@returns bufnr,winnr buffer and window number of the newly created floating
---preview window
function M.open_floating_preview(contents, syntax, opts)
@@ -1332,7 +1354,7 @@ function M.open_floating_preview(contents, syntax, opts)
local bufnr = api.nvim_get_current_buf()
-- check if this popup is focusable and we need to focus
- if opts.focus_id then
+ if opts.focus_id and opts.focusable ~= false then
-- Go back to previous window if we are in a focusable one
local current_winnr = api.nvim_get_current_win()
if npcall(api.nvim_win_get_var, current_winnr, opts.focus_id) then
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)