summaryrefslogtreecommitdiffstats
path: root/src/testdir
diff options
context:
space:
mode:
Diffstat (limited to 'src/testdir')
-rw-r--r--src/testdir/Make_all.mak4
-rw-r--r--src/testdir/check.vim8
-rw-r--r--src/testdir/dumps/Test_balloon_eval_term_02.dump2
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_01.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_02.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_03.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_rl_01.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_rl_02.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_cul_wcr_rl_03.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_1.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_2.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_3.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_4.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_wrap_1.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_wrap_2.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_wrap_3.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_double_width_wrap_4.dump4
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_1.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_2.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_3.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_4.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_5.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_rl_1.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_rl_2.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_rl_3.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_rl_4.dump3
-rw-r--r--src/testdir/dumps/Test_conceal_ve_after_eol_rl_5.dump3
-rw-r--r--src/testdir/dumps/Test_cursorline_with_visualmode_01.dump12
-rw-r--r--src/testdir/dumps/Test_difffunc_diffexpr_1.dump12
-rw-r--r--src/testdir/dumps/Test_display_scroll_update_visual.dump2
-rw-r--r--src/testdir/dumps/Test_drop_modified_1.dump10
-rw-r--r--src/testdir/dumps/Test_echowin_showmode.dump2
-rw-r--r--src/testdir/dumps/Test_hlsearch_visual_1.dump6
-rw-r--r--src/testdir/dumps/Test_matchparen_win_execute_1.dump5
-rw-r--r--src/testdir/dumps/Test_number_insert_delete_lines_1.dump8
-rw-r--r--src/testdir/dumps/Test_number_insert_delete_lines_2.dump8
-rw-r--r--src/testdir/dumps/Test_number_insert_delete_lines_3.dump8
-rw-r--r--src/testdir/dumps/Test_popup_command_03.dump2
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_1.dump20
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_2.dump20
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_3.dump20
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_4.dump20
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_5.dump20
-rw-r--r--src/testdir/dumps/Test_prop_above_below_empty_6.dump16
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_1.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_2.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_3.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_4.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_5.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_6.dump2
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_2.dump8
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_3.dump8
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_4.dump8
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_5.dump8
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_6.dump8
-rw-r--r--src/testdir/dumps/Test_prop_inserts_text_visual_block_7.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_1.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_2.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_3.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_4.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_5.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_6.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_7.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_8.dump8
-rw-r--r--src/testdir/dumps/Test_prop_wincolor_9.dump8
-rw-r--r--src/testdir/dumps/Test_prop_with_text_above_6.dump2
-rw-r--r--src/testdir/dumps/Test_prop_with_text_above_7.dump2
-rw-r--r--src/testdir/dumps/Test_prop_with_text_empty_line_6.dump4
-rw-r--r--src/testdir/dumps/Test_prop_with_text_override_2.dump2
-rw-r--r--src/testdir/dumps/Test_relnr_colors_wrapped_1.dump20
-rw-r--r--src/testdir/dumps/Test_relnr_colors_wrapped_2.dump20
-rw-r--r--src/testdir/dumps/Test_relnr_colors_wrapped_3.dump20
-rw-r--r--src/testdir/dumps/Test_relnr_colors_wrapped_4.dump20
-rw-r--r--src/testdir/dumps/Test_relnr_colors_wrapped_5.dump20
-rw-r--r--src/testdir/dumps/Test_statusline_showcmd_3.dump6
-rw-r--r--src/testdir/dumps/Test_syntax_c_01.dump4
-rw-r--r--src/testdir/dumps/Test_tabline_showcmd_3.dump6
-rw-r--r--src/testdir/dumps/Test_terminal_popup_7.dump2
-rw-r--r--src/testdir/dumps/Test_textprop_hl_override_2.dump2
-rw-r--r--src/testdir/dumps/Test_vim9_no_redraw.dump2
-rw-r--r--src/testdir/dumps/Test_virtual_text_overlap_with_highlight_1.dump8
-rw-r--r--src/testdir/dumps/Test_visual_block_with_virtualedit.dump6
-rw-r--r--src/testdir/dumps/Test_visual_block_with_virtualedit2.dump6
-rw-r--r--src/testdir/dumps/Test_visual_ends_before_showbreak.dump6
-rw-r--r--src/testdir/dumps/Test_visual_hl_with_showbreak.dump2
-rw-r--r--src/testdir/dumps/Test_visual_sbr_1.dump2
-rw-r--r--src/testdir/dumps/Test_visual_starts_before_skipcol_1.dump2
-rw-r--r--src/testdir/dumps/Test_visual_starts_before_skipcol_2.dump2
-rw-r--r--src/testdir/dumps/Test_visual_starts_before_skipcol_3.dump2
-rw-r--r--src/testdir/dumps/Test_visual_starts_before_skipcol_4.dump2
-rw-r--r--src/testdir/dumps/Test_win_gotoid_2.dump2
-rw-r--r--src/testdir/dumps/Test_wincolor_01.dump4
-rw-r--r--src/testdir/shared.vim5
-rw-r--r--src/testdir/test_autocmd.vim165
-rw-r--r--src/testdir/test_breakindent.vim84
-rw-r--r--src/testdir/test_clientserver.vim2
-rw-r--r--src/testdir/test_cmdline.vim52
-rw-r--r--src/testdir/test_cmdwin.vim115
-rw-r--r--src/testdir/test_comments.vim6
-rw-r--r--src/testdir/test_conceal.vim398
-rw-r--r--src/testdir/test_diffmode.vim309
-rw-r--r--src/testdir/test_display.vim20
-rw-r--r--src/testdir/test_edit.vim35
-rw-r--r--src/testdir/test_excmd.vim27
-rw-r--r--src/testdir/test_execute_func.vim24
-rw-r--r--src/testdir/test_exists.vim49
-rw-r--r--src/testdir/test_expand.vim13
-rw-r--r--src/testdir/test_filetype.vim78
-rw-r--r--src/testdir/test_filter_map.vim142
-rw-r--r--src/testdir/test_format.vim168
-rw-r--r--src/testdir/test_functions.vim51
-rw-r--r--src/testdir/test_goto.vim13
-rw-r--r--src/testdir/test_gui.vim12
-rw-r--r--src/testdir/test_highlight.vim28
-rw-r--r--src/testdir/test_ins_complete.vim74
-rw-r--r--src/testdir/test_listchars.vim76
-rw-r--r--src/testdir/test_listlbr.vim8
-rw-r--r--src/testdir/test_listlbr_utf8.vim21
-rw-r--r--src/testdir/test_lua.vim22
-rw-r--r--src/testdir/test_mapping.vim20
-rw-r--r--src/testdir/test_matchparen.vim25
-rw-r--r--src/testdir/test_messages.vim12
-rw-r--r--src/testdir/test_mswin_event.vim54
-rw-r--r--src/testdir/test_normal.vim26
-rw-r--r--src/testdir/test_number.vim65
-rw-r--r--src/testdir/test_options.vim68
-rw-r--r--src/testdir/test_perl.vim17
-rw-r--r--src/testdir/test_popup.vim18
-rw-r--r--src/testdir/test_popupwin.vim18
-rw-r--r--src/testdir/test_prompt_buffer.vim45
-rw-r--r--src/testdir/test_put.vim38
-rw-r--r--src/testdir/test_python2.vim17
-rw-r--r--src/testdir/test_python3.vim17
-rw-r--r--src/testdir/test_quotestar.vim4
-rw-r--r--src/testdir/test_registers.vim18
-rw-r--r--src/testdir/test_remote.vim79
-rw-r--r--src/testdir/test_ruby.vim17
-rw-r--r--src/testdir/test_scroll_opt.vim38
-rw-r--r--src/testdir/test_startup.vim16
-rw-r--r--src/testdir/test_tabpage.vim96
-rw-r--r--src/testdir/test_tagjump.vim29
-rw-r--r--src/testdir/test_tcl.vim17
-rw-r--r--src/testdir/test_terminal2.vim8
-rw-r--r--src/testdir/test_textprop.vim134
-rw-r--r--src/testdir/test_undo.vim2
-rw-r--r--src/testdir/test_user_func.vim19
-rw-r--r--src/testdir/test_utf8_comparisons.vim35
-rw-r--r--src/testdir/test_vim9_assign.vim97
-rw-r--r--src/testdir/test_vim9_builtin.vim30
-rw-r--r--src/testdir/test_vim9_class.vim758
-rw-r--r--src/testdir/test_vim9_disassemble.vim195
-rw-r--r--src/testdir/test_vim9_expr.vim27
-rw-r--r--src/testdir/test_vim9_import.vim49
-rw-r--r--src/testdir/test_vim9_script.vim25
-rw-r--r--src/testdir/test_vim9_typealias.vim4
-rw-r--r--src/testdir/test_virtualedit.vim17
-rw-r--r--src/testdir/test_visual.vim341
-rw-r--r--src/testdir/test_window_cmd.vim339
-rw-r--r--src/testdir/test_winfixbuf.vim3286
159 files changed, 8167 insertions, 549 deletions
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 1614cf0..c4403e6 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -248,6 +248,7 @@ NEW_TESTS = \
test_regexp_utf8 \
test_registers \
test_reltime \
+ test_remote \
test_rename \
test_restricted \
test_retab \
@@ -324,6 +325,7 @@ NEW_TESTS = \
test_window_cmd \
test_window_id \
test_windows_home \
+ test_winfixbuf \
test_wnext \
test_wordcount \
test_writefile \
@@ -492,6 +494,7 @@ NEW_TESTS_RES = \
test_recover.res \
test_regex_char_classes.res \
test_registers.res \
+ test_remote.res \
test_rename.res \
test_restricted.res \
test_retab.res \
@@ -555,6 +558,7 @@ NEW_TESTS_RES = \
test_window_cmd.res \
test_window_id.res \
test_windows_home.res \
+ test_winfixbuf.res \
test_wordcount.res \
test_writefile.res \
test_xxd.res \
diff --git a/src/testdir/check.vim b/src/testdir/check.vim
index 82abb64..e67d39a 100644
--- a/src/testdir/check.vim
+++ b/src/testdir/check.vim
@@ -160,6 +160,14 @@ func CheckEnv(name)
endif
endfunc
+" Command to Check for pure X11 (no Wayland)
+command -nargs=0 CheckX11 call CheckX11()
+func CheckX11()
+ if !empty($WAYLAND_DISPLAY) || empty($DISPLAY)
+ throw 'Skipped: not pure X11 environment'
+ endif
+endfunc
+
" Command to check that we are using the GUI
command CheckGui call CheckGui()
func CheckGui()
diff --git a/src/testdir/dumps/Test_balloon_eval_term_02.dump b/src/testdir/dumps/Test_balloon_eval_term_02.dump
index 51c5e32..f3bd633 100644
--- a/src/testdir/dumps/Test_balloon_eval_term_02.dump
+++ b/src/testdir/dumps/Test_balloon_eval_term_02.dump
@@ -1,6 +1,6 @@
|o+0&#ffffff0|n|e| |o|n|e| |o|n|e| @38
|t|w|o| |t|X|o| |t|w|o| @38
-|t|h|r|e|e+0&#e0e0e08| |t|h>r+0&#ffffff0|e@1| |t|h|r|e@1| @32
+|t|h|r|e|e+0#0000001#a8a8a8255| |t|h>r+0#0000000#ffffff0|e@1| |t|h|r|e@1| @32
|~+0#4040ff13&| @2| +0#0000001#ffd7ff255@17| +0#4040ff13#ffffff0@27
|~| @2| +0#0000001#ffd7ff255|l|i|n|e| |3| |c|o|l|u|m|n| |5|:| | +0#4040ff13#ffffff0@27
|~| @2| +0#0000001#ffd7ff255|e| |t|h|r|<| @10| +0#4040ff13#ffffff0@27
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_01.dump b/src/testdir/dumps/Test_conceal_cul_wcr_01.dump
new file mode 100644
index 0000000..9cb27c8
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_01.dump
@@ -0,0 +1,4 @@
+|o+8&#ffffff0|n|e| |o|n|e| |o|n|e| @1|o|n|e| |o|n|e| |o|n|e| |o|n|e| |o|n>e| @7
+@1|o|n|e| |o|n|e| |o|n|e| @27
+|~+0#4040ff13&| @38
+| +0#0000000&@21|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_02.dump b/src/testdir/dumps/Test_conceal_cul_wcr_02.dump
new file mode 100644
index 0000000..668e1af
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_02.dump
@@ -0,0 +1,4 @@
+|o+8#ffffff16#e000002|n|e| |o|n|e| |o|n|e| @1|o|n|e| |o|n|e| |o|n|e| |o|n|e| |o|n>e| @7
+@1|o|n|e| |o|n|e| |o|n|e| @27
+|~+0#4040ff13&| @38
+|:+0#0000000#ffffff0|s|e|t| |w|i|n|c|o|l|o|r|=|E|r@1|o|r|M|s|g|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_03.dump b/src/testdir/dumps/Test_conceal_cul_wcr_03.dump
new file mode 100644
index 0000000..965e9f5
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_03.dump
@@ -0,0 +1,4 @@
+|o+0#ffffff16#e000002|n|e| |o|n|e| |o|n|e| @1|o|n|e| |o|n|e| |o|n|e| |o|n|e| |o|n>e| @7
+@1|o|n|e| |o|n|e| |o|n|e| @27
+|~+0#4040ff13&| @38
+|:+0#0000000#ffffff0|s|e|t| |n|o|c|u|r|s|o|r|l|i|n|e| @4|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_rl_01.dump b/src/testdir/dumps/Test_conceal_cul_wcr_rl_01.dump
new file mode 100644
index 0000000..d1a31a6
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_rl_01.dump
@@ -0,0 +1,4 @@
+| +8&#ffffff0@7>e|n|o| |e|n|o| |e|n|o| |e|n|o| |e|n|o| @1|e|n|o| |e|n|o| |e|n|o
+| @27|e|n|o| |e|n|o| |e|n|o|
+| +0#4040ff13&@38|~
+| +0#0000000&@21|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_rl_02.dump b/src/testdir/dumps/Test_conceal_cul_wcr_rl_02.dump
new file mode 100644
index 0000000..21809e9
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_rl_02.dump
@@ -0,0 +1,4 @@
+| +8#ffffff16#e000002@7>e|n|o| |e|n|o| |e|n|o| |e|n|o| |e|n|o| @1|e|n|o| |e|n|o| |e|n|o
+| @27|e|n|o| |e|n|o| |e|n|o|
+| +0#4040ff13&@38|~
+|:+0#0000000#ffffff0|s|e|t| |w|i|n|c|o|l|o|r|=|E|r@1|o|r|M|s|g|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_cul_wcr_rl_03.dump b/src/testdir/dumps/Test_conceal_cul_wcr_rl_03.dump
new file mode 100644
index 0000000..0eeb739
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_cul_wcr_rl_03.dump
@@ -0,0 +1,4 @@
+| +0#ffffff16#e000002@7>e|n|o| |e|n|o| |e|n|o| |e|n|o| |e|n|o| @1|e|n|o| |e|n|o| |e|n|o
+| @27|e|n|o| |e|n|o| |e|n|o|
+| +0#4040ff13&@38|~
+|:+0#0000000#ffffff0|s|e|t| |n|o|c|u|r|s|o|r|l|i|n|e| @4|1|,|4|0| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_double_width_1.dump b/src/testdir/dumps/Test_conceal_double_width_1.dump
new file mode 100644
index 0000000..57353bb
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_1.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@4|β+0#e0e0e08#6c6c6c255|b+0#0000000#ffffff0@4|β+0#e0e0e08#6c6c6c255|c+0#0000000#ffffff0@3>c| @11| +0&#ffd7d7255| +0&#ffffff0@44
+|f|o@1|b|a|r| @22| +0&#ffd7d7255| +0&#ffffff0@44
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|9|-|2|5| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_double_width_2.dump b/src/testdir/dumps/Test_conceal_double_width_2.dump
new file mode 100644
index 0000000..782db47
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_2.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@4|β+0#e0e0e08#6c6c6c255|b+0#0000000#ffffff0@1>b@2|β+0#e0e0e08#6c6c6c255|c+0#0000000#ffffff0@4| @11| +0&#ffd7d7255| +0&#ffffff0@44
+|f|o@1|b|a|r| @22| +0&#ffd7d7255| +0&#ffffff0@44
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|1|5|-|1|3| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_double_width_3.dump b/src/testdir/dumps/Test_conceal_double_width_3.dump
new file mode 100644
index 0000000..5102c7b
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_3.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@4|b@1>b@2|c@4| @13| +0&#ffd7d7255| +0&#ffffff0@44
+|f|o@1|b|a|r| @22| +0&#ffd7d7255| +0&#ffffff0@44
+|~+0#4040ff13&| @73
+|:+0#0000000&|s|e|t| |c|o|n|c|e|a|l@1|e|v|e|l|=|3| @37|1|,|1|5|-|1|3| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_double_width_4.dump b/src/testdir/dumps/Test_conceal_double_width_4.dump
new file mode 100644
index 0000000..8ef8b96
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_4.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@4|b@4|c@3>c| @13| +0&#ffd7d7255| +0&#ffffff0@44
+|f|o@1|b|a|r| @22| +0&#ffd7d7255| +0&#ffffff0@44
+|~+0#4040ff13&| @73
+|:+0#0000000&|s|e|t| |c|o|n|c|e|a|l@1|e|v|e|l|=|3| @37|1|,|2|9|-|2|5| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_double_width_wrap_1.dump b/src/testdir/dumps/Test_conceal_double_width_wrap_1.dump
new file mode 100644
index 0000000..e794b7c
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_wrap_1.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@9|β+0#e0e0e08#6c6c6c255|b+0#0000000#ffffff0@4| @3
+|b@4|β+0#e0e0e08#6c6c6c255|c+0#0000000#ffffff0@8>c| @3
+|~+0#4040ff13&| @18
+| +0#0000000&@9|1|,|4@1|-|4|0| @2
diff --git a/src/testdir/dumps/Test_conceal_double_width_wrap_2.dump b/src/testdir/dumps/Test_conceal_double_width_wrap_2.dump
new file mode 100644
index 0000000..36e64fc
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_wrap_2.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@9|β+0#e0e0e08#6c6c6c255|b+0#0000000#ffffff0@4| @3
+>b@4|β+0#e0e0e08#6c6c6c255|c+0#0000000#ffffff0@9| @3
+|~+0#4040ff13&| @18
+| +0#0000000&@9|1|,|2|3|-|2|1| @2
diff --git a/src/testdir/dumps/Test_conceal_double_width_wrap_3.dump b/src/testdir/dumps/Test_conceal_double_width_wrap_3.dump
new file mode 100644
index 0000000..ba6c835
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_wrap_3.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@9|b@4| @4
+>b@4|c@9| @4
+|~+0#4040ff13&| @18
+|:+0#0000000&|s|e|t| |c|o|n|c|e|1|,|2|3|-|2|1| @2
diff --git a/src/testdir/dumps/Test_conceal_double_width_wrap_4.dump b/src/testdir/dumps/Test_conceal_double_width_wrap_4.dump
new file mode 100644
index 0000000..8997e98
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_double_width_wrap_4.dump
@@ -0,0 +1,4 @@
+|a+0&#ffffff0@9|b@4| @4
+|b@4|c@8>c| @4
+|~+0#4040ff13&| @18
+|:+0#0000000&| @8|1|,|4@1|-|4|0| @2
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_1.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_1.dump
new file mode 100644
index 0000000..635c6db
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_1.dump
@@ -0,0 +1,3 @@
+|a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|p|o>p| @57
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|5| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_2.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_2.dump
new file mode 100644
index 0000000..ecdc5a3
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_2.dump
@@ -0,0 +1,3 @@
+|a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|p|o|p> @57
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|6| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_3.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_3.dump
new file mode 100644
index 0000000..5eff9fa
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_3.dump
@@ -0,0 +1,3 @@
+|a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|p|o|p| > @56
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|6|-|2|7| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_4.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_4.dump
new file mode 100644
index 0000000..0b2120a
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_4.dump
@@ -0,0 +1,3 @@
+|a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|p|o|p| @1> @55
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|6|-|2|8| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_5.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_5.dump
new file mode 100644
index 0000000..2aa8644
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_5.dump
@@ -0,0 +1,3 @@
+|a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|p|o|p| @1>r| @54
+|~+0#4040ff13&| @73
+| +0#0000000&@56|1|,|2|8| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_rl_1.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_1.dump
new file mode 100644
index 0000000..840e19b
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_1.dump
@@ -0,0 +1,3 @@
+| +0&#ffffff0@57>p|o|p|n|m|l|k|j|i|h|g|f|e|d|c|b|a
+| +0#4040ff13&@73|~
+| +0#0000000&@56|1|,|2|5| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_rl_2.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_2.dump
new file mode 100644
index 0000000..c4b0190
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_2.dump
@@ -0,0 +1,3 @@
+| +0&#ffffff0@56> |p|o|p|n|m|l|k|j|i|h|g|f|e|d|c|b|a
+| +0#4040ff13&@73|~
+| +0#0000000&@56|1|,|2|6| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_rl_3.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_3.dump
new file mode 100644
index 0000000..e4a05f7
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_3.dump
@@ -0,0 +1,3 @@
+| +0&#ffffff0@55> @1|p|o|p|n|m|l|k|j|i|h|g|f|e|d|c|b|a
+| +0#4040ff13&@73|~
+| +0#0000000&@56|1|,|2|6|-|2|7| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_rl_4.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_4.dump
new file mode 100644
index 0000000..9f21657
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_4.dump
@@ -0,0 +1,3 @@
+| +0&#ffffff0@54> @2|p|o|p|n|m|l|k|j|i|h|g|f|e|d|c|b|a
+| +0#4040ff13&@73|~
+| +0#0000000&@56|1|,|2|6|-|2|8| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_conceal_ve_after_eol_rl_5.dump b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_5.dump
new file mode 100644
index 0000000..7d2dad6
--- /dev/null
+++ b/src/testdir/dumps/Test_conceal_ve_after_eol_rl_5.dump
@@ -0,0 +1,3 @@
+| +0&#ffffff0@54>r| @1|p|o|p|n|m|l|k|j|i|h|g|f|e|d|c|b|a
+| +0#4040ff13&@73|~
+| +0#0000000&@56|1|,|2|8| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_cursorline_with_visualmode_01.dump b/src/testdir/dumps/Test_cursorline_with_visualmode_01.dump
index b6e20b7..326abac 100644
--- a/src/testdir/dumps/Test_cursorline_with_visualmode_01.dump
+++ b/src/testdir/dumps/Test_cursorline_with_visualmode_01.dump
@@ -1,9 +1,9 @@
-|a+0&#e0e0e08|b|c| | +0&#ffffff0@70
-|a+0&#e0e0e08|b|c| | +0&#ffffff0@70
-|a+0&#e0e0e08|b|c| | +0&#ffffff0@70
-|a+0&#e0e0e08|b|c| | +0&#ffffff0@70
-|a+0&#e0e0e08|b|c| | +0&#ffffff0@70
->a|b+0&#e0e0e08|c| | +0&#ffffff0@70
+|a+0#0000001#a8a8a8255|b|c| | +0#0000000#ffffff0@70
+|a+0#0000001#a8a8a8255|b|c| | +0#0000000#ffffff0@70
+|a+0#0000001#a8a8a8255|b|c| | +0#0000000#ffffff0@70
+|a+0#0000001#a8a8a8255|b|c| | +0#0000000#ffffff0@70
+|a+0#0000001#a8a8a8255|b|c| | +0#0000000#ffffff0@70
+>a|b+0#0000001#a8a8a8255|c| | +0#0000000#ffffff0@70
|a|b|c| @71
|a|b|c| @71
|a|b|c| @71
diff --git a/src/testdir/dumps/Test_difffunc_diffexpr_1.dump b/src/testdir/dumps/Test_difffunc_diffexpr_1.dump
new file mode 100644
index 0000000..b103cc8
--- /dev/null
+++ b/src/testdir/dumps/Test_difffunc_diffexpr_1.dump
@@ -0,0 +1,12 @@
+| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|z+0#0000000#5fd7ff255|e|r|o| @30
+| +0#0000e05#a8a8a8255@1>o+0#0000000#ffffff0|n|e| @31||+1&&| +0#0000e05#a8a8a8255@1|o+0#0000000#ffffff0|n|e| @31
+| +0#0000e05#a8a8a8255@1|t+0#0000000#ffd7ff255|w|o|x+2&#ff404010| +0&#ffd7ff255@30||+1&#ffffff0| +0#0000e05#a8a8a8255@1|t+0#0000000#ffd7ff255|w|o| @31
+| +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|r|e@1| @29||+1&&| +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|r|e@1| @29
+| +0#0000e05#a8a8a8255@1|f+0#0000000#5fd7ff255|o|u|r| @30||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34
+|~+0&#ffffff0| @35||+1#0000000&|~+0#4040ff13&| @35
+|~| @35||+1#0000000&|~+0#4040ff13&| @35
+|~| @35||+1#0000000&|~+0#4040ff13&| @35
+|~| @35||+1#0000000&|~+0#4040ff13&| @35
+|~| @35||+1#0000000&|~+0#4040ff13&| @35
+|X+3#0000000&|d|i|f@2|u|n|c|2|.|t|x|t| @4|1|,|1| @11|A|l@1| |X+1&&|d|i|f@2|u|n|c|1|.|t|x|t| @4|1|,|1| @11|A|l@1
+|"+0&&|X|d|i|f@2|u|n|c|2|.|t|x|t|"| |4|L|,| |2|0|B| @50
diff --git a/src/testdir/dumps/Test_display_scroll_update_visual.dump b/src/testdir/dumps/Test_display_scroll_update_visual.dump
index c174390..4b17a3b 100644
--- a/src/testdir/dumps/Test_display_scroll_update_visual.dump
+++ b/src/testdir/dumps/Test_display_scroll_update_visual.dump
@@ -1,4 +1,4 @@
-| +0#0000e05#a8a8a8255@1>f+0#0000000#ffffff0|o+0&#e0e0e08@1| | +0&#ffffff0@53
+| +0#0000e05#a8a8a8255@1>f+0#0000000#ffffff0|o+0#0000001#a8a8a8255@1| | +0#0000000#ffffff0@53
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
diff --git a/src/testdir/dumps/Test_drop_modified_1.dump b/src/testdir/dumps/Test_drop_modified_1.dump
new file mode 100644
index 0000000..bbd60c0
--- /dev/null
+++ b/src/testdir/dumps/Test_drop_modified_1.dump
@@ -0,0 +1,10 @@
+>T+0&#ffffff0|h|e| |q|u|i|c|k| |b|r|o|w|n| |f|o|x| |j|u|m|p|e|d| |o|v|e|r| |t|h|e| |l|a|z|y| |d|o|g|s| @29
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|:+0#0000000&|d|r|o|p| |X|d|r|o|p|_|m|o|d|i|f|i|e|d|.|t|x|t| @32|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_echowin_showmode.dump b/src/testdir/dumps/Test_echowin_showmode.dump
index 24eb4d0..9a73bbf 100644
--- a/src/testdir/dumps/Test_echowin_showmode.dump
+++ b/src/testdir/dumps/Test_echowin_showmode.dump
@@ -1,4 +1,4 @@
->o+0&#ffffff0|n+0&#e0e0e08|e| | +0&#ffffff0@70
+>o+0&#ffffff0|n+0#0000001#a8a8a8255|e| | +0#0000000#ffffff0@70
|t|w|o| @71
|~+0#4040ff13&| @73
|~| @73
diff --git a/src/testdir/dumps/Test_hlsearch_visual_1.dump b/src/testdir/dumps/Test_hlsearch_visual_1.dump
index 99150db..e65d567 100644
--- a/src/testdir/dumps/Test_hlsearch_visual_1.dump
+++ b/src/testdir/dumps/Test_hlsearch_visual_1.dump
@@ -1,6 +1,6 @@
-|x+0&#ffffff0@2| |y+2&#ffff4012|y+2&#e0e0e08@1| +0&&|z@2| | +0&#ffffff0@27
-|x+0&#e0e0e08@2| |y+2&&@2| +0&&|z@2| | +0&#ffffff0@27
-|x+0&#e0e0e08@2| |y+2&&>y+2&#ffff4012@1| +0&#ffffff0|z@2| @28
+|x+0&#ffffff0@2| |y+2&#ffff4012|y+2#0000001#a8a8a8255@1| +0&&|z@2| | +0#0000000#ffffff0@27
+|x+0#0000001#a8a8a8255@2| |y+2&&@2| +0&&|z@2| | +0#0000000#ffffff0@27
+|x+0#0000001#a8a8a8255@2| |y+2&&>y+2#0000000#ffff4012@1| +0&#ffffff0|z@2| @28
|~+0#4040ff13&| @38
|~| @38
|-+2#0000000&@1| |V|I|S|U|A|L| |-@1|3+0&&| @8|3|,|6| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_matchparen_win_execute_1.dump b/src/testdir/dumps/Test_matchparen_win_execute_1.dump
new file mode 100644
index 0000000..75ab210
--- /dev/null
+++ b/src/testdir/dumps/Test_matchparen_win_execute_1.dump
@@ -0,0 +1,5 @@
+>{+0&#40ffff15|}| +0&#ffffff0@72
+|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|A|l@1
+|{+0&&|}| @72
+|[+1&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|A|l@1
+| +0&&@74
diff --git a/src/testdir/dumps/Test_number_insert_delete_lines_1.dump b/src/testdir/dumps/Test_number_insert_delete_lines_1.dump
new file mode 100644
index 0000000..5aecb46
--- /dev/null
+++ b/src/testdir/dumps/Test_number_insert_delete_lines_1.dump
@@ -0,0 +1,8 @@
+| +0#af5f00255#ffffff0@1|1| |1+0#0000000&| @69
+| +0#af5f00255&@1|2| >2+0#0000000&| @69
+| +0#af5f00255&@1|3| |3+0#0000000&| @69
+| +0#af5f00255&@1|4| |4+0#0000000&| @69
+| +0#af5f00255&@1|5| |5+0#0000000&| @69
+| +0#af5f00255&@1|6| |6+0#0000000&| @69
+| +0#af5f00255&@1|7| |7+0#0000000&| @69
+@57|2|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_number_insert_delete_lines_2.dump b/src/testdir/dumps/Test_number_insert_delete_lines_2.dump
new file mode 100644
index 0000000..dea68e8
--- /dev/null
+++ b/src/testdir/dumps/Test_number_insert_delete_lines_2.dump
@@ -0,0 +1,8 @@
+| +0#af5f00255#ffffff0@1|1| |1+0#0000000&| @69
+| +0#af5f00255&@1|2| >3+0#0000000&| @69
+| +0#af5f00255&@1|3| |4+0#0000000&| @69
+| +0#af5f00255&@1|4| |5+0#0000000&| @69
+| +0#af5f00255&@1|5| |6+0#0000000&| @69
+| +0#af5f00255&@1|6| |7+0#0000000&| @69
+|~+0#4040ff13&| @73
+| +0#0000000&@56|2|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_number_insert_delete_lines_3.dump b/src/testdir/dumps/Test_number_insert_delete_lines_3.dump
new file mode 100644
index 0000000..fff3297
--- /dev/null
+++ b/src/testdir/dumps/Test_number_insert_delete_lines_3.dump
@@ -0,0 +1,8 @@
+| +0#af5f00255#ffffff0@1|1| |1+0#0000000&| @69
+| +0#af5f00255&@1|2| >4+0#0000000&| @69
+| +0#af5f00255&@1|3| |5+0#0000000&| @69
+| +0#af5f00255&@1|4| |6+0#0000000&| @69
+| +0#af5f00255&@1|5| |7+0#0000000&| @69
+|~+0#4040ff13&| @73
+|~| @73
+| +0#0000000&@56|2|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_popup_command_03.dump b/src/testdir/dumps/Test_popup_command_03.dump
index fa2ac70..2a0c5af 100644
--- a/src/testdir/dumps/Test_popup_command_03.dump
+++ b/src/testdir/dumps/Test_popup_command_03.dump
@@ -1,5 +1,5 @@
|o+0&#ffffff0|n|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| @51
-|a|n|d| |o|n|e| |t|w|o| |X+0&#e0e0e08|t|h|r|e@1> +0&#ffffff0|f|o|u|r| |f|i|v|e| @46
+|a|n|d| |o|n|e| |t|w|o| |X+0#0000001#a8a8a8255|t|h|r|e@1> +0#0000000#ffffff0|f|o|u|r| |f|i|v|e| @46
|o|n|e| |m|o|r|e| |t|w|o| |t|h|r|e@1| |f|o|u|r| |f|i|v|e| @46
|~+0#4040ff13&| @73
|~| @73
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_1.dump b/src/testdir/dumps/Test_prop_above_below_empty_1.dump
index f470016..1cc6d01 100644
--- a/src/testdir/dumps/Test_prop_above_below_empty_1.dump
+++ b/src/testdir/dumps/Test_prop_above_below_empty_1.dump
@@ -1,16 +1,16 @@
-| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| |1+0#0000000&@7| @47
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|3| |3+0#0000000&@8| @46
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|4| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|5| >5+0#0000000&@10| @44
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
@42|5|,|1|-|5|7| @7|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_2.dump b/src/testdir/dumps/Test_prop_above_below_empty_2.dump
index 3954a4b..db11724 100644
--- a/src/testdir/dumps/Test_prop_above_below_empty_2.dump
+++ b/src/testdir/dumps/Test_prop_above_below_empty_2.dump
@@ -1,16 +1,16 @@
-| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| |1+0#0000000&@7|$+0#4040ff13&| +0#0000000&@46
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| |$+0#4040ff13&| +0#0000000&@54
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|3| |3+0#0000000&@8|$+0#4040ff13&| +0#0000000&@45
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|4| |$+0#4040ff13&| +0#0000000&@54
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|5| >5+0#0000000&@10|$+0#4040ff13&| +0#0000000&@43
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
|:|s|e|t| |l|i|s|t| @32|5|,|1|-|5|7| @7|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_3.dump b/src/testdir/dumps/Test_prop_above_below_empty_3.dump
index 27f9eec..78f30b7 100644
--- a/src/testdir/dumps/Test_prop_above_below_empty_3.dump
+++ b/src/testdir/dumps/Test_prop_above_below_empty_3.dump
@@ -1,16 +1,16 @@
-| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| |1+0#0000000&@7| | +0&#ffd7d7255| +0&#ffffff0@45
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| | +0#0000000&@8| +0&#ffd7d7255| +0&#ffffff0@45
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|3| |3+0#0000000&@8| +0&#ffd7d7255| +0&#ffffff0@45
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|4| | +0#0000000&@8| +0&#ffd7d7255| +0&#ffffff0@45
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|5| >5+0#0000000&@8|5+0&#ffd7d7255|5+0&#ffffff0| @44
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
|:| @40|5|,|1|-|5|7| @7|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_4.dump b/src/testdir/dumps/Test_prop_above_below_empty_4.dump
index cc3c27d..2c7bf4d 100644
--- a/src/testdir/dumps/Test_prop_above_below_empty_4.dump
+++ b/src/testdir/dumps/Test_prop_above_below_empty_4.dump
@@ -1,16 +1,16 @@
-| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|4| |1+0#0000000&@7| @47
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|3| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| |3+0#0000000&@8| @46
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
|5+0#af5f00255&| @2>5+0#0000000&@10| @44
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
|:| @40|5|,|1|-|5|7| @7|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_5.dump b/src/testdir/dumps/Test_prop_above_below_empty_5.dump
index 5ee4d80..1928ea2 100644
--- a/src/testdir/dumps/Test_prop_above_below_empty_5.dump
+++ b/src/testdir/dumps/Test_prop_above_below_empty_5.dump
@@ -1,16 +1,16 @@
-| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| |1+0#0000000&@7| @47
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
|3+0#af5f00255&| @2>3+0#0000000&@8| @46
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|1| | +0#0000000&@55
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
-| +0#af5f00255&@3|-+0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
| +0#af5f00255&@1|2| |5+0#0000000&@10| @44
-| +0#af5f00255&@3|++0#0000001#ffff4012@2| +0#0000000#ffffff0@52
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
|:| @40|3|,|1|-|5|7| @7|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_above_below_empty_6.dump b/src/testdir/dumps/Test_prop_above_below_empty_6.dump
new file mode 100644
index 0000000..5013643
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_above_below_empty_6.dump
@@ -0,0 +1,16 @@
+| +0#af5f00255#ffffff0@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@1|1| |1+0#0000000&@7| @47
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
+|2+0#af5f00255&| @2> +0#0000000&@55
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@1|1| |3+0#0000000&@8| @46
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@1|2| | +0#0000000&@55
+| +0#af5f00255&@3|++0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|-+0#0000001#ffff4012| +0#0000000#ffffff0@54
+| +0#af5f00255&@3|!+0#0000001#ffff4012| +0#0000000#ffffff0@54
+|@+0#4040ff13&@2| @56
+|:+0#0000000&|c|a|l@1| |A|d@1|M|o|r|e|(|)| @26|2|,|0|-|5|7| @7|T|o|p|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_1.dump b/src/testdir/dumps/Test_prop_inserts_text_1.dump
index 7d1b102..7a51c12 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_1.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_1.dump
@@ -1,6 +1,6 @@
|i+0&#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d| |s|o
|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n>g| @27
-|p|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s|t| @40
+|p|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s|t| @40
|F|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=|1+0#0000000&|,| |.+0#0000e05&|y|=|2+0#0000000&| |}|;| @34
|e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@49
|l|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @42
diff --git a/src/testdir/dumps/Test_prop_inserts_text_2.dump b/src/testdir/dumps/Test_prop_inserts_text_2.dump
index 2a5b2f3..c9c325f 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_2.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_2.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d|
| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n>g| @23
-| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s|t| @38
+| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s|t| @38
| +0#0000e05#a8a8a8255@1|F+0#0000000#ffffff0|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=|1+0#0000000&|,| |.+0#0000e05&|y|=|2+0#0000000&| |}|;| @32
| +0#0000e05#a8a8a8255@1|e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@47
| +0#0000e05#a8a8a8255@1|l+0#0000000#ffffff0|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @40
diff --git a/src/testdir/dumps/Test_prop_inserts_text_3.dump b/src/testdir/dumps/Test_prop_inserts_text_3.dump
index 36a4b8c..2b93554 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_3.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_3.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d|
| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n|g| @23
-| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s>t| @38
+| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s>t| @38
| +0#0000e05#a8a8a8255@1|F+0#0000000#ffffff0|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=|1+0#0000000&|,| |.+0#0000e05&|y|=|2+0#0000000&| |}|;| @32
| +0#0000e05#a8a8a8255@1|e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@47
| +0#0000e05#a8a8a8255@1|l+0#0000000#ffffff0|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @40
diff --git a/src/testdir/dumps/Test_prop_inserts_text_4.dump b/src/testdir/dumps/Test_prop_inserts_text_4.dump
index 8bb637a..83e656a 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_4.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_4.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d|
| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n|g| @23
-| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s|t| @38
+| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s|t| @38
| +0#0000e05#a8a8a8255@1|F+0#0000000#ffffff0|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=>1+0#0000000&|,| |.+0#0000e05&|y|=|2+0#0000000&| |}|;| @32
| +0#0000e05#a8a8a8255@1|e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@47
| +0#0000e05#a8a8a8255@1|l+0#0000000#ffffff0|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @40
diff --git a/src/testdir/dumps/Test_prop_inserts_text_5.dump b/src/testdir/dumps/Test_prop_inserts_text_5.dump
index 75194f6..2885d4d 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_5.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_5.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d|
| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n|g| @23
-| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s|t| @38
+| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s|t| @38
| +0#0000e05#a8a8a8255@1|F+0#0000000#ffffff0|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=|1+0#0000000&|,| |.+0#0000e05&|y|=>2+0#0000000&| |}|;| @32
| +0#0000e05#a8a8a8255@1|e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@47
| +0#0000e05#a8a8a8255@1|l+0#0000000#ffffff0|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @40
diff --git a/src/testdir/dumps/Test_prop_inserts_text_6.dump b/src/testdir/dumps/Test_prop_inserts_text_6.dump
index fb25ddf..5aeddb4 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_6.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_6.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|n|s|e|r|t| |s|o|m|e| |t|e|x|t| |S+0#ffffff16#e000002|O|M|E| |h+0#0000000#ffffff0|e|r|e| |a|n|d| |o|t|h|e|r| |t|e|x|t| |O+0&#ffff4012|T|H|E|R| |t+0&#ffffff0|h|e|r|e| |a|n|d|
| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e| |m|o|r|e| |t|e|x|t| |a|f|t|e|r| |M+0&#5fd7ff255|O|R|E| |w+0&#ffffff0|r|a|p@1|i|n|g| @23
-| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0&#e0e0e08|ö|m|e|和*&|平|t+&|é|x|t|p+0&#ffffff0|o|s|t| @38
+| +0#0000e05#a8a8a8255@1|p+0#0000000#ffffff0|r|e|s+0#0000001#a8a8a8255|ö|m|e|和*&|平|t+&|é|x|t|p+0#0000000#ffffff0|o|s|t| @38
| +0#0000e05#a8a8a8255@1|F+0#0000000#ffffff0|o@1| |f|o@1| |=| |{| |.+0#0000e05&|x|=|1+0#0000000&|,| |.+0#0000e05&|y|=|2+0#0000000&| |}|;| @32
| +0#0000e05#a8a8a8255@1>e+0#ffffff16#e000002|m|p|t|y| |l|i|n|e| +0#0000000#ffffff0@47
| +0#0000e05#a8a8a8255@1|l+0#0000000#ffffff0|o@1|k| |n|o| |h|i|g|h|l|i|g|h|t| @40
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_2.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_2.dump
index 2383b4a..48d8ebc 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_2.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_2.dump
@@ -1,6 +1,6 @@
-|1+0&#e0e0e08|2|3+0&#ffffff0|4|5|6|7|8|9| @50
-|1+0&#e0e0e08|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000000&|3|4|5|6|7|8|9| @46
-|1+0&#e0e0e08|2|口*0#e000e06#ffffff0|3+0#0000000&|4|5|6|7|8|9| @48
-|1+0&#e0e0e08>2+0&#ffffff0|3|4|5|6|7|8|9| @50
+|1+0#0000001#a8a8a8255|2|3+0#0000000#ffffff0|4|5|6|7|8|9| @50
+|1+0#0000001#a8a8a8255|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000000&|3|4|5|6|7|8|9| @46
+|1+0#0000001#a8a8a8255|2|口*0#e000e06#ffffff0|3+0#0000000&|4|5|6|7|8|9| @48
+|1+0#0000001#a8a8a8255>2+0#0000000#ffffff0|3|4|5|6|7|8|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|2| @6|4|,|2| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_3.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_3.dump
index 920cb1d..0234ba4 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_3.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_3.dump
@@ -1,6 +1,6 @@
-|1+0&#e0e0e08|2|3|4+0&#ffffff0|5|6|7|8|9| @50
-|1+0&#e0e0e08|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000000&|3|4|5|6|7|8|9| @46
-|1+0&#e0e0e08|2|口*0#e000e06#ffffff0|3+0#0000000&|4|5|6|7|8|9| @48
-|1+0&#e0e0e08|2>3+0&#ffffff0|4|5|6|7|8|9| @50
+|1+0#0000001#a8a8a8255|2|3|4+0#0000000#ffffff0|5|6|7|8|9| @50
+|1+0#0000001#a8a8a8255|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000000&|3|4|5|6|7|8|9| @46
+|1+0#0000001#a8a8a8255|2|口*0#e000e06#ffffff0|3+0#0000000&|4|5|6|7|8|9| @48
+|1+0#0000001#a8a8a8255|2>3+0#0000000#ffffff0|4|5|6|7|8|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|3| @6|4|,|3| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_4.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_4.dump
index 0017f02..ff5af3a 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_4.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_4.dump
@@ -1,6 +1,6 @@
-|1+0&#e0e0e08|2|3|4|5|6|7|8+0&#ffffff0|9| @50
-|1+0&#e0e0e08|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000000#e0e0e08|3|4+0&#ffffff0|5|6|7|8|9| @46
-|1+0&#e0e0e08|2|口*0#e000e06#ffffff0|3+0#0000000#e0e0e08|4|5|6+0&#ffffff0|7|8|9| @48
-|1+0&#e0e0e08|2|3|4|5|6>7+0&#ffffff0|8|9| @50
+|1+0#0000001#a8a8a8255|2|3|4|5|6|7|8+0#0000000#ffffff0|9| @50
+|1+0#0000001#a8a8a8255|-+0#e000e06#ffffff0|口*&|-+&|2+0#0000001#a8a8a8255|3|4+0#0000000#ffffff0|5|6|7|8|9| @46
+|1+0#0000001#a8a8a8255|2|口*0#e000e06#ffffff0|3+0#0000001#a8a8a8255|4|5|6+0#0000000#ffffff0|7|8|9| @48
+|1+0#0000001#a8a8a8255|2|3|4|5|6>7+0#0000000#ffffff0|8|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|7| @6|4|,|7| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_5.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_5.dump
index cb7e0c9..496eee9 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_5.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_5.dump
@@ -1,6 +1,6 @@
-|1+0&#ffffff0|2+0&#e0e0e08|3|4|5|6|7|8+0&#ffffff0|9| @50
-|1|-+0#e000e06&|口*&|-+&|2+0#0000000#e0e0e08|3|4+0&#ffffff0|5|6|7|8|9| @46
-|1|2+0&#e0e0e08|口*0#e000e06#ffffff0|3+0#0000000#e0e0e08|4|5|6+0&#ffffff0|7|8|9| @48
-|1>2|3+0&#e0e0e08|4|5|6|7|8+0&#ffffff0|9| @50
+|1+0&#ffffff0|2+0#0000001#a8a8a8255|3|4|5|6|7|8+0#0000000#ffffff0|9| @50
+|1|-+0#e000e06&|口*&|-+&|2+0#0000001#a8a8a8255|3|4+0#0000000#ffffff0|5|6|7|8|9| @46
+|1|2+0#0000001#a8a8a8255|口*0#e000e06#ffffff0|3+0#0000001#a8a8a8255|4|5|6+0#0000000#ffffff0|7|8|9| @48
+|1>2|3+0#0000001#a8a8a8255|4|5|6|7|8+0#0000000#ffffff0|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|6| @6|4|,|2| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_6.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_6.dump
index c4be65f..8630777 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_6.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_6.dump
@@ -1,6 +1,6 @@
-|1+0&#ffffff0|2|3+0&#e0e0e08|4|5|6|7|8+0&#ffffff0|9| @50
-|1|-+0#e000e06&|口*&|-+&|2+0#0000000#e0e0e08|3|4+0&#ffffff0|5|6|7|8|9| @46
-|1|2|口*0#e000e06&|3+0#0000000#e0e0e08|4|5|6+0&#ffffff0|7|8|9| @48
-|1|2>3|4+0&#e0e0e08|5|6|7|8+0&#ffffff0|9| @50
+|1+0&#ffffff0|2|3+0#0000001#a8a8a8255|4|5|6|7|8+0#0000000#ffffff0|9| @50
+|1|-+0#e000e06&|口*&|-+&|2+0#0000001#a8a8a8255|3|4+0#0000000#ffffff0|5|6|7|8|9| @46
+|1|2|口*0#e000e06&|3+0#0000001#a8a8a8255|4|5|6+0#0000000#ffffff0|7|8|9| @48
+|1|2>3|4+0#0000001#a8a8a8255|5|6|7|8+0#0000000#ffffff0|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|5| @6|4|,|3| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_inserts_text_visual_block_7.dump b/src/testdir/dumps/Test_prop_inserts_text_visual_block_7.dump
index 54c7fde..bcc9101 100644
--- a/src/testdir/dumps/Test_prop_inserts_text_visual_block_7.dump
+++ b/src/testdir/dumps/Test_prop_inserts_text_visual_block_7.dump
@@ -1,6 +1,6 @@
-|1+0&#ffffff0|2|3|4+0&#e0e0e08|5|6|7|8+0&#ffffff0|9| @50
-|1|-+0#e000e06&|口*&|-+&|2+0#0000000#e0e0e08|3|4+0&#ffffff0|5|6|7|8|9| @46
-|1|2|口*0#e000e06&|3+0#0000000#e0e0e08|4|5|6+0&#ffffff0|7|8|9| @48
-|1|2|3>4|5+0&#e0e0e08|6|7|8+0&#ffffff0|9| @50
+|1+0&#ffffff0|2|3|4+0#0000001#a8a8a8255|5|6|7|8+0#0000000#ffffff0|9| @50
+|1|-+0#e000e06&|口*&|-+&|2+0#0000001#a8a8a8255|3|4+0#0000000#ffffff0|5|6|7|8|9| @46
+|1|2|口*0#e000e06&|3+0#0000001#a8a8a8255|4|5|6+0#0000000#ffffff0|7|8|9| @48
+|1|2|3>4|5+0#0000001#a8a8a8255|6|7|8+0#0000000#ffffff0|9| @50
|~+0#4040ff13&| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|4|x|4| @6|4|,|4| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_1.dump b/src/testdir/dumps/Test_prop_wincolor_1.dump
new file mode 100644
index 0000000..d8a6226
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_1.dump
@@ -0,0 +1,8 @@
+>s+0&#ffd7ff255|o|m|e| |t|e|x|t| |h|e|r|e| @45
+@3|B+0&#5fd7ff255|E|L|O|W| +0&#ffd7ff255@51
+|s|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e| @33
+|m|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@37
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_2.dump b/src/testdir/dumps/Test_prop_wincolor_2.dump
new file mode 100644
index 0000000..6e5126c
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_2.dump
@@ -0,0 +1,8 @@
+>s+8&#ffd7ff255|o|m|e| |t|e|x|t| |h|e|r|e| @45
+@3|B+8&#5fd7ff255|E|L|O|W| +8&#ffd7ff255@51
+|s+0&&|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e| @33
+|m|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@37
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_3.dump b/src/testdir/dumps/Test_prop_wincolor_3.dump
new file mode 100644
index 0000000..f77c274
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_3.dump
@@ -0,0 +1,8 @@
+>s+8&#ffd7ff255|o|m|e| |t|e|x|t| |h|e|r|e| @14| +8&#ffd7d7255| +8&#ffd7ff255@29
+@3|B+8&#5fd7ff255|E|L|O|W| +8&#ffd7ff255@51
+|s+0&&|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e| @2| +0&#ffd7d7255| +0&#ffd7ff255@29
+|m|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@6| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_4.dump b/src/testdir/dumps/Test_prop_wincolor_4.dump
new file mode 100644
index 0000000..06d6b0f
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_4.dump
@@ -0,0 +1,8 @@
+>s+8&#af5f00255|o|m|e| |t|e|x|t| |h|e|r|e| @14| +8&#ffd7d7255| +8&#af5f00255@29
+@3|B+8&#5fd7ff255|E|L|O|W| +8&#af5f00255@51
+|s+0&#ffd7ff255|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e| @2| +0&#ffd7d7255| +0&#ffd7ff255@29
+|m|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@6| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_5.dump b/src/testdir/dumps/Test_prop_wincolor_5.dump
new file mode 100644
index 0000000..0c062b4
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_5.dump
@@ -0,0 +1,8 @@
+|s+8&#af5f00255|o|m|e| |t|e|x|t| |h|e|r>e| @14| +8&#ffd7d7255| +8&#af5f00255@29
+@3|B+8&#5fd7ff255|E|L|O|W| +8&#af5f00255@51
+|s+0&#ffd7ff255|o|m|e| |m|u|c|h| |l|o|n|g+0&#e0e0e08|e+0&#ffd7ff255|r| |t|e|x|t| |h|e|r|e| @2| +0&#ffd7d7255| +0&#ffd7ff255@29
+|m|o|r|e| |t|e|x|t| |h|e|r|e+0&#e0e0e08| +0&#ffd7ff255@2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@6| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1|4| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_6.dump b/src/testdir/dumps/Test_prop_wincolor_6.dump
new file mode 100644
index 0000000..e07b3e6
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_6.dump
@@ -0,0 +1,8 @@
+|s+0&#ffd7ff255|o|m|e| |t|e|x|t| |h|e|r|e| @10| +0&#e0e0e08| +0&#ffd7ff255@2| +0&#ffd7d7255| +0&#ffd7ff255@29
+@3|B+0&#5fd7ff255|E|L|O|W| +0&#ffd7ff255@51
+|s+8&#af5f00255|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r>e| @2| +8&#ffd7d7255| +8&#af5f00255@29
+|m+0&#ffd7ff255|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@2| +0&#e0e0e08| +0&#ffd7ff255@2| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|2|,|2|6| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_7.dump b/src/testdir/dumps/Test_prop_wincolor_7.dump
new file mode 100644
index 0000000..9412811
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_7.dump
@@ -0,0 +1,8 @@
+|s+0&#ffd7ff255|o|m|e| |t|e|x|t| |h|e|r|e| @11| +0&#e0e0e08| +0&#ffd7ff255@1| +0&#ffd7d7255| +0&#ffd7ff255@29
+@3|B+0&#5fd7ff255|E|L|O|W| +0&#ffd7ff255@51
+|s+8&#af5f00255|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e> @2| +8&#ffd7d7255| +8&#af5f00255@29
+|m+0&#ffd7ff255|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@3| +0&#e0e0e08| +0&#ffd7ff255@1| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|2|,|2|7| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_8.dump b/src/testdir/dumps/Test_prop_wincolor_8.dump
new file mode 100644
index 0000000..9df69ec
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_8.dump
@@ -0,0 +1,8 @@
+|s+8&#af5f00255|o|m|e| |t|e|x|t| |h|e|r|e| @11> @2| +8&#ffd7d7255| +8&#af5f00255@29
+@3|B+8&#5fd7ff255|E|L|O|W| +8&#af5f00255@51
+|s+0&#ffd7ff255|o|m|e| |m|u|c|h| |l|o|n|g|e|r| |t|e|x|t| |h|e|r|e| +0&#e0e0e08| +0&#ffd7ff255@1| +0&#ffd7d7255| +0&#ffd7ff255@29
+|m|o|r|e| |t|e|x|t| |h|e|r|e| @2|A+0&#ffff4012|F|T|E|R| +0&#ffd7ff255@3| +0&#e0e0e08| +0&#ffd7ff255@1| +0&#ffd7d7255| +0&#ffd7ff255@29
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|:+0#0000000#ffffff0| @40|1|,|1|5|-|2|7| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_wincolor_9.dump b/src/testdir/dumps/Test_prop_wincolor_9.dump
new file mode 100644
index 0000000..7b26974
--- /dev/null
+++ b/src/testdir/dumps/Test_prop_wincolor_9.dump
@@ -0,0 +1,8 @@
+| +8&#af5f00255@29| +8&#ffd7d7255| +8&#af5f00255@1> @12|e|r|e|h| |t|x|e|t| |e|m|o|s
+| @51|W+8&#5fd7ff255|O|L|E|B| +8&#af5f00255@2
+| +0&#ffd7ff255@29| +0&#ffd7d7255| +0&#ffd7ff255@1| +0&#e0e0e08|e+0&#ffd7ff255|r|e|h| |t|x|e|t| |r|e|g|n|o|l| |h|c|u|m| |e|m|o|s
+| @29| +0&#ffd7d7255| +0&#ffd7ff255@1| +0&#e0e0e08| +0&#ffd7ff255@3|R+0&#ffff4012|E|T|F|A| +0&#ffd7ff255@2|e|r|e|h| |t|x|e|t| |e|r|o|m
+| +0#4040ff13&@58|~
+| @58|~
+| @58|~
+|:+0#0000000#ffffff0| @40|1|,|1|5|-|2|7| @6|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_with_text_above_6.dump b/src/testdir/dumps/Test_prop_with_text_above_6.dump
index da28228..0aa04b5 100644
--- a/src/testdir/dumps/Test_prop_with_text_above_6.dump
+++ b/src/testdir/dumps/Test_prop_with_text_above_6.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| +0&#ffffff0@36
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| +0&#ffffff0@35
-| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |i+0#0000000#e0e0e08|n|s|e|r|t|e>d+0&#ffffff0| |o|n|e| |t|w|o| @37
+| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |i+0#0000001#a8a8a8255|n|s|e|r|t|e>d+0#0000000#ffffff0| |o|n|e| |t|w|o| @37
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|b+0#0000000#5fd7ff255|e|l|o|w| +0&#ffffff0@48
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| |t+0#0000000&|h|r|e@1| |f|o|u|r| @43
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3| +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| +0&#ffffff0@37
diff --git a/src/testdir/dumps/Test_prop_with_text_above_7.dump b/src/testdir/dumps/Test_prop_with_text_above_7.dump
index c50749b..b675ed9 100644
--- a/src/testdir/dumps/Test_prop_with_text_above_7.dump
+++ b/src/testdir/dumps/Test_prop_with_text_above_7.dump
@@ -1,6 +1,6 @@
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|f+0#0000000#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| +0&#ffffff0@36
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|s+0#0000000#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| +0&#ffffff0@35
-| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |i+0#0000000#e0e0e08|n|s|e|r|t|e>d+0&#ffffff0| +0&#e0e0e08|o|n|e| |t|w|o| +0&#ffffff0@37
+| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|1| |i+0#0000001#a8a8a8255|n|s|e|r|t|e>d+0#0000000#ffffff0| +0#0000001#a8a8a8255|o|n|e| |t|w|o| +0#0000000#ffffff0@37
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3|b+0#0000000#5fd7ff255|e|l|o|w| +0#4040ff13#ffffff0| +0#0000000&@47
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@1|2| |t+0#0000000&|h|r|e@1| |f|o|u|r| @43
| +0#0000e05#a8a8a8255@1| +0#af5f00255#ffffff0@3| +0#0000000&@2|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| +0&#ffffff0@37
diff --git a/src/testdir/dumps/Test_prop_with_text_empty_line_6.dump b/src/testdir/dumps/Test_prop_with_text_empty_line_6.dump
index 1bc9903..d9a055e 100644
--- a/src/testdir/dumps/Test_prop_with_text_empty_line_6.dump
+++ b/src/testdir/dumps/Test_prop_with_text_empty_line_6.dump
@@ -1,8 +1,8 @@
|X+0&#ffff4012@59
-|a+0&#e0e0e08@1>a+0&#ffffff0| @56
+|a+0#0000001#a8a8a8255@1>a+0#0000000#ffffff0| @56
|X+0&#ffff4012@59
@1| +0&#ffffff0@58
-|b+0&#e0e0e08@2|b+0&#ffffff0@2| @53
+|b+0#0000001#a8a8a8255@2|b+0#0000000#ffffff0@2| @53
|~+0#4040ff13&| @58
|~| @58
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@13|3|x|3| @6|2|,|3| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_prop_with_text_override_2.dump b/src/testdir/dumps/Test_prop_with_text_override_2.dump
index 334929e..5311cd7 100644
--- a/src/testdir/dumps/Test_prop_with_text_override_2.dump
+++ b/src/testdir/dumps/Test_prop_with_text_override_2.dump
@@ -1,4 +1,4 @@
-|s+0&#ffffff0|o|m+0&#e0e0e08|e| | +0#4040ff13#40ffff15|i|n|s|e|r|t|e|d| |t+0#0000000#e0e0e08|e|x|t| |h|e>r+0&#ffffff0|e| @35
+|s+0&#ffffff0|o|m+0#0000001#a8a8a8255|e| | +0#4040ff13#40ffff15|i|n|s|e|r|t|e|d| |t+0#0000001#a8a8a8255|e|x|t| |h|e>r+0#0000000#ffffff0|e| @35
|~+0#4040ff13&| @58
|~| @58
|~| @58
diff --git a/src/testdir/dumps/Test_relnr_colors_wrapped_1.dump b/src/testdir/dumps/Test_relnr_colors_wrapped_1.dump
new file mode 100644
index 0000000..a0a269f
--- /dev/null
+++ b/src/testdir/dumps/Test_relnr_colors_wrapped_1.dump
@@ -0,0 +1,20 @@
+| +0#0000001#4040ff13@1|2| |1+0#0000000#ffffff0|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0
+| +0#0000001#4040ff13@3|8+0#0000000#ffffff0|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8| @17
+| +0#0000001#4040ff13@1|1| |1+0#0000000#ffffff0|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0
+| +0#0000001#4040ff13@3|9+0#0000000#ffffff0|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9| @17
+|1+0#0000001#ff404010@2| >1+0#0000000#ffffff0@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1
+| +0#0000001#ff404010@3|1+0#0000000#ffffff0|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1
+| +0#0000001#ff404010@3|0+0#0000000#ffffff0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0| @17
+| +0#0000001#40ff4011@1|1| |1+0#0000000#ffffff0@45
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0@45
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0@27| @17
+| +0#0000001#40ff4011@1|2| |1+0#0000000#ffffff0@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1
+| +0#0000001#40ff4011@3|2+0#0000000#ffffff0|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2| @17
+| +0#0000001#40ff4011@1|3| |1+0#0000000#ffffff0@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1
+| +0#0000001#40ff4011@3|3+0#0000000#ffffff0|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3| @17
+| +0#0000001#40ff4011@1|4| |1+0#0000000#ffffff0@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1|@+0#4040ff13&@2
+| +0#0000000&@31|1@2|,|1| @8|5@1|%|
diff --git a/src/testdir/dumps/Test_relnr_colors_wrapped_2.dump b/src/testdir/dumps/Test_relnr_colors_wrapped_2.dump
new file mode 100644
index 0000000..1f2df5e
--- /dev/null
+++ b/src/testdir/dumps/Test_relnr_colors_wrapped_2.dump
@@ -0,0 +1,20 @@
+| +0#0000001#4040ff13@1|1| |1+0#0000000#ffffff0|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0
+| +0#0000001#4040ff13@3|8+0#0000000#ffffff0|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8| @17
+|1+0#0000001#ff404010@1|0| >1+0#0000000#ffffff0|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1
+| +0#0000001#ff404010@3|0+0#0000000#ffffff0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0
+| +0#0000001#ff404010@3|9+0#0000000#ffffff0|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9| @17
+| +0#0000001#40ff4011@1|1| |1+0#0000000#ffffff0@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1
+| +0#0000001#40ff4011@3|0+0#0000000#ffffff0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0| @17
+| +0#0000001#40ff4011@1|2| |1+0#0000000#ffffff0@45
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0@45
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0@27| @17
+| +0#0000001#40ff4011@1|3| |1+0#0000000#ffffff0@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1
+| +0#0000001#40ff4011@3|2+0#0000000#ffffff0|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2| @17
+| +0#0000001#40ff4011@1|4| |1+0#0000000#ffffff0@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1
+| +0#0000001#40ff4011@3|3+0#0000000#ffffff0|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3| @17
+| +0#0000001#40ff4011@1|5| |1+0#0000000#ffffff0@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1|@+0#4040ff13&@2
+| +0#0000000&@31|1@1|0|,|1| @8|5@1|%|
diff --git a/src/testdir/dumps/Test_relnr_colors_wrapped_3.dump b/src/testdir/dumps/Test_relnr_colors_wrapped_3.dump
new file mode 100644
index 0000000..b6f6c60
--- /dev/null
+++ b/src/testdir/dumps/Test_relnr_colors_wrapped_3.dump
@@ -0,0 +1,20 @@
+| +0#0000001#4040ff13@1|3| |1+0#0000000#ffffff0|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0
+| +0#0000001#4040ff13@3|8+0#0000000#ffffff0|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8| @17
+| +0#0000001#4040ff13@1|2| |1+0#0000000#ffffff0|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0
+| +0#0000001#4040ff13@3|9+0#0000000#ffffff0|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9| @17
+| +0#0000001#4040ff13@1|1| |1+0#0000000#ffffff0@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0| @17
+|1+0#0000001#ff404010@1|2| >1+0#0000000#ffffff0@45
+| +0#0000001#ff404010@3|1+0#0000000#ffffff0@45
+| +0#0000001#ff404010@3|1+0#0000000#ffffff0@27| @17
+| +0#0000001#40ff4011@1|1| |1+0#0000000#ffffff0@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1
+| +0#0000001#40ff4011@3|2+0#0000000#ffffff0|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2| @17
+| +0#0000001#40ff4011@1|2| |1+0#0000000#ffffff0@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1
+| +0#0000001#40ff4011@3|3+0#0000000#ffffff0|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3| @17
+| +0#0000001#40ff4011@1|3| |1+0#0000000#ffffff0@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1|@+0#4040ff13&@2
+| +0#0000000&@31|1@1|2|,|1| @8|5@1|%|
diff --git a/src/testdir/dumps/Test_relnr_colors_wrapped_4.dump b/src/testdir/dumps/Test_relnr_colors_wrapped_4.dump
new file mode 100644
index 0000000..b838950
--- /dev/null
+++ b/src/testdir/dumps/Test_relnr_colors_wrapped_4.dump
@@ -0,0 +1,20 @@
+| +0#0000001#4040ff13@1|5| |1+0#0000000#ffffff0|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0
+| +0#0000001#4040ff13@3|8+0#0000000#ffffff0|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8| @17
+| +0#0000001#4040ff13@1|4| |1+0#0000000#ffffff0|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0
+| +0#0000001#4040ff13@3|9+0#0000000#ffffff0|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9| @17
+| +0#0000001#4040ff13@1|3| |1+0#0000000#ffffff0@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0| @17
+| +0#0000001#4040ff13@1|2| |1+0#0000000#ffffff0@45
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0@45
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0@27| @17
+| +0#0000001#4040ff13@1|1| |1+0#0000000#ffffff0@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1
+| +0#0000001#4040ff13@3|2+0#0000000#ffffff0|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2| @17
+|1+0#0000001#ff404010@1|4| >1+0#0000000#ffffff0@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1
+| +0#0000001#ff404010@3|1+0#0000000#ffffff0|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1
+| +0#0000001#ff404010@3|3+0#0000000#ffffff0|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3| @17
+| +0#0000001#40ff4011@1|1| |1+0#0000000#ffffff0@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1|@+0#4040ff13&@2
+| +0#0000000&@31|1@1|4|,|1| @8|5@1|%|
diff --git a/src/testdir/dumps/Test_relnr_colors_wrapped_5.dump b/src/testdir/dumps/Test_relnr_colors_wrapped_5.dump
new file mode 100644
index 0000000..549275a
--- /dev/null
+++ b/src/testdir/dumps/Test_relnr_colors_wrapped_5.dump
@@ -0,0 +1,20 @@
+| +0#0000001#4040ff13@1|4| |1+0#0000000#ffffff0|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0
+| +0#0000001#4040ff13@3|8+0#0000000#ffffff0|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8|1|0|8| @17
+| +0#0000001#4040ff13@1|3| |1+0#0000000#ffffff0|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0
+| +0#0000001#4040ff13@3|9+0#0000000#ffffff0|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9|1|0|9| @17
+| +0#0000001#4040ff13@1|2| |1+0#0000000#ffffff0@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1
+| +0#0000001#4040ff13@3|0+0#0000000#ffffff0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0|1@1|0| @17
+| +0#0000001#4040ff13@1|1| |1+0#0000000#ffffff0@45
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0@45
+| +0#0000001#4040ff13@3|1+0#0000000#ffffff0@27| @17
+|1+0#0000001#ff404010@1|3| >1+0#0000000#ffffff0@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1
+| +0#0000001#ff404010@3|1+0#0000000#ffffff0|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1
+| +0#0000001#ff404010@3|2+0#0000000#ffffff0|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2|1@1|2| @17
+| +0#0000001#40ff4011@1|1| |1+0#0000000#ffffff0@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1
+| +0#0000001#40ff4011@3|1+0#0000000#ffffff0|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1
+| +0#0000001#40ff4011@3|3+0#0000000#ffffff0|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3|1@1|3| @17
+| +0#0000001#40ff4011@1|2| |1+0#0000000#ffffff0@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1@1|4|1|@+0#4040ff13&@2
+| +0#0000000&@31|1@1|3|,|1| @8|5@1|%|
diff --git a/src/testdir/dumps/Test_statusline_showcmd_3.dump b/src/testdir/dumps/Test_statusline_showcmd_3.dump
index 049e0f7..d2b2158 100644
--- a/src/testdir/dumps/Test_statusline_showcmd_3.dump
+++ b/src/testdir/dumps/Test_statusline_showcmd_3.dump
@@ -1,6 +1,6 @@
-|a+0&#e0e0e08| +0&#ffffff0@73
-|b+0&#e0e0e08| +0&#ffffff0@73
-|c+0&#e0e0e08> +0&#ffffff0@73
+|a+0#0000001#a8a8a8255| +0#0000000#ffffff0@73
+|b+0#0000001#a8a8a8255| +0#0000000#ffffff0@73
+|c+0#0000001#a8a8a8255> +0#0000000#ffffff0@73
|~+0#4040ff13&| @73
|3+3#0000000&|x|2| @71
|-+2&&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@56
diff --git a/src/testdir/dumps/Test_syntax_c_01.dump b/src/testdir/dumps/Test_syntax_c_01.dump
index 7224216..bd2ec04 100644
--- a/src/testdir/dumps/Test_syntax_c_01.dump
+++ b/src/testdir/dumps/Test_syntax_c_01.dump
@@ -4,8 +4,8 @@
| +0#0000e05&@2|i|n|t| @2|n|o|t|_|u|s|e|d|;| +0#0000000&@56
|#+0#e000e06&|e|l|s|e| +0#0000000&@69
@3|i+0#00e0003&|n|t| +0#0000000&@2|u|s|e|d|;| @60
-|#+0#e000e06&|e+0&#e0e0e08|n|d|i|f| +0#0000000&| +0&#ffffff0@67
-| +0&#e0e0e08@2|p|r|i|n|t|f|(|"+0#e000002&|J|u|s|t| |a|n| |e|x|a|m|p|l|e| |p|i|e|c|e| |o|f| >C+0&#ffffff0| |c|o|d|e|\+0#e000e06&|n|"+0#e000002&|)+0#0000000&|;| @27
+|#+0#e000e06&|e+0#0000001#a8a8a8255|n|d|i|f| | +0#0000000#ffffff0@67
+| +0#0000001#a8a8a8255@2|p|r|i|n|t|f|(|"|J|u|s|t| |a|n| |e|x|a|m|p|l|e| |p|i|e|c|e| |o|f| >C+0#e000002#ffffff0| |c|o|d|e|\+0#e000e06&|n|"+0#e000002&|)+0#0000000&|;| @27
@3|r+0#af5f00255&|e|t|u|r|n| +0#0000000&|0+0#e000002&|x|0|f@1|;+0#0000000&| @58
|}| @73
| +0#ffffff16#ff404010@16| +0#0000000#ffffff0@57
diff --git a/src/testdir/dumps/Test_tabline_showcmd_3.dump b/src/testdir/dumps/Test_tabline_showcmd_3.dump
index 3e29cda..21a66a3 100644
--- a/src/testdir/dumps/Test_tabline_showcmd_3.dump
+++ b/src/testdir/dumps/Test_tabline_showcmd_3.dump
@@ -1,6 +1,6 @@
|3+1&#ffffff0|x|2| @71
-|a+0&#e0e0e08| +0&#ffffff0@73
-|b+0&#e0e0e08| +0&#ffffff0@73
-|c+0&#e0e0e08> +0&#ffffff0@73
+|a+0#0000001#a8a8a8255| +0#0000000#ffffff0@73
+|b+0#0000001#a8a8a8255| +0#0000000#ffffff0@73
+|c+0#0000001#a8a8a8255> +0#0000000#ffffff0@73
|~+0#4040ff13&| @73
|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@38|3|,|2| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_terminal_popup_7.dump b/src/testdir/dumps/Test_terminal_popup_7.dump
index e7e5aae..0b14d27 100644
--- a/src/testdir/dumps/Test_terminal_popup_7.dump
+++ b/src/testdir/dumps/Test_terminal_popup_7.dump
@@ -4,7 +4,7 @@
|3| @12|╔+0#0000001#ffd7ff255|═@44|╗| +0#0000000#ffffff0@13
|4| @12|║+0#0000001#ffd7ff255|s|o|m|e| |t|e|x|t| @35|║| +0#0000000#ffffff0@13
|5| @12|║+0#0000001#ffd7ff255|t|o| |e+0&#ffff4012|d|i|t| +0&#ffd7ff255@37|║| +0#0000000#ffffff0@13
-|6| @12|║+0#0000001#ffd7ff255|i+0&#e0e0e08|n| |a| >p+0&#ffd7ff255|o|p|u|p| |w|i|n|d|o|w| @27|║| +0#0000000#ffffff0@13
+|6| @12|║+0#0000001#ffd7ff255|i+0&#a8a8a8255|n| |a| >p+0&#ffd7ff255|o|p|u|p| |w|i|n|d|o|w| @27|║| +0#0000000#ffffff0@13
|7| @12|║+0#0000001#ffd7ff255|~+0#4040ff13&| @43|║+0#0000001&| +0#0000000#ffffff0@13
|8| @12|║+0#0000001#ffd7ff255|~+0#4040ff13&| @43|║+0#0000001&| +0#0000000#ffffff0@13
|9| @12|║+0#0000001#ffd7ff255|~+0#4040ff13&| @43|║+0#0000001&| +0#0000000#ffffff0@13
diff --git a/src/testdir/dumps/Test_textprop_hl_override_2.dump b/src/testdir/dumps/Test_textprop_hl_override_2.dump
index 187233d..9e677f8 100644
--- a/src/testdir/dumps/Test_textprop_hl_override_2.dump
+++ b/src/testdir/dumps/Test_textprop_hl_override_2.dump
@@ -1,6 +1,6 @@
|O+0&#ffffff0|n|e| |o+0#4040ff13#ffff4012|n|e| |o+0#0000000#ffffff0|n|e| |o+0#4040ff13#ffff4012|n|e| |o+0#0000000#ffffff0|n|e| @55
|T|w|o| |t+0#4040ff13#ffff4012|w|o| |t+0#0000000#ffffff0|w|o| |t+0#4040ff13#ffff4012|w|o| |t+0#0000000#ffffff0|w|o| @55
-|T|h|r+0&#e0e0e08|e|e+0#4040ff13&| |t|h|r+0#0000000&|e@1| |t+0#4040ff13#ffff4012|h|r|e|e+0#0000000#e0e0e08| |t|h|r>e+0&#ffffff0@1| @51
+|T|h|r+0#0000001#a8a8a8255|e@1| |t|h|r|e@1| |t+0#4040ff13#ffff4012|h|r|e|e+0#0000001#a8a8a8255| |t|h|r>e+0#0000000#ffffff0@1| @51
|~+0#4040ff13&| @73
|~| @73
|~| @73
diff --git a/src/testdir/dumps/Test_vim9_no_redraw.dump b/src/testdir/dumps/Test_vim9_no_redraw.dump
index 1d77a08..2f02082 100644
--- a/src/testdir/dumps/Test_vim9_no_redraw.dump
+++ b/src/testdir/dumps/Test_vim9_no_redraw.dump
@@ -1,4 +1,4 @@
-|s+0&#ffffff0|o+0&#e0e0e08|m|e| |t|e|x|t| | +0&#ffffff0@64
+|s+0&#ffffff0|o+0#0000001#a8a8a8255|m|e| |t|e|x|t| | +0#0000000#ffffff0@64
|~+0#4040ff13&| @73
|~| @73
|~| @73
diff --git a/src/testdir/dumps/Test_virtual_text_overlap_with_highlight_1.dump b/src/testdir/dumps/Test_virtual_text_overlap_with_highlight_1.dump
new file mode 100644
index 0000000..d81456f
--- /dev/null
+++ b/src/testdir/dumps/Test_virtual_text_overlap_with_highlight_1.dump
@@ -0,0 +1,8 @@
+| +0#af5f00255#ffffff0@1|1| |o+0#0000000&|n|e| @52
+| +0#af5f00255&@1|2| |t+0#0000000&|w|o| @52
+| +0#af5f00255&@3|s+0#ffffff16#ff404010|y|n|t|a|x| |e|r@1|o|r| +0#0000000#ffffff0@43
+| +0#af5f00255&@1|3| >t+0#0000000&|h|r|e@1| @50
+| +0#af5f00255&@1|4| |f+0#0000000&|o|u|r+0#e000002&| +0#0000000&@40|o+0#ffffff16#ff404010|t|h|e|r| |e|r@1|o|r
+| +0#af5f00255#ffffff0@1|5| |f+0#e000002&|i+0#0000000&|v|e| @51
+|~+0#4040ff13&| @58
+| +0#0000000&@41|3|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_visual_block_with_virtualedit.dump b/src/testdir/dumps/Test_visual_block_with_virtualedit.dump
index 2991a63..7a24250 100644
--- a/src/testdir/dumps/Test_visual_block_with_virtualedit.dump
+++ b/src/testdir/dumps/Test_visual_block_with_virtualedit.dump
@@ -1,6 +1,6 @@
-|a+0&#e0e0e08@5> +0&#ffffff0@43
-|b+0&#e0e0e08@3| @2| +0&#ffffff0@42
-|c+0&#e0e0e08@1| @4| +0&#ffffff0@42
+|a+0#0000001#a8a8a8255@5> +0#0000000#ffffff0@43
+|b+0#0000001#a8a8a8255@3| @2| +0#0000000#ffffff0@42
+|c+0#0000001#a8a8a8255@1| @4| +0#0000000#ffffff0@42
|~+0#4040ff13&| @48
|~| @48
|~| @48
diff --git a/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump b/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump
index 3c62156..e68df74 100644
--- a/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump
+++ b/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump
@@ -1,6 +1,6 @@
-|a+0&#e0e0e08@5| | +0&#ffffff0@42
-|b+0&#e0e0e08@3| @2| +0&#ffffff0@42
-|c+0&#e0e0e08@1> +0&#ffffff0| +0&#e0e0e08@3| +0&#ffffff0@42
+|a+0#0000001#a8a8a8255@5| | +0#0000000#ffffff0@42
+|b+0#0000001#a8a8a8255@3| @2| +0#0000000#ffffff0@42
+|c+0#0000001#a8a8a8255@1> +0#0000000#ffffff0| +0#0000001#a8a8a8255@3| +0#0000000#ffffff0@42
|~+0#4040ff13&| @48
|~| @48
|~| @48
diff --git a/src/testdir/dumps/Test_visual_ends_before_showbreak.dump b/src/testdir/dumps/Test_visual_ends_before_showbreak.dump
new file mode 100644
index 0000000..ee809ba
--- /dev/null
+++ b/src/testdir/dumps/Test_visual_ends_before_showbreak.dump
@@ -0,0 +1,6 @@
+|x+0&#ffffff0@4| @69
+|↪+0#4040ff13&| |y+0#0000001#a8a8a8255@68> +0#0000000#ffffff0| +0#0000001#a8a8a8255@2
+|↪+0#4040ff13#ffffff0| |z+0#0000000&@3| @68
+|~+0#4040ff13&| @73
+|~| @73
+|-+2#0000000&@1| |V|I|S|U|A|L| |-@1| +0&&@34|7|0| @7|1|,|7|6|-|1|4|7| @5|A|l@1|
diff --git a/src/testdir/dumps/Test_visual_hl_with_showbreak.dump b/src/testdir/dumps/Test_visual_hl_with_showbreak.dump
index fa0b2dc..0bd83b0 100644
--- a/src/testdir/dumps/Test_visual_hl_with_showbreak.dump
+++ b/src/testdir/dumps/Test_visual_hl_with_showbreak.dump
@@ -1,5 +1,5 @@
|a+0&#ffffff0@48>a
-|++0#4040ff13&|a+0#0000000#e0e0e08@3|a+0&#ffffff0@5| @38
+|++0#4040ff13&|a+0#0000001#a8a8a8255@3|a+0#0000000#ffffff0@5| @38
|~+0#4040ff13&| @48
|~| @48
|~| @48
diff --git a/src/testdir/dumps/Test_visual_sbr_1.dump b/src/testdir/dumps/Test_visual_sbr_1.dump
index 0a9511d..3c167ec 100644
--- a/src/testdir/dumps/Test_visual_sbr_1.dump
+++ b/src/testdir/dumps/Test_visual_sbr_1.dump
@@ -1,4 +1,4 @@
-|>+0#4040ff13#ffffff0| +0#0000000#e0e0e08|k|a|s|d| |g|u|b|e|r|g|r|e|n|,| |n|o| |s|e|a| |t|a|k|i|m|a|t|a| |s|a|n|c|t|u|s| |e|s|t| |L|o|r|e|m| |i|p|s|u|m| |d|o|l|o|r| |s|i|t| |a|m|e|t|.> +0&#ffffff0@1
+|>+0#4040ff13#ffffff0| +0#0000001#a8a8a8255|k|a|s|d| |g|u|b|e|r|g|r|e|n|,| |n|o| |s|e|a| |t|a|k|i|m|a|t|a| |s|a|n|c|t|u|s| |e|s|t| |L|o|r|e|m| |i|p|s|u|m| |d|o|l|o|r| |s|i|t| |a|m|e|t|.> +0#0000000#ffffff0@1
@75
@75
@75
diff --git a/src/testdir/dumps/Test_visual_starts_before_skipcol_1.dump b/src/testdir/dumps/Test_visual_starts_before_skipcol_1.dump
index f61440a..4cbf8d0 100644
--- a/src/testdir/dumps/Test_visual_starts_before_skipcol_1.dump
+++ b/src/testdir/dumps/Test_visual_starts_before_skipcol_1.dump
@@ -1,4 +1,4 @@
-|<+0#4040ff13#ffffff0@2| +0#0000000&@4| +0&#e0e0e08|9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0&#ffffff0@45
+|<+0#4040ff13#ffffff0@2| +0#0000000&@4| +0#0000001#a8a8a8255|9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0#0000000#ffffff0@45
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|2|9|1|-|3@1|0| @5|A|l@1
| +0&&@74
|~+0#4040ff13&| @73
diff --git a/src/testdir/dumps/Test_visual_starts_before_skipcol_2.dump b/src/testdir/dumps/Test_visual_starts_before_skipcol_2.dump
index 1f10940..a6df79e 100644
--- a/src/testdir/dumps/Test_visual_starts_before_skipcol_2.dump
+++ b/src/testdir/dumps/Test_visual_starts_before_skipcol_2.dump
@@ -1,4 +1,4 @@
-| +0&#ffffff0@7|++0#4040ff13&@2| +0#0000000#e0e0e08|9|0| |9|1| |9|2| |9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0&#ffffff0@33
+| +0&#ffffff0@7|++0#4040ff13&@2| +0#0000001#a8a8a8255|9|0| |9|1| |9|2| |9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0#0000000#ffffff0@33
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|2|9|1|-|3|4|2| @5|A|l@1
| +0&&@74
|~+0#4040ff13&| @73
diff --git a/src/testdir/dumps/Test_visual_starts_before_skipcol_3.dump b/src/testdir/dumps/Test_visual_starts_before_skipcol_3.dump
index d36d675..77ef541 100644
--- a/src/testdir/dumps/Test_visual_starts_before_skipcol_3.dump
+++ b/src/testdir/dumps/Test_visual_starts_before_skipcol_3.dump
@@ -1,4 +1,4 @@
-|++0#4040ff13#ffffff0@2| +0#0000000&@4| +0&#e0e0e08|9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0&#ffffff0@45
+|++0#4040ff13#ffffff0@2| +0#0000000&@4| +0#0000001#a8a8a8255|9|3| |9|4| |9|5| |9|6| |9|7| |9|8| |9@1> +0#0000000#ffffff0@45
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|2|9|1|-|3@1|0| @5|A|l@1
| +0&&@74
|~+0#4040ff13&| @73
diff --git a/src/testdir/dumps/Test_visual_starts_before_skipcol_4.dump b/src/testdir/dumps/Test_visual_starts_before_skipcol_4.dump
index 101352e..0abcc3c 100644
--- a/src/testdir/dumps/Test_visual_starts_before_skipcol_4.dump
+++ b/src/testdir/dumps/Test_visual_starts_before_skipcol_4.dump
@@ -1,4 +1,4 @@
-|++0#4040ff13#ffffff0@2| +0#0000000#e0e0e08|9|8| |9@1> +0&#ffffff0@65
+|++0#4040ff13#ffffff0@2| +0#0000001#a8a8a8255|9|8| |9@1> +0#0000000#ffffff0@65
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|2|9|1|-|3|1|0| @5|A|l@1
| +0&&@74
|~+0#4040ff13&| @73
diff --git a/src/testdir/dumps/Test_win_gotoid_2.dump b/src/testdir/dumps/Test_win_gotoid_2.dump
index 7a778f5..8573fbe 100644
--- a/src/testdir/dumps/Test_win_gotoid_2.dump
+++ b/src/testdir/dumps/Test_win_gotoid_2.dump
@@ -6,7 +6,7 @@
|[+1&&|N|o| |N|a|m|e|]| @47|1|,|1| @11|T|o|p
|2+0&&|1| @72
|2@1| @72
-|2+0&#e0e0e08>3+0&#ffffff0| @72
+|2+0#0000001#a8a8a8255>3+0#0000000#ffffff0| @72
|2|4| @72
|2|5| @72
|2|6| @72
diff --git a/src/testdir/dumps/Test_wincolor_01.dump b/src/testdir/dumps/Test_wincolor_01.dump
index 5a5ccf5..c8578b2 100644
--- a/src/testdir/dumps/Test_wincolor_01.dump
+++ b/src/testdir/dumps/Test_wincolor_01.dump
@@ -1,6 +1,6 @@
| +0#af5f00255#ffd7ff255@1|2| | +0#0000001&@4| +0&#e0e0e08| +0&#ffd7ff255@64
-| +0#af5f00255&@1|1| |1+0#0000001&@4|1+0&#e0e0e08@4| | +0&#ffd7ff255@59
-| +8#af5f00255&@1|0| |2+0#0000001#e0e0e08@4>2+0&#ffd7ff255@5| +8&&@59
+| +0#af5f00255&@1|1| |1+0#0000001&@4|1+0&#a8a8a8255@4| | +0&#ffd7ff255@59
+| +8#af5f00255&@1|0| |2+0#0000001#a8a8a8255@4>2+0&#ffd7ff255@5| +8&&@59
| +0#af5f00255&@1|1| |3+0#0000001&| |h|e|r|e+0&#e0e0e08| +0&#ffd7ff255|3| @62
| +0#af5f00255&@1|2| | +0#0000001&@4| +0&#e0e0e08| +0&#ffd7ff255@64
| +0#af5f00255&@1|3| |t+0#40ff4011&|h|e| |c|a+0&#e0e0e08|t+0&#ffd7ff255| |i|s| |o+1&&|u|t| +0&&|o|f| |t|h|e| |b|a|g| +0#0000001&@45
diff --git a/src/testdir/shared.vim b/src/testdir/shared.vim
index 4d008a0..dd2f012 100644
--- a/src/testdir/shared.vim
+++ b/src/testdir/shared.vim
@@ -300,7 +300,8 @@ func GetVimCommand(...)
endif
let cmd .= ' --not-a-term'
let cmd .= ' --gui-dialog-file guidialogfile'
- let cmd = substitute(cmd, 'VIMRUNTIME=\S\+', '', '')
+ " remove any environment variables
+ let cmd = substitute(cmd, '[A-Z_]\+=\S\+ *', '', 'g')
" If using valgrind, make sure every run uses a different log file.
if cmd =~ 'valgrind.*--log-file='
@@ -363,7 +364,7 @@ func RunVimPiped(before, after, arguments, pipecmd)
" Optionally run Vim under valgrind
" let cmd = 'valgrind --tool=memcheck --leak-check=yes --num-callers=25 --log-file=valgrind ' . cmd
- exe "silent !" . a:pipecmd . cmd . args . ' ' . a:arguments
+ exe "silent !" .. a:pipecmd .. ' ' .. cmd .. args .. ' ' .. a:arguments
if len(a:before) > 0
call delete('Xbefore.vim')
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index cecb55a..564d568 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -270,6 +270,7 @@ func Test_win_tab_autocmd()
let g:record = []
augroup testing
+ au WinNewPre * call add(g:record, 'WinNewPre')
au WinNew * call add(g:record, 'WinNew')
au WinClosed * call add(g:record, 'WinClosed')
au WinEnter * call add(g:record, 'WinEnter')
@@ -286,8 +287,8 @@ func Test_win_tab_autocmd()
close
call assert_equal([
- \ 'WinLeave', 'WinNew', 'WinEnter',
- \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
+ \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter',
+ \ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinClosed', 'TabClosed', 'WinEnter', 'TabEnter',
\ 'WinLeave', 'WinClosed', 'WinEnter'
\ ], g:record)
@@ -298,17 +299,96 @@ func Test_win_tab_autocmd()
bwipe somefile
call assert_equal([
- \ 'WinLeave', 'TabLeave', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
+ \ 'WinLeave', 'TabLeave', 'WinNewPre', 'WinNew', 'WinEnter', 'TabNew', 'TabEnter',
\ 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter',
\ 'WinClosed', 'TabClosed'
\ ], g:record)
+ let g:record = []
+ copen
+ help
+ tabnext
+ vnew
+
+ call assert_equal([
+ \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter',
+ \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter',
+ \ 'WinNewPre', 'WinLeave', 'WinNew', 'WinEnter'
+ \ ], g:record)
+
augroup testing
au!
augroup END
unlet g:record
endfunc
+func Test_WinNewPre()
+ " Test that the old window layout can be accessed before a new window is created.
+ let g:layouts_pre = []
+ let g:layouts_post = []
+ augroup testing
+ au WinNewPre * call add(g:layouts_pre, winlayout())
+ au WinNew * call add(g:layouts_post, winlayout())
+ augroup END
+ split
+ call assert_notequal(g:layouts_pre[0], g:layouts_post[0])
+ split
+ call assert_equal(g:layouts_pre[1], g:layouts_post[0])
+ call assert_notequal(g:layouts_pre[1], g:layouts_post[1])
+ tabnew
+ call assert_notequal(g:layouts_pre[2], g:layouts_post[1])
+ call assert_notequal(g:layouts_pre[2], g:layouts_post[2])
+ augroup testing
+ au!
+ augroup END
+ unlet g:layouts_pre
+ unlet g:layouts_post
+
+ " Test modifying window layout during WinNewPre throws.
+ let g:caught = 0
+ augroup testing
+ au!
+ au WinNewPre * split
+ augroup END
+ try
+ vnew
+ catch
+ let g:caught += 1
+ endtry
+ augroup testing
+ au!
+ au WinNewPre * tabnew
+ augroup END
+ try
+ vnew
+ catch
+ let g:caught += 1
+ endtry
+ augroup testing
+ au!
+ au WinNewPre * close
+ augroup END
+ try
+ vnew
+ catch
+ let g:caught += 1
+ endtry
+ augroup testing
+ au!
+ au WinNewPre * tabclose
+ augroup END
+ try
+ vnew
+ catch
+ let g:caught += 1
+ endtry
+ call assert_equal(4, g:caught)
+ augroup testing
+ au!
+ augroup END
+ unlet g:caught
+endfunc
+
func Test_WinResized()
CheckRunVimInTerminal
@@ -739,6 +819,27 @@ func Test_WinClosed_switch_tab()
%bwipe!
endfunc
+" This used to trigger WinClosed twice for the same window, and the window's
+" buffer was NULL in the second autocommand.
+func Test_WinClosed_BufUnload_close_other()
+ tabnew
+ let g:tab = tabpagenr()
+ let g:buf = bufnr()
+ new
+ setlocal bufhidden=wipe
+ augroup test-WinClosed
+ autocmd BufUnload * ++once exe g:buf .. 'bwipe!'
+ autocmd WinClosed * call tabpagebuflist(g:tab)
+ augroup END
+ close
+
+ unlet g:tab
+ unlet g:buf
+ autocmd! test-WinClosed
+ augroup! test-WinClosed
+ %bwipe!
+endfunc
+
func s:AddAnAutocmd()
augroup vimBarTest
au BufReadCmd * echo 'hello'
@@ -2626,6 +2727,24 @@ func Test_TextChangedI_with_setline()
bwipe!
endfunc
+func Test_TextChanged_with_norm()
+ " For unknown reason this fails on MS-Windows
+ CheckNotMSWindows
+ CheckFeature terminal
+ let buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
+ call assert_equal('running', term_getstatus(buf))
+ call term_sendkeys(buf, ":let g:a=0\<cr>")
+ call term_wait(buf, 50)
+ call term_sendkeys(buf, ":au! TextChanged * :let g:a+=1\<cr>")
+ call term_wait(buf, 50)
+ call term_sendkeys(buf, ":norm! ia\<cr>")
+ call term_wait(buf, 50)
+ call term_sendkeys(buf, ":echo g:a\<cr>")
+ call term_wait(buf, 50)
+ call WaitForAssert({-> assert_match('^1.*$', term_getline(buf, 3))})
+ bwipe!
+endfunc
+
func Test_Changed_FirstTime()
CheckFeature terminal
CheckNotGui
@@ -4315,4 +4434,44 @@ func Test_autocmd_shortmess()
delfunc SetupVimTest_shm
endfunc
+func Test_autocmd_invalidates_undo_on_textchanged()
+ CheckRunVimInTerminal
+ let script =<< trim END
+ set hidden
+ " create quickfix list (at least 2 lines to move line)
+ vimgrep /u/j %
+
+ " enter quickfix window
+ cwindow
+
+ " set modifiable
+ setlocal modifiable
+
+ " set autocmd to clear quickfix list
+
+ autocmd! TextChanged <buffer> call setqflist([])
+ " move line
+ move+1
+ END
+ call writefile(script, 'XTest_autocmd_invalidates_undo_on_textchanged', 'D')
+ let buf = RunVimInTerminal('XTest_autocmd_invalidates_undo_on_textchanged', {'rows': 20})
+ call term_sendkeys(buf, ":so %\<cr>")
+ call term_sendkeys(buf, "G")
+ call WaitForAssert({-> assert_match('^XTest_autocmd_invalidates_undo_on_textchanged\s*$', term_getline(buf, 20))}, 1000)
+
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_autocmd_creates_new_buffer_on_bufleave()
+ e a.txt
+ e b.txt
+ setlocal bufhidden=wipe
+ autocmd BufLeave <buffer> diffsplit c.txt
+ bn
+ call assert_equal(1, winnr('$'))
+ call assert_equal('a.txt', bufname('%'))
+ bw a.txt
+ bw c.txt
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_breakindent.vim b/src/testdir/test_breakindent.vim
index ee05548..96d91c9 100644
--- a/src/testdir/test_breakindent.vim
+++ b/src/testdir/test_breakindent.vim
@@ -424,7 +424,7 @@ func Test_breakindent12()
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
- call s:close_windows('set nuw=4 listchars=')
+ call s:close_windows('set nuw=4 listchars&')
endfunc
func Test_breakindent12_vartabs()
@@ -439,7 +439,7 @@ func Test_breakindent12_vartabs()
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
- call s:close_windows('set nuw=4 listchars= vts&')
+ call s:close_windows('set nuw=4 listchars& vts&')
endfunc
func Test_breakindent13()
@@ -733,7 +733,7 @@ func Test_breakindent20_list()
\ "shall make no law ",
\ ]
call s:compare_lines(expect, lines)
- " set minimum indent
+ " set minimum text width
setl briopt=min:5
redraw!
let lines = s:screen_lines2(1, 6, 20)
@@ -1086,5 +1086,83 @@ func Test_linebreak_list()
bwipe!
endfunc
+func Test_breakindent_change_display_uhex()
+ call s:test_windows('setl briopt=min:0 list listchars=eol:$')
+ redraw!
+ let lines = s:screen_lines(line('.'), 20)
+ let expect = [
+ \ "^Iabcdefghijklmnopqr",
+ \ " stuvwxyzABCDEFGHIJ",
+ \ " KLMNOP$ "
+ \ ]
+ call s:compare_lines(expect, lines)
+ set display+=uhex
+ redraw!
+ let lines = s:screen_lines(line('.'), 20)
+ let expect = [
+ \ "<09>abcdefghijklmnop",
+ \ " qrstuvwxyzABCDEF",
+ \ " GHIJKLMNOP$ "
+ \ ]
+ call s:compare_lines(expect, lines)
+ set display&
+
+ call s:close_windows()
+endfunc
+
+func Test_breakindent_list_split()
+ 10new
+ 61vsplit
+ setlocal tabstop=8 breakindent list listchars=tab:<->,eol:$
+ put =s:input
+ 30vsplit
+ setlocal listchars=eol:$
+ let expect = [
+ \ "^IabcdefghijklmnopqrstuvwxyzAB|<------>abcdefghijklmnopqrstuv",
+ \ " CDEFGHIJKLMNOP$ | wxyzABCDEFGHIJKLMNOP$ ",
+ \ "~ |~ "
+ \ ]
+ redraw!
+ let lines = s:screen_lines(line('.'), 61)
+ call s:compare_lines(expect, lines)
+ wincmd p
+ redraw!
+ let lines = s:screen_lines(line('.'), 61)
+ call s:compare_lines(expect, lines)
+
+ bwipe!
+endfunc
+
+func Test_breakindent_min_with_signcol()
+ call s:test_windows('setl briopt=min:15 signcolumn=yes')
+ redraw!
+ let expect = [
+ \ " abcdefghijklmn",
+ \ " opqrstuvwxyzABC",
+ \ " DEFGHIJKLMNOP "
+ \ ]
+ let lines = s:screen_lines(line('.'), 20)
+ call s:compare_lines(expect, lines)
+ setl briopt=min:17
+ redraw!
+ let expect = [
+ \ " abcdefghijklmn",
+ \ " opqrstuvwxyzABCDE",
+ \ " FGHIJKLMNOP "
+ \ ]
+ let lines = s:screen_lines(line('.'), 20)
+ call s:compare_lines(expect, lines)
+ setl briopt=min:19
+ redraw!
+ let expect = [
+ \ " abcdefghijklmn",
+ \ " opqrstuvwxyzABCDEF",
+ \ " GHIJKLMNOP "
+ \ ]
+ let lines = s:screen_lines(line('.'), 20)
+ call s:compare_lines(expect, lines)
+
+ call s:close_windows()
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim
index 53947f4..8be521b 100644
--- a/src/testdir/test_clientserver.vim
+++ b/src/testdir/test_clientserver.vim
@@ -13,7 +13,7 @@ source shared.vim
func Check_X11_Connection()
if has('x11')
- CheckEnv DISPLAY
+ CheckX11
try
call remote_send('xxx', '')
catch
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index 4554712..7c86bcd 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -691,7 +691,7 @@ func Test_getcompletion()
bw Xtest\
endif
- call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:')
+ call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E866:')
call assert_fails('call getcompletion("", "burp")', 'E475:')
call assert_fails('call getcompletion("abc", [])', 'E1174:')
endfunc
@@ -917,6 +917,26 @@ func Test_cmdline_remove_char()
let &encoding = encoding_save
endfunc
+func Test_cmdline_del_utf8()
+ let @s = '⒌'
+ call feedkeys(":\"\<C-R>s,,\<C-B>\<Right>\<Del>\<CR>", 'tx')
+ call assert_equal('",,', @:)
+
+ let @s = 'a̳'
+ call feedkeys(":\"\<C-R>s,,\<C-B>\<Right>\<Del>\<CR>", 'tx')
+ call assert_equal('",,', @:)
+
+ let @s = 'β̳'
+ call feedkeys(":\"\<C-R>s,,\<C-B>\<Right>\<Del>\<CR>", 'tx')
+ call assert_equal('",,', @:)
+
+ if has('arabic')
+ let @s = 'لا'
+ call feedkeys(":\"\<C-R>s,,\<C-B>\<Right>\<Del>\<CR>", 'tx')
+ call assert_equal('",,', @:)
+ endif
+endfunc
+
func Test_cmdline_keymap_ctrl_hat()
CheckFeature keymap
@@ -3742,4 +3762,34 @@ func Test_custom_completion_with_glob()
delfunc TestGlobComplete
endfunc
+func Test_window_size_stays_same_after_changing_cmdheight()
+ set laststatus=2
+ let expected = winheight(0)
+ function! Function_name() abort
+ call feedkeys(":"..repeat('x', &columns), 'x')
+ let &cmdheight=2
+ let &cmdheight=1
+ redraw
+ endfunction
+ call Function_name()
+ call assert_equal(expected, winheight(0))
+endfunc
+
+" verify that buffer-completion finds all buffer names matching a pattern
+func Test_buffer_completion()
+ " should return empty list
+ call assert_equal([], getcompletion('', 'buffer'))
+
+ call mkdir('Xbuf_complete', 'R')
+ e Xbuf_complete/Foobar.c
+ e Xbuf_complete/MyFoobar.c
+ e AFoobar.h
+ let expected = ["Xbuf_complete/Foobar.c", "Xbuf_complete/MyFoobar.c", "AFoobar.h"]
+
+ call assert_equal(3, len(getcompletion('Foo', 'buffer')))
+ call assert_equal(expected, getcompletion('Foo', 'buffer'))
+ call feedkeys(":b Foo\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"b Xbuf_complete/Foobar.c Xbuf_complete/MyFoobar.c AFoobar.h", @:)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_cmdwin.vim b/src/testdir/test_cmdwin.vim
index 5ba16db..33a2662 100644
--- a/src/testdir/test_cmdwin.vim
+++ b/src/testdir/test_cmdwin.vim
@@ -188,7 +188,7 @@ func Test_cmdwin_tabpage()
tabclose!
endfunc
-func Test_cmdwin_interrupted()
+func Test_cmdwin_interrupted_more_prompt()
CheckScreendump
" aborting the :smile output caused the cmdline window to use the current
@@ -470,4 +470,117 @@ func Test_cmdwin_restore_heights()
set cmdheight& showtabline& laststatus&
endfunc
+func Test_cmdwin_temp_curwin()
+ func CheckWraps(expect_wrap)
+ setlocal textwidth=0 wrapmargin=1
+
+ call deletebufline('', 1, '$')
+ let as = repeat('a', winwidth(0) - 2 - &wrapmargin)
+ call setline(1, as .. ' b')
+ normal! gww
+
+ setlocal textwidth& wrapmargin&
+ call assert_equal(a:expect_wrap ? [as, 'b'] : [as .. ' b'], getline(1, '$'))
+ endfunc
+
+ func CheckCmdWin()
+ call assert_equal('command', win_gettype())
+ " textoff and &wrapmargin formatting considers the cmdwin_type char.
+ call assert_equal(1, getwininfo(win_getid())[0].textoff)
+ call CheckWraps(1)
+ endfunc
+
+ func CheckOtherWin()
+ call assert_equal('', win_gettype())
+ call assert_equal(0, getwininfo(win_getid())[0].textoff)
+ call CheckWraps(0)
+ endfunc
+
+ call feedkeys("q::call CheckCmdWin()\<CR>:call win_execute(win_getid(winnr('#')), 'call CheckOtherWin()')\<CR>:q<CR>", 'ntx')
+
+ %bwipe!
+ delfunc CheckWraps
+ delfunc CheckCmdWin
+ delfunc CheckOtherWin
+endfunc
+
+func Test_cmdwin_interrupted()
+ func CheckInterrupted()
+ call feedkeys("q::call assert_equal('', getcmdwintype())\<CR>:call assert_equal('', getcmdtype())\<CR>:q<CR>", 'ntx')
+ endfunc
+
+ augroup CmdWin
+
+ " While opening the cmdwin's split:
+ " Close the cmdwin's window.
+ au WinEnter * ++once quit
+ call CheckInterrupted()
+
+ " Close the old window.
+ au WinEnter * ++once execute winnr('#') 'quit'
+ call CheckInterrupted()
+
+ " Switch back to the old window.
+ au WinEnter * ++once wincmd p
+ call CheckInterrupted()
+
+ " Change the old window's buffer.
+ au WinEnter * ++once call win_execute(win_getid(winnr('#')), 'enew')
+ call CheckInterrupted()
+
+ " Using BufLeave autocmds as cmdwin restrictions do not apply to them when
+ " fired from opening the cmdwin...
+ " After opening the cmdwin's split, while creating the cmdwin's buffer:
+ " Delete the cmdwin's buffer.
+ au BufLeave * ++once bwipe
+ call CheckInterrupted()
+
+ " Close the cmdwin's window.
+ au BufLeave * ++once quit
+ call CheckInterrupted()
+
+ " Close the old window.
+ au BufLeave * ++once execute winnr('#') 'quit'
+ call CheckInterrupted()
+
+ " Switch to a different window.
+ au BufLeave * ++once split
+ call CheckInterrupted()
+
+ " Change the old window's buffer.
+ au BufLeave * ++once call win_execute(win_getid(winnr('#')), 'enew')
+ call CheckInterrupted()
+
+ " However, changing the current buffer is OK and does not interrupt.
+ au BufLeave * ++once edit other
+ call feedkeys("q::let t=getcmdwintype()\<CR>:let b=bufnr()\<CR>:clo<CR>", 'ntx')
+ call assert_equal(':', t)
+ call assert_equal(1, bufloaded('other'))
+ call assert_notequal(b, bufnr('other'))
+
+ augroup END
+
+ " No autocmds should remain, but clear the augroup to be sure.
+ augroup CmdWin
+ au!
+ augroup END
+
+ %bwipe!
+ delfunc CheckInterrupted
+endfunc
+
+func Test_cmdwin_existing_bufname()
+ func CheckName()
+ call assert_equal(1, getbufinfo('')[0].command)
+ call assert_equal(0, getbufinfo('[Command Line]')[0].command)
+ call assert_match('#a\s*"\[Command Line\]"', execute('ls'))
+ call assert_match('%a\s*"\[Command Line\]"', execute('ls'))
+ endfunc
+
+ file [Command Line]
+ call feedkeys("q::call CheckName()\<CR>:q\<CR>", 'ntx')
+ 0file
+ delfunc CheckName
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_comments.vim b/src/testdir/test_comments.vim
index c34b85c..67454f4 100644
--- a/src/testdir/test_comments.vim
+++ b/src/testdir/test_comments.vim
@@ -237,6 +237,12 @@ func Test_comment_autoformat()
call feedkeys("aone\ntwo\n", 'xt')
call assert_equal(['one', 'two', ''], getline(1, '$'))
+ set backspace=indent,eol,start
+ %d
+ call feedkeys("aone \n\<BS>", 'xt')
+ call assert_equal(['one'], getline(1, '$'))
+ set backspace&
+
close!
endfunc
diff --git a/src/testdir/test_conceal.vim b/src/testdir/test_conceal.vim
index e977921..355817d 100644
--- a/src/testdir/test_conceal.vim
+++ b/src/testdir/test_conceal.vim
@@ -171,6 +171,57 @@ func Test_conceal_with_cursorcolumn()
call StopVimInTerminal(buf)
endfunc
+" Check that 'cursorline' and 'wincolor' apply to the whole line in presence
+" of wrapped lines containing concealed text.
+func Test_conceal_wrapped_cursorline_wincolor()
+ CheckScreendump
+
+ let code =<< trim [CODE]
+ call setline(1, 'one one one |hidden| one one one one one one one one')
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2 concealcursor=n cursorline
+ normal! g$
+ [CODE]
+
+ call writefile(code, 'XTest_conceal_cul_wcr', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_cul_wcr', {'rows': 4, 'cols': 40})
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_01', {})
+
+ call term_sendkeys(buf, ":set wincolor=ErrorMsg\n")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_02', {})
+
+ call term_sendkeys(buf, ":set nocursorline\n")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_03', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+" Same as Test_conceal_wrapped_cursorline_wincolor(), but with 'rightleft'.
+func Test_conceal_wrapped_cursorline_wincolor_rightleft()
+ CheckScreendump
+
+ let code =<< trim [CODE]
+ call setline(1, 'one one one |hidden| one one one one one one one one')
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2 concealcursor=n cursorline rightleft
+ normal! g$
+ [CODE]
+
+ call writefile(code, 'XTest_conceal_cul_wcr_rl', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_cul_wcr_rl', {'rows': 4, 'cols': 40})
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_01', {})
+
+ call term_sendkeys(buf, ":set wincolor=ErrorMsg\n")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_02', {})
+
+ call term_sendkeys(buf, ":set nocursorline\n")
+ call VerifyScreenDump(buf, 'Test_conceal_cul_wcr_rl_03', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_conceal_resize_term()
CheckScreendump
@@ -338,77 +389,290 @@ func Test_conceal_eol()
endfunc
func Test_conceal_mouse_click()
- enew!
+ call NewWindow(10, 40)
set mouse=a
setlocal conceallevel=2 concealcursor=nc
syn match Concealed "this" conceal
hi link Concealed Search
- call setline(1, 'conceal this click here')
- redraw
- call assert_equal(['conceal click here '], ScreenLines(1, 20))
-
- " click on the space between "this" and "click" puts cursor there
- call test_setmouse(1, 9)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 13, 0, 13], getcurpos())
- " click on 'h' of "here" puts cursor there
- call test_setmouse(1, 16)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 20, 0, 20], getcurpos())
- " click on 'e' of "here" puts cursor there
- call test_setmouse(1, 19)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 23], getcurpos())
- " click after end of line puts cursor on 'e' without 'virtualedit'
- call test_setmouse(1, 20)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 24], getcurpos())
- call test_setmouse(1, 21)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 25], getcurpos())
- call test_setmouse(1, 22)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 26], getcurpos())
- call test_setmouse(1, 31)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 35], getcurpos())
- call test_setmouse(1, 32)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 36], getcurpos())
-
- set virtualedit=all
- redraw
- " click on the space between "this" and "click" puts cursor there
- call test_setmouse(1, 9)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 13, 0, 13], getcurpos())
- " click on 'h' of "here" puts cursor there
- call test_setmouse(1, 16)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 20, 0, 20], getcurpos())
- " click on 'e' of "here" puts cursor there
- call test_setmouse(1, 19)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 23, 0, 23], getcurpos())
- " click after end of line puts cursor there without 'virtualedit'
- call test_setmouse(1, 20)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 24, 0, 24], getcurpos())
- call test_setmouse(1, 21)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 24, 1, 25], getcurpos())
- call test_setmouse(1, 22)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 24, 2, 26], getcurpos())
- call test_setmouse(1, 31)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 24, 11, 35], getcurpos())
- call test_setmouse(1, 32)
- call feedkeys("\<LeftMouse>", "tx")
- call assert_equal([0, 1, 24, 12, 36], getcurpos())
-
- bwipe!
- set mouse& virtualedit&
+
+ " Test with both 'nocursorline' and 'cursorline', as they use two different
+ " code paths to set virtual columns for the cells to clear.
+ for cul in [v:false, v:true]
+ let &l:cursorline = cul
+
+ call setline(1, 'conceal this click here')
+ call assert_equal([
+ \ 'conceal click here ',
+ \ ], ScreenLines(1, 40))
+
+ " Click on the space between "this" and "click" puts cursor there.
+ call test_setmouse(1, 9)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 13, 0, 13], getcurpos())
+ " Click on 'h' of "here" puts cursor there.
+ call test_setmouse(1, 16)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 20, 0, 20], getcurpos())
+ " Click on 'e' of "here" puts cursor there.
+ call test_setmouse(1, 19)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 23], getcurpos())
+ " Click after end of line puts cursor on 'e' without 'virtualedit'.
+ call test_setmouse(1, 20)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 24], getcurpos())
+ call test_setmouse(1, 21)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 25], getcurpos())
+ call test_setmouse(1, 22)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 26], getcurpos())
+ call test_setmouse(1, 31)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 35], getcurpos())
+ call test_setmouse(1, 32)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 36], getcurpos())
+
+ set virtualedit=all
+ redraw
+ " Click on the space between "this" and "click" puts cursor there.
+ call test_setmouse(1, 9)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 13, 0, 13], getcurpos())
+ " Click on 'h' of "here" puts cursor there.
+ call test_setmouse(1, 16)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 20, 0, 20], getcurpos())
+ " Click on 'e' of "here" puts cursor there.
+ call test_setmouse(1, 19)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 23], getcurpos())
+ " Click after end of line puts cursor there with 'virtualedit'.
+ call test_setmouse(1, 20)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 0, 24], getcurpos())
+ call test_setmouse(1, 21)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 1, 25], getcurpos())
+ call test_setmouse(1, 22)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 2, 26], getcurpos())
+ call test_setmouse(1, 31)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 11, 35], getcurpos())
+ call test_setmouse(1, 32)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 12, 36], getcurpos())
+ " Behavior should also be the same with 'colorcolumn'.
+ setlocal colorcolumn=30
+ redraw
+ call test_setmouse(1, 31)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 11, 35], getcurpos())
+ call test_setmouse(1, 32)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 12, 36], getcurpos())
+ setlocal colorcolumn&
+
+ if has('rightleft')
+ setlocal rightleft
+ call assert_equal([
+ \ ' ereh kcilc laecnoc',
+ \ ], ScreenLines(1, 40))
+ " Click on the space between "this" and "click" puts cursor there.
+ call test_setmouse(1, 41 - 9)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 13, 0, 13], getcurpos())
+ " Click on 'h' of "here" puts cursor there.
+ call test_setmouse(1, 41 - 16)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 20, 0, 20], getcurpos())
+ " Click on 'e' of "here" puts cursor there.
+ call test_setmouse(1, 41 - 19)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 23, 0, 23], getcurpos())
+ " Click after end of line puts cursor there with 'virtualedit'.
+ call test_setmouse(1, 41 - 20)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 0, 24], getcurpos())
+ call test_setmouse(1, 41 - 21)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 1, 25], getcurpos())
+ call test_setmouse(1, 41 - 22)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 2, 26], getcurpos())
+ call test_setmouse(1, 41 - 31)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 11, 35], getcurpos())
+ call test_setmouse(1, 41 - 32)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 24, 12, 36], getcurpos())
+ setlocal rightleft&
+ endif
+
+ set virtualedit&
+
+ " Test with a wrapped line.
+ call setline(1, ['conceal this click here']->repeat(3)->join())
+ call assert_equal([
+ \ 'conceal click here conceal cli ',
+ \ 'ck here conceal click here ',
+ \ ], ScreenLines([1, 2], 40))
+ " Click on boguscols puts cursor on the last char of a screen line.
+ for col in range(33, 40)
+ call test_setmouse(1, col)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 40, 0, 40], getcurpos())
+ endfor
+
+ " Also test with the last char of a screen line concealed.
+ setlocal number signcolumn=yes
+ call assert_equal([
+ \ ' 1 conceal click here conceal ',
+ \ ' click here conceal click h ',
+ \ ' ere ',
+ \ ], ScreenLines([1, 3], 40))
+ call test_setmouse(1, 34)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 32, 0, 32], getcurpos())
+ call test_setmouse(2, 7)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 37, 0, 37], getcurpos())
+ " Click on boguscols puts cursor on the last char of a screen line.
+ for col in range(35, 40)
+ call test_setmouse(1, col)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 34, 0, 34], getcurpos())
+ call test_setmouse(2, col)
+ call feedkeys("\<LeftMouse>", "tx")
+ call assert_equal([0, 1, 68, 0, 68], getcurpos())
+ endfor
+ setlocal number& signcolumn&
+ endfor
+
+ call CloseWindow()
+ set mouse&
+endfunc
+
+" Test that cursor is drawn at the correct column when it is after end of the
+" line with 'virtualedit' and concealing.
+func Run_test_conceal_virtualedit_after_eol(wrap)
+ let code =<< trim eval [CODE]
+ let &wrap = {a:wrap}
+ call setline(1, 'abcdefgh|hidden|ijklmnpop')
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2 concealcursor=n virtualedit=all
+ normal! $
+ [CODE]
+ call writefile(code, 'XTest_conceal_ve_after_eol', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_ve_after_eol', {'rows': 3})
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_1', {})
+ call term_sendkeys(buf, "l")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_2', {})
+ call term_sendkeys(buf, "l")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_3', {})
+ call term_sendkeys(buf, "l")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_4', {})
+ call term_sendkeys(buf, "rr")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_5', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_conceal_virtualedit_after_eol()
+ CheckScreendump
+
+ call Run_test_conceal_virtualedit_after_eol(1)
+ call Run_test_conceal_virtualedit_after_eol(0)
+endfunc
+
+" Same as Run_test_conceal_virtualedit_after_eol(), but with 'rightleft'.
+func Run_test_conceal_virtualedit_after_eol_rightleft(wrap)
+ let code =<< trim eval [CODE]
+ let &wrap = {a:wrap}
+ call setline(1, 'abcdefgh|hidden|ijklmnpop')
+ syntax match test /|hidden|/ conceal
+ set conceallevel=2 concealcursor=n virtualedit=all rightleft
+ normal! $
+ [CODE]
+ call writefile(code, 'XTest_conceal_ve_after_eol_rl', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_ve_after_eol_rl', {'rows': 3})
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_1', {})
+ call term_sendkeys(buf, "h")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_2', {})
+ call term_sendkeys(buf, "h")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_3', {})
+ call term_sendkeys(buf, "h")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_4', {})
+ call term_sendkeys(buf, "rr")
+ call VerifyScreenDump(buf, 'Test_conceal_ve_after_eol_rl_5', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_conceal_virtualedit_after_eol_rightleft()
+ CheckFeature rightleft
+ CheckScreendump
+
+ call Run_test_conceal_virtualedit_after_eol_rightleft(1)
+ call Run_test_conceal_virtualedit_after_eol_rightleft(0)
+endfunc
+
+" Test that cursor position is correct when double-width chars are concealed.
+func Run_test_conceal_double_width(wrap)
+ let code =<< trim eval [CODE]
+ let &wrap = {a:wrap}
+ call setline(1, ['aaaaa口=口bbbbb口=口ccccc', 'foobar'])
+ syntax match test /口=口/ conceal cchar=β
+ set conceallevel=2 concealcursor=n colorcolumn=30
+ normal! $
+ [CODE]
+ call writefile(code, 'XTest_conceal_double_width', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_double_width', {'rows': 4})
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_1', {})
+ call term_sendkeys(buf, "gM")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_2', {})
+ call term_sendkeys(buf, ":set conceallevel=3\<CR>")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_3', {})
+ call term_sendkeys(buf, "$")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_4', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_conceal_double_width()
+ CheckScreendump
+
+ call Run_test_conceal_double_width(1)
+ call Run_test_conceal_double_width(0)
+endfunc
+
+" Test that line wrapping is correct when double-width chars are concealed.
+func Test_conceal_double_width_wrap()
+ CheckScreendump
+
+ let code =<< trim [CODE]
+ call setline(1, 'aaaaaaaaaa口=口bbbbbbbbbb口=口cccccccccc')
+ syntax match test /口=口/ conceal cchar=β
+ set conceallevel=2 concealcursor=n
+ normal! $
+ [CODE]
+ call writefile(code, 'XTest_conceal_double_width_wrap', 'D')
+ let buf = RunVimInTerminal('-S XTest_conceal_double_width_wrap', {'rows': 4, 'cols': 20})
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_1', {})
+ call term_sendkeys(buf, "gM")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_2', {})
+ call term_sendkeys(buf, ":set conceallevel=3\<CR>")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_3', {})
+ call term_sendkeys(buf, "$")
+ call VerifyScreenDump(buf, 'Test_conceal_double_width_wrap_4', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_diffmode.vim b/src/testdir/test_diffmode.vim
index 930fd7b..7602bd3 100644
--- a/src/testdir/test_diffmode.vim
+++ b/src/testdir/test_diffmode.vim
@@ -3,6 +3,7 @@
source shared.vim
source screendump.vim
source check.vim
+source view_util.vim
func Test_diff_fold_sync()
enew!
@@ -871,6 +872,10 @@ func VerifyInternal(buf, dumpfile, extra)
endfunc
func Test_diff_screen()
+ if has('osxdarwin') && system('diff --version') =~ '^Apple diff'
+ throw 'Skipped: unified diff does not work properly on this macOS version'
+ endif
+
let g:test_is_flaky = 1
CheckScreendump
CheckFeature menu
@@ -1129,7 +1134,7 @@ func Test_diff_breakindent_after_filler()
CheckScreendump
let lines =<< trim END
- set laststatus=0 diffopt+=followwrap breakindent
+ set laststatus=0 diffopt+=followwrap breakindent breakindentopt=min:0
call setline(1, ['a', ' ' .. repeat('c', 50)])
vnew
call setline(1, ['a', 'b', ' ' .. repeat('c', 50)])
@@ -1712,5 +1717,307 @@ func Test_diff_put_and_undo()
set nodiff
endfunc
+" Test for the diff() function
+def Test_diff_func()
+ # string is added/removed/modified at the beginning
+ assert_equal("@@ -0,0 +1 @@\n+abc\n",
+ diff(['def'], ['abc', 'def'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 1}],
+ diff(['def'], ['abc', 'def'], {output: 'indices'}))
+ assert_equal("@@ -1 +0,0 @@\n-abc\n",
+ diff(['abc', 'def'], ['def'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 0}],
+ diff(['abc', 'def'], ['def'], {output: 'indices'}))
+ assert_equal("@@ -1 +1 @@\n-abc\n+abx\n",
+ diff(['abc', 'def'], ['abx', 'def'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['abc', 'def'], ['abx', 'def'], {output: 'indices'}))
+
+ # string is added/removed/modified at the end
+ assert_equal("@@ -1,0 +2 @@\n+def\n",
+ diff(['abc'], ['abc', 'def'], {output: 'unified'}))
+ assert_equal([{from_idx: 1, from_count: 0, to_idx: 1, to_count: 1}],
+ diff(['abc'], ['abc', 'def'], {output: 'indices'}))
+ assert_equal("@@ -2 +1,0 @@\n-def\n",
+ diff(['abc', 'def'], ['abc'], {output: 'unified'}))
+ assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 0}],
+ diff(['abc', 'def'], ['abc'], {output: 'indices'}))
+ assert_equal("@@ -2 +2 @@\n-def\n+xef\n",
+ diff(['abc', 'def'], ['abc', 'xef'], {output: 'unified'}))
+ assert_equal([{from_idx: 1, from_count: 1, to_idx: 1, to_count: 1}],
+ diff(['abc', 'def'], ['abc', 'xef'], {output: 'indices'}))
+
+ # string is added/removed/modified in the middle
+ assert_equal("@@ -2,0 +3 @@\n+xxx\n",
+ diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'unified'}))
+ assert_equal([{from_idx: 2, from_count: 0, to_idx: 2, to_count: 1}],
+ diff(['111', '222', '333'], ['111', '222', 'xxx', '333'], {output: 'indices'}))
+ assert_equal("@@ -3 +2,0 @@\n-333\n",
+ diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'unified'}))
+ assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 0}],
+ diff(['111', '222', '333', '444'], ['111', '222', '444'], {output: 'indices'}))
+ assert_equal("@@ -3 +3 @@\n-333\n+xxx\n",
+ diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'unified'}))
+ assert_equal([{from_idx: 2, from_count: 1, to_idx: 2, to_count: 1}],
+ diff(['111', '222', '333', '444'], ['111', '222', 'xxx', '444'], {output: 'indices'}))
+
+ # new strings are added to an empty List
+ assert_equal("@@ -0,0 +1,2 @@\n+abc\n+def\n",
+ diff([], ['abc', 'def'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 0, to_idx: 0, to_count: 2}],
+ diff([], ['abc', 'def'], {output: 'indices'}))
+
+ # all the strings are removed from a List
+ assert_equal("@@ -1,2 +0,0 @@\n-abc\n-def\n",
+ diff(['abc', 'def'], [], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 2, to_idx: 0, to_count: 0}],
+ diff(['abc', 'def'], [], {output: 'indices'}))
+
+ # First character is added/removed/different
+ assert_equal("@@ -1 +1 @@\n-abc\n+bc\n",
+ diff(['abc'], ['bc'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['abc'], ['bc'], {output: 'indices'}))
+ assert_equal("@@ -1 +1 @@\n-bc\n+abc\n",
+ diff(['bc'], ['abc'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['bc'], ['abc'], {output: 'indices'}))
+ assert_equal("@@ -1 +1 @@\n-abc\n+xbc\n",
+ diff(['abc'], ['xbc'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['abc'], ['xbc'], {output: 'indices'}))
+
+ # Last character is added/removed/different
+ assert_equal("@@ -1 +1 @@\n-abc\n+abcd\n",
+ diff(['abc'], ['abcd'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['abc'], ['abcd'], {output: 'indices'}))
+ assert_equal("@@ -1 +1 @@\n-abcd\n+abc\n",
+ diff(['abcd'], ['abc'], {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff(['abcd'], ['abc'], {output: 'indices'}))
+ var diff_unified: string = diff(['abc'], ['abx'], {output: 'unified'})
+ assert_equal("@@ -1 +1 @@\n-abc\n+abx\n", diff_unified)
+ var diff_indices: list<dict<number>> =
+ diff(['abc'], ['abx'], {output: 'indices'})
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff_indices)
+
+ # partial string modification at the start and at the end.
+ var fromlist =<< trim END
+ one two
+ three four
+ five six
+ END
+ var tolist =<< trim END
+ one
+ six
+ END
+ assert_equal("@@ -1,3 +1,2 @@\n-one two\n-three four\n-five six\n+one\n+six\n", diff(fromlist, tolist, {output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 3, to_idx: 0, to_count: 2}],
+ diff(fromlist, tolist, {output: 'indices'}))
+
+ # non-contiguous modifications
+ fromlist =<< trim END
+ one two
+ three four
+ five abc six
+ END
+ tolist =<< trim END
+ one abc two
+ three four
+ five six
+ END
+ assert_equal("@@ -1 +1 @@\n-one two\n+one abc two\n@@ -3 +3 @@\n-five abc six\n+five six\n",
+ diff(fromlist, tolist, {output: 'unified'}))
+ assert_equal([{'from_count': 1, 'to_idx': 0, 'to_count': 1, 'from_idx': 0},
+ {'from_count': 1, 'to_idx': 2, 'to_count': 1, 'from_idx': 2}],
+ diff(fromlist, tolist, {output: 'indices'}))
+
+ # add/remove blank lines
+ assert_equal("@@ -2,2 +1,0 @@\n-\n-\n",
+ diff(['one', '', '', 'two'], ['one', 'two'], {output: 'unified'}))
+ assert_equal([{from_idx: 1, from_count: 2, to_idx: 1, to_count: 0}],
+ diff(['one', '', '', 'two'], ['one', 'two'], {output: 'indices'}))
+ assert_equal("@@ -1,0 +2,2 @@\n+\n+\n",
+ diff(['one', 'two'], ['one', '', '', 'two'], {output: 'unified'}))
+ assert_equal([{'from_idx': 1, 'from_count': 0, 'to_idx': 1, 'to_count': 2}],
+ diff(['one', 'two'], ['one', '', '', 'two'], {output: 'indices'}))
+
+ # diff ignoring case
+ assert_equal('', diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'unified'}))
+ assert_equal([], diff(['abc', 'def'], ['ABC', 'DEF'], {icase: true, output: 'indices'}))
+
+ # diff ignoring all whitespace changes except leading whitespace changes
+ assert_equal('', diff(['abc def'], ['abc def '], {iwhite: true}))
+ assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:true}))
+ assert_equal("@@ -1 +1 @@\n- abc\n+ xxx\n", diff([' abc'], [' xxx'], {iwhite: v:false}))
+ assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:true}))
+ assert_equal("@@ -1 +1 @@\n-abc \n+xxx \n", diff(['abc '], ['xxx '], {iwhite: v:false}))
+
+ # diff ignoring all whitespace changes
+ assert_equal('', diff(['abc def'], [' abc def '], {iwhiteall: true}))
+ assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:true}))
+ assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n", diff([' abc '], [' xxx '], {iwhiteall: v:false}))
+
+ # diff ignoring trailing whitespace changes
+ assert_equal('', diff(['abc'], ['abc '], {iwhiteeol: true}))
+
+ # diff ignoring blank lines
+ assert_equal('', diff(['one', '', '', 'two'], ['one', 'two'], {iblank: true}))
+ assert_equal('', diff(['one', 'two'], ['one', '', '', 'two'], {iblank: true}))
+
+ # same string
+ assert_equal('', diff(['abc', 'def', 'ghi'], ['abc', 'def', 'ghi']))
+ assert_equal('', diff([''], ['']))
+ assert_equal('', diff([], []))
+
+ # different xdiff algorithms
+ for algo in ['myers', 'minimal', 'patience', 'histogram']
+ assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n",
+ diff([' abc '], [' xxx '], {algorithm: algo, output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff([' abc '], [' xxx '], {algorithm: algo, output: 'indices'}))
+ endfor
+ assert_equal("@@ -1 +1 @@\n- abc \n+ xxx \n",
+ diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'unified'}))
+ assert_equal([{from_idx: 0, from_count: 1, to_idx: 0, to_count: 1}],
+ diff([' abc '], [' xxx '], {indent-heuristic: true, output: 'indices'}))
+
+ # identical strings
+ assert_equal('', diff(['111', '222'], ['111', '222'], {output: 'unified'}))
+ assert_equal([], diff(['111', '222'], ['111', '222'], {output: 'indices'}))
+ assert_equal('', diff([], [], {output: 'unified'}))
+ assert_equal([], diff([], [], {output: 'indices'}))
+
+ # If 'diffexpr' is set, it should not be used for diff()
+ def MyDiffExpr()
+ enddef
+ var save_diffexpr = &diffexpr
+ :set diffexpr=MyDiffExpr()
+ assert_equal("@@ -1 +1 @@\n-abc\n+\n",
+ diff(['abc'], [''], {output: 'unified'}))
+ assert_equal([{'from_idx': 0, 'from_count': 1, 'to_idx': 0, 'to_count': 1}],
+ diff(['abc'], [''], {output: 'indices'}))
+ assert_equal('MyDiffExpr()', &diffexpr)
+ &diffexpr = save_diffexpr
+
+ # try different values for unified diff 'context'
+ assert_equal("@@ -0,0 +1 @@\n+x\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c']))
+ assert_equal("@@ -0,0 +1 @@\n+x\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 0}))
+ assert_equal("@@ -1 +1,2 @@\n+x\n a\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 1}))
+ assert_equal("@@ -1,2 +1,3 @@\n+x\n a\n b\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 2}))
+ assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 3}))
+ assert_equal("@@ -1,3 +1,4 @@\n+x\n a\n b\n c\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: 4}))
+ assert_equal("@@ -0,0 +1 @@\n+x\n",
+ diff(['a', 'b', 'c'], ['x', 'a', 'b', 'c'], {context: -1}))
+
+ # Error cases
+ assert_fails('call diff({}, ["a"])', 'E1211:')
+ assert_fails('call diff(["a"], {})', 'E1211:')
+ assert_fails('call diff(["a"], ["a"], [])', 'E1206:')
+ assert_fails('call diff(["a"], ["a"], {output: "xyz"})', 'E106: Unsupported diff output format: xyz')
+ assert_fails('call diff(["a"], ["a"], {context: []})', 'E745: Using a List as a Number')
+enddef
+
+" Test for using the diff() function with 'diffexpr'
+func Test_diffexpr_with_diff_func()
+ CheckScreendump
+
+ let lines =<< trim END
+ def DiffFuncExpr()
+ var in: list<string> = readfile(v:fname_in)
+ var new: list<string> = readfile(v:fname_new)
+ var out: string = diff(in, new)
+ writefile(split(out, "\n"), v:fname_out)
+ enddef
+ set diffexpr=DiffFuncExpr()
+
+ edit Xdifffunc1.txt
+ diffthis
+ vert split Xdifffunc2.txt
+ diffthis
+ END
+ call writefile(lines, 'XsetupDiffFunc.vim', 'D')
+
+ call writefile(['zero', 'one', 'two', 'three'], 'Xdifffunc1.txt', 'D')
+ call writefile(['one', 'twox', 'three', 'four'], 'Xdifffunc2.txt', 'D')
+
+ let buf = RunVimInTerminal('-S XsetupDiffFunc.vim', {'rows': 12})
+ call VerifyScreenDump(buf, 'Test_difffunc_diffexpr_1', {})
+ call StopVimInTerminal(buf)
+endfunc
+
+func Test_diff_toggle_wrap_skipcol_leftcol()
+ 61vnew
+ call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.')
+ 30vnew
+ call setline(1, 'ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.')
+ let win1 = win_getid()
+ setlocal smoothscroll
+ exe "normal! $\<C-E>"
+ wincmd l
+ let win2 = win_getid()
+ setlocal smoothscroll
+ exe "normal! $\<C-E>"
+ call assert_equal([
+ \ '<<<sadipscing elitr, sed diam |<<<tetur sadipscing elitr, sed|',
+ \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|',
+ \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|',
+ \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|',
+ \ '~ |ptua. |',
+ \ ], ScreenLines([1, 5], 62))
+ call assert_equal({'col': 29, 'row': 4, 'endcol': 29, 'curscol': 29},
+ \ screenpos(win1, line('.', win1), col('.', win1)))
+ call assert_equal({'col': 36, 'row': 5, 'endcol': 36, 'curscol': 36},
+ \ screenpos(win2, line('.', win2), col('.', win2)))
+
+ wincmd h
+ diffthis
+ wincmd l
+ diffthis
+ normal! 0
+ call assert_equal([
+ \ ' ipsum dolor sit amet, conset| Lorem ipsum dolor sit amet, |',
+ \ '~ |~ |',
+ \ ], ScreenLines([1, 2], 62))
+ call assert_equal({'col': 3, 'row': 1, 'endcol': 3, 'curscol': 3},
+ \ screenpos(win1, line('.', win1), col('.', win1)))
+ call assert_equal({'col': 34, 'row': 1, 'endcol': 34, 'curscol': 34},
+ \ screenpos(win2, line('.', win2), col('.', win2)))
+
+ normal! $
+ call assert_equal([
+ \ ' voluptua. | diam voluptua. |',
+ \ '~ |~ |',
+ \ ], ScreenLines([1, 2], 62))
+ call assert_equal({'col': 11, 'row': 1, 'endcol': 11, 'curscol': 11},
+ \ screenpos(win1, line('.', win1), col('.', win1)))
+ call assert_equal({'col': 48, 'row': 1, 'endcol': 48, 'curscol': 48},
+ \ screenpos(win2, line('.', win2), col('.', win2)))
+
+ diffoff!
+ call assert_equal([
+ \ 'ipsum dolor sit amet, consetet|Lorem ipsum dolor sit amet, co|',
+ \ 'ur sadipscing elitr, sed diam |nsetetur sadipscing elitr, sed|',
+ \ 'nonumy eirmod tempor invidunt | diam nonumy eirmod tempor inv|',
+ \ 'ut labore et dolore magna aliq|idunt ut labore et dolore magn|',
+ \ 'uyam erat, sed diam voluptua. |a aliquyam erat, sed diam volu|',
+ \ '~ |ptua. |',
+ \ ], ScreenLines([1, 6], 62))
+ call assert_equal({'col': 29, 'row': 5, 'endcol': 29, 'curscol': 29},
+ \ screenpos(win1, line('.', win1), col('.', win1)))
+ call assert_equal({'col': 36, 'row': 6, 'endcol': 36, 'curscol': 36},
+ \ screenpos(win2, line('.', win2), col('.', win2)))
+
+ bwipe!
+ bwipe!
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_display.vim b/src/testdir/test_display.vim
index 5e0f86a..86e400e 100644
--- a/src/testdir/test_display.vim
+++ b/src/testdir/test_display.vim
@@ -252,12 +252,12 @@ func Test_eob_fillchars()
" default value
call assert_match('eob:\~', &fillchars)
" invalid values
- call assert_fails(':set fillchars=eob:', 'E474:')
- call assert_fails(':set fillchars=eob:xy', 'E474:')
- call assert_fails(':set fillchars=eob:\255', 'E474:')
- call assert_fails(':set fillchars=eob:<ff>', 'E474:')
- call assert_fails(":set fillchars=eob:\x01", 'E474:')
- call assert_fails(':set fillchars=eob:\\x01', 'E474:')
+ call assert_fails(':set fillchars=eob:', 'E1511:')
+ call assert_fails(':set fillchars=eob:xy', 'E1511:')
+ call assert_fails(':set fillchars=eob:\255', 'E1511:')
+ call assert_fails(':set fillchars=eob:<ff>', 'E1511:')
+ call assert_fails(":set fillchars=eob:\x01", 'E1512:')
+ call assert_fails(':set fillchars=eob:\\x01', 'E1512:')
" default is ~
new
redraw
@@ -411,14 +411,16 @@ func Run_Test_display_lastline(euro)
call StopVimInTerminal(buf)
endfunc
-func Test_display_lastline()
+func Test_display_lastline_dump()
CheckScreendump
call Run_Test_display_lastline('')
call Run_Test_display_lastline('euro_')
+endfunc
- call assert_fails(':set fillchars=lastline:', 'E474:')
- call assert_fails(':set fillchars=lastline:〇', 'E474:')
+func Test_display_lastline_fails()
+ call assert_fails(':set fillchars=lastline:', 'E1511:')
+ call assert_fails(':set fillchars=lastline:〇', 'E1512:')
endfunc
func Test_display_long_lastline()
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index d662f4a..36e0525 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -2123,4 +2123,39 @@ func Test_edit_shift_bs()
call StopVimInTerminal(buf)
endfunc
+func Test_edit_Ctrl_RSB()
+ new
+ let g:triggered = []
+ autocmd InsertCharPre <buffer> let g:triggered += [v:char]
+
+ " i_CTRL-] should not trigger InsertCharPre
+ exe "normal! A\<C-]>"
+ call assert_equal([], g:triggered)
+
+ " i_CTRL-] should expand abbreviations but not trigger InsertCharPre
+ inoreabbr <buffer> f foo
+ exe "normal! Af\<C-]>a"
+ call assert_equal(['f', 'f', 'o', 'o', 'a'], g:triggered)
+ call assert_equal('fooa', getline(1))
+
+ " CTRL-] followed by i_CTRL-V should not expand abbreviations
+ " i_CTRL-V doesn't trigger InsertCharPre
+ call setline(1, '')
+ exe "normal! Af\<C-V>\<C-]>"
+ call assert_equal("f\<C-]>", getline(1))
+
+ let g:triggered = []
+ call setline(1, '')
+
+ " Also test assigning to v:char
+ autocmd InsertCharPre <buffer> let v:char = 'f'
+ exe "normal! Ag\<C-]>h"
+ call assert_equal(['g', 'f', 'o', 'o', 'h'], g:triggered)
+ call assert_equal('ffff', getline(1))
+
+ autocmd! InsertCharPre
+ unlet g:triggered
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim
index 47fc267..221ceb0 100644
--- a/src/testdir/test_excmd.vim
+++ b/src/testdir/test_excmd.vim
@@ -3,6 +3,7 @@
source check.vim
source shared.vim
source term_util.vim
+source screendump.vim
func Test_ex_delete()
new
@@ -84,18 +85,27 @@ endfunc
" Test for the :drop command
func Test_drop_cmd()
call writefile(['L1', 'L2'], 'Xdropfile', 'D')
+ " Test for reusing the current buffer
enew | only
+ let expected_nr = bufnr()
drop Xdropfile
+ call assert_equal(expected_nr, bufnr())
call assert_equal('L2', getline(2))
" Test for switching to an existing window
below new
drop Xdropfile
call assert_equal(1, winnr())
- " Test for splitting the current window
+ " Test for splitting the current window (set nohidden)
enew | only
set modified
drop Xdropfile
call assert_equal(2, winnr('$'))
+ " Not splitting the current window even if modified (set hidden)
+ set hidden
+ enew | only
+ set modified
+ drop Xdropfile
+ call assert_equal(1, winnr('$'))
" Check for setting the argument list
call assert_equal(['Xdropfile'], argv())
enew | only!
@@ -729,4 +739,19 @@ func Test_ex_address_range_overflow()
call assert_fails(':--+foobar', 'E492:')
endfunc
+func Test_drop_modified_file()
+ CheckScreendump
+ let lines =<< trim END
+ call setline(1, 'The quick brown fox jumped over the lazy dogs')
+ END
+ call writefile([''], 'Xdrop_modified.txt', 'D')
+ call writefile(lines, 'Xtest_drop_modified', 'D')
+ let buf = RunVimInTerminal('-S Xtest_drop_modified Xdrop_modified.txt', {'rows': 10,'columns': 40})
+ call term_sendkeys(buf, ":drop Xdrop_modified.txt\<CR>")
+ call VerifyScreenDump(buf, 'Test_drop_modified_1', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim
index 09b976b..faaace0 100644
--- a/src/testdir/test_execute_func.vim
+++ b/src/testdir/test_execute_func.vim
@@ -204,4 +204,28 @@ func Test_execute_func_with_null()
endif
endfunc
+func Test_win_execute_tabpagewinnr()
+ belowright split
+ tab split
+ belowright split
+ call assert_equal(2, tabpagewinnr(1))
+
+ tabprevious
+ wincmd p
+ call assert_equal(1, tabpagenr())
+ call assert_equal(1, tabpagewinnr(1))
+ call assert_equal(2, tabpagewinnr(2))
+
+ call win_execute(win_getid(1, 2),
+ \ 'call assert_equal(2, tabpagenr())'
+ \ .. '| call assert_equal(1, tabpagewinnr(1))'
+ \ .. '| call assert_equal(1, tabpagewinnr(2))')
+
+ call assert_equal(1, tabpagenr())
+ call assert_equal(1, tabpagewinnr(1))
+ call assert_equal(2, tabpagewinnr(2))
+
+ %bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_exists.vim b/src/testdir/test_exists.vim
index a735a7d..9cdb3fb 100644
--- a/src/testdir/test_exists.vim
+++ b/src/testdir/test_exists.vim
@@ -1,5 +1,7 @@
" Tests for the exists() function
+import './vim9.vim' as v9
+
func Test_exists()
augroup myagroup
autocmd! BufEnter *.my echo "myfile edited"
@@ -334,4 +336,51 @@ func Test_exists_funcarg()
call FuncArg_Tests("arg1", "arg2")
endfunc
+" Test for using exists() with class and object variables and methods.
+func Test_exists_class_object()
+ let lines =<< trim END
+ vim9script
+ class A
+ var var1: number = 10
+ static var var2: number = 10
+ static def Foo()
+ enddef
+ def Bar()
+ enddef
+ endclass
+
+ assert_equal(1, exists("A"))
+ var a = A.new()
+ assert_equal(1, exists("a"))
+
+ assert_equal(1, exists("a.var1"))
+ assert_fails('exists("a.var2")', 'E1375: Class variable "var2" accessible only using class "A"')
+ assert_fails('exists("a.var3")', 'E1326: Variable "var3" not found in object "A"')
+ assert_equal(1, exists("A.var2"))
+ assert_fails('exists("A.var1")', 'E1376: Object variable "var1" accessible only using class "A" object')
+ assert_fails('exists("A.var3")', 'E1337: Class variable "var3" not found in class "A"')
+
+ assert_equal(1, exists("a.Bar"))
+ assert_fails('exists("a.Barz")', 'E1326: Variable "Barz" not found in object "A"')
+ assert_fails('exists("a.Foo")', 'E1326: Variable "Foo" not found in object "A"')
+ assert_equal(1, exists("A.Foo"))
+ assert_fails('exists("A.Bar")', 'E1337: Class variable "Bar" not found in class "A"')
+ assert_fails('exists("A.Barz")', 'E1337: Class variable "Barz" not found in class "A"')
+
+ def Baz()
+ assert_equal(1, exists("A"))
+ var aa = A.new()
+ assert_equal(1, exists("A.var2"))
+ assert_fails('exists("A.var1")', 'E1376: Object variable "var1" accessible only using class "A" object')
+ assert_fails('exists("A.var3")', 'E1337: Class variable "var3" not found in class "A"')
+
+ assert_equal(1, exists("A.Foo"))
+ assert_fails('exists("A.Bar")', 'E1337: Class variable "Bar" not found in class "A"')
+ assert_fails('exists("A.Barz")', 'E1337: Class variable "Barz" not found in class "A"')
+ enddef
+ Baz()
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_expand.vim b/src/testdir/test_expand.vim
index d7c393a..bebe138 100644
--- a/src/testdir/test_expand.vim
+++ b/src/testdir/test_expand.vim
@@ -51,6 +51,19 @@ func Test_expand_tilde_filename()
bwipe!
endfunc
+func Test_expand_env_pathsep()
+ let $FOO = './foo'
+ call assert_equal('./foo/bar', expand('$FOO/bar'))
+ let $FOO = './foo/'
+ call assert_equal('./foo/bar', expand('$FOO/bar'))
+ let $FOO = 'C:'
+ call assert_equal('C:/bar', expand('$FOO/bar'))
+ let $FOO = 'C:/'
+ call assert_equal('C:/bar', expand('$FOO/bar'))
+
+ unlet $FOO
+endfunc
+
func Test_expandcmd()
let $FOO = 'Test'
call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y'))
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index 6913eec..d77e5e0 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -150,6 +150,7 @@ def s:GetFilenameChecks(): dict<list<string>>
chatito: ['file.chatito'],
chill: ['file..ch'],
chordpro: ['file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'],
+ chuck: ['file.ck'],
cl: ['file.eni'],
clean: ['file.dcl', 'file.icl'],
clojure: ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc'],
@@ -188,6 +189,7 @@ def s:GetFilenameChecks(): dict<list<string>>
cynpp: ['file.cyn'],
cypher: ['file.cypher'],
d: ['file.d'],
+ dafny: ['file.dfy'],
dart: ['file.dart', 'file.drt'],
datascript: ['file.ds'],
dcd: ['file.dcd'],
@@ -213,7 +215,7 @@ def s:GetFilenameChecks(): dict<list<string>>
dracula: ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'],
dtd: ['file.dtd'],
dtrace: ['/usr/lib/dtrace/io.d'],
- dts: ['file.dts', 'file.dtsi'],
+ dts: ['file.dts', 'file.dtsi', 'file.dtso', 'file.its'],
dune: ['jbuild', 'dune', 'dune-project', 'dune-workspace'],
dylan: ['file.dylan'],
dylanintr: ['file.intr'],
@@ -281,7 +283,7 @@ def s:GetFilenameChecks(): dict<list<string>>
glsl: ['file.glsl'],
gn: ['file.gn', 'file.gni'],
gnash: ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'],
- gnuplot: ['file.gpi', '.gnuplot'],
+ gnuplot: ['file.gpi', '.gnuplot', 'file.gnuplot'],
go: ['file.go'],
gomod: ['go.mod'],
gosum: ['go.sum', 'go.work.sum'],
@@ -350,13 +352,13 @@ def s:GetFilenameChecks(): dict<list<string>>
jproperties: ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'],
json: ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.prettierrc', '.firebaserc', '.stylelintrc', 'file.slnf'],
json5: ['file.json5'],
- jsonc: ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json'],
+ jsonc: ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc'],
jsonl: ['file.jsonl'],
jsonnet: ['file.jsonnet', 'file.libsonnet'],
jsp: ['file.jsp'],
julia: ['file.jl'],
just: ['justfile', 'Justfile', '.justfile', 'config.just'],
- kconfig: ['Kconfig', 'Kconfig.debug', 'Kconfig.file'],
+ kconfig: ['Kconfig', 'Kconfig.debug', 'Kconfig.file', 'Config.in', 'Config.in.host'],
kdl: ['file.kdl'],
kivy: ['file.kv'],
kix: ['file.kix'],
@@ -620,7 +622,7 @@ def s:GetFilenameChecks(): dict<list<string>>
services: ['/etc/services', 'any/etc/services'],
setserial: ['/etc/serial.conf', 'any/etc/serial.conf'],
sexplib: ['file.sexp'],
- sh: ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'],
+ sh: ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats'],
sieve: ['file.siv', 'file.sieve'],
sil: ['file.sil'],
simula: ['file.sim'],
@@ -777,6 +779,7 @@ def s:GetFilenameChecks(): dict<list<string>>
vdmpp: ['file.vpp', 'file.vdmpp'],
vdmrt: ['file.vdmrt'],
vdmsl: ['file.vdm', 'file.vdmsl'],
+ vento: ['file.vto'],
vera: ['file.vr', 'file.vri', 'file.vrh'],
verilogams: ['file.va', 'file.vams'],
vgrindefs: ['vgrindefs'],
@@ -1602,82 +1605,89 @@ func Test_mod_file()
filetype on
" *.mod defaults to Modsim III
- call writefile(['locks like Modsim III'], 'modfile.mod')
- split modfile.mod
+ call writefile(['locks like Modsim III'], 'Xfile.mod', 'D')
+ split Xfile.mod
call assert_equal('modsim3', &filetype)
bwipe!
" Users preference set by g:filetype_mod
let g:filetype_mod = 'lprolog'
- split modfile.mod
+ split Xfile.mod
call assert_equal('lprolog', &filetype)
unlet g:filetype_mod
bwipe!
+ " LambdaProlog module
+ call writefile(['module lpromod.'], 'Xfile.mod')
+ split Xfile.mod
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
+ " LambdaProlog with comment and empty lines prior module
+ call writefile(['', '% with', '% comment', '', 'module lpromod.'], 'Xfile.mod')
+ split Xfile.mod
+ call assert_equal('lprolog', &filetype)
+ bwipe!
+
" RAPID header start with a line containing only "%%%",
" but is not always present.
- call writefile(['%%%'], 'modfile.mod')
- split modfile.mod
+ call writefile(['%%%'], 'Xfile.mod')
+ split Xfile.mod
call assert_equal('rapid', &filetype)
bwipe!
- call delete('modfile.mod')
" RAPID supports umlauts in module names, leading spaces,
" the .mod extension is not case sensitive.
- call writefile([' module ÜmlautModule'], 'modfile.Mod')
- split modfile.Mod
+ call writefile([' module ÜmlautModule'], 'Xfile.Mod', 'D')
+ split Xfile.Mod
call assert_equal('rapid', &filetype)
bwipe!
- call delete('modfile.Mod')
" RAPID is not case sensitive, embedded spaces, sysmodule,
" file starts with empty line(s).
- call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'modfile.MOD')
- split modfile.MOD
+ call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'Xfile.MOD', 'D')
+ split Xfile.MOD
call assert_equal('rapid', &filetype)
bwipe!
" Modula-2 MODULE not start of line
- call writefile(['IMPLEMENTATION MODULE Module2Mod;'], 'modfile.MOD')
- split modfile.MOD
+ call writefile(['IMPLEMENTATION MODULE Module2Mod;'], 'Xfile.mod')
+ split Xfile.mod
call assert_equal('modula2', &filetype)
call assert_equal('pim', b:modula2.dialect)
bwipe!
" Modula-2 with comment and empty lines prior MODULE
- call writefile(['', '(* with', ' comment *)', '', 'MODULE Module2Mod;'], 'modfile.MOD')
- split modfile.MOD
+ call writefile(['', '(* with', ' comment *)', '', 'MODULE Module2Mod;'], 'Xfile.mod')
+ split Xfile.mod
call assert_equal('modula2', &filetype)
call assert_equal('pim', b:modula2.dialect)
bwipe!
- call delete('modfile.MOD')
-
- " LambdaProlog module
- call writefile(['module lpromod.'], 'modfile.mod')
- split modfile.mod
- call assert_equal('lprolog', &filetype)
+ " Modula-2 program MODULE with priorty (and uppercase extension)
+ call writefile(['MODULE Module2Mod [42];'], 'Xfile.MOD')
+ split Xfile.MOD
+ call assert_equal('modula2', &filetype)
+ call assert_equal('pim', b:modula2.dialect)
bwipe!
- " LambdaProlog with comment and empty lines prior module
- call writefile(['', '% with', '% comment', '', 'module lpromod.'], 'modfile.mod')
- split modfile.mod
- call assert_equal('lprolog', &filetype)
+ " Modula-2 implementation MODULE with priorty (and uppercase extension)
+ call writefile(['IMPLEMENTATION MODULE Module2Mod [42];'], 'Xfile.MOD')
+ split Xfile.MOD
+ call assert_equal('modula2', &filetype)
+ call assert_equal('pim', b:modula2.dialect)
bwipe!
- call delete('modfile.mod')
" go.mod
- call writefile(['module example.com/M'], 'go.mod')
+ call writefile(['module example.com/M'], 'go.mod', 'D')
split go.mod
call assert_equal('gomod', &filetype)
bwipe!
- call delete('go.mod')
call writefile(['module M'], 'go.mod')
split go.mod
call assert_equal('gomod', &filetype)
bwipe!
- call delete('go.mod')
filetype off
endfunc
diff --git a/src/testdir/test_filter_map.vim b/src/testdir/test_filter_map.vim
index 6a07bec..37ebe84 100644
--- a/src/testdir/test_filter_map.vim
+++ b/src/testdir/test_filter_map.vim
@@ -14,6 +14,18 @@ func Test_filter_map_list_expr_string()
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], 'v:key * 2'))
call assert_equal([9, 9, 9, 9], map([1, 2, 3, 4], 9))
call assert_equal([7, 7, 7], map([1, 2, 3], ' 7 '))
+
+ " foreach()
+ let list01 = [1, 2, 3, 4]
+ let list02 = []
+ call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, v:val * 2)'))
+ call assert_equal([2, 4, 6, 8], list02)
+ let list02 = []
+ call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, v:key * 2)'))
+ call assert_equal([0, 2, 4, 6], list02)
+ let list02 = []
+ call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(list02, 9)'))
+ call assert_equal([9, 9, 9, 9], list02)
endfunc
" dict with expression string
@@ -29,6 +41,14 @@ func Test_filter_map_dict_expr_string()
call assert_equal({"foo": 2, "bar": 4, "baz": 6}, map(copy(dict), 'v:val * 2'))
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), 'v:key[0]'))
call assert_equal({"foo": 9, "bar": 9, "baz": 9}, map(copy(dict), 9))
+
+ " foreach()
+ let dict01 = {}
+ call assert_equal(dict, foreach(copy(dict), 'let dict01[v:key] = v:val * 2'))
+ call assert_equal({"foo": 2, "bar": 4, "baz": 6}, dict01)
+ let dict01 = {}
+ call assert_equal(dict, foreach(copy(dict), 'let dict01[v:key] = v:key[0]'))
+ call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, dict01)
endfunc
" list with funcref
@@ -54,6 +74,16 @@ func Test_filter_map_list_expr_funcref()
return a:index * 2
endfunc
call assert_equal([0, 2, 4, 6], map([1, 2, 3, 4], function('s:filter4')))
+
+ " foreach()
+ func! s:foreach1(index, val) abort
+ call add(g:test_variable, a:val + 1)
+ return [ 11, 12, 13, 14 ]
+ endfunc
+ let g:test_variable = []
+ call assert_equal([0, 1, 2, 3, 4], foreach(range(5), function('s:foreach1')))
+ call assert_equal([1, 2, 3, 4, 5], g:test_variable)
+ call remove(g:, 'test_variable')
endfunc
func Test_filter_map_nested()
@@ -90,11 +120,46 @@ func Test_filter_map_dict_expr_funcref()
return a:key[0]
endfunc
call assert_equal({"foo": "f", "bar": "b", "baz": "b"}, map(copy(dict), function('s:filter4')))
+
+ " foreach()
+ func! s:foreach1(key, val) abort
+ call extend(g:test_variable, {a:key: a:val * 2})
+ return [ 11, 12, 13, 14 ]
+ endfunc
+ let g:test_variable = {}
+ call assert_equal(dict, foreach(copy(dict), function('s:foreach1')))
+ call assert_equal({"foo": 2, "bar": 4, "baz": 6}, g:test_variable)
+ call remove(g:, 'test_variable')
+endfunc
+
+func Test_map_filter_locked()
+ let list01 = [1, 2, 3, 4]
+ lockvar 1 list01
+ call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
+ call assert_equal([2, 4, 6, 8], map(list01, 'v:val * 2'))
+ call assert_equal([1, 2, 3, 4], map(list01, 'v:val / 2'))
+ call assert_equal([2, 4, 6, 8], mapnew(list01, 'v:val * 2'))
+ let g:test_variable = []
+ call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(g:test_variable, v:val * 2)'))
+ call remove(g:, 'test_variable')
+ call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
+ unlockvar 1 list01
+ lockvar! list01
+ call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
+ call assert_fails('call map(list01, "v:val * 2")', 'E741:')
+ call assert_equal([2, 4, 6, 8], mapnew(list01, 'v:val * 2'))
+ let g:test_variable = []
+ call assert_equal([1, 2, 3, 4], foreach(list01, 'call add(g:test_variable, v:val * 2)'))
+ call assert_fails('call foreach(list01, "let list01[0] = -1")', 'E741:')
+ call assert_fails('call filter(list01, "v:val > 1")', 'E741:')
+ call remove(g:, 'test_variable')
+ unlockvar! list01
endfunc
func Test_map_filter_fails()
call assert_fails('call map([1], "42 +")', 'E15:')
call assert_fails('call filter([1], "42 +")', 'E15:')
+ call assert_fails('call foreach([1], "let a = }")', 'E15:')
call assert_fails("let l = filter([1, 2, 3], '{}')", 'E728:')
call assert_fails("let l = filter({'k' : 10}, '{}')", 'E728:')
call assert_fails("let l = filter([1, 2], {})", 'E731:')
@@ -106,6 +171,8 @@ func Test_map_filter_fails()
call assert_fails("let l = filter([1, 2], function('min'))", 'E118:')
call assert_equal([1, 2, 3], filter([1, 2, 3], test_null_partial()))
call assert_fails("let l = filter([1, 2], {a, b, c -> 1})", 'E119:')
+ call assert_fails('call foreach([1], "xyzzy")', 'E492:')
+ call assert_fails('call foreach([1], "let a = foo")', 'E121:')
endfunc
func Test_map_and_modify()
@@ -123,7 +190,7 @@ endfunc
func Test_filter_and_modify()
let l = [0]
- " cannot change the list halfway a map()
+ " cannot change the list halfway thru filter()
call assert_fails('call filter(l, "remove(l, 0)")', 'E741:')
let d = #{a: 0, b: 0, c: 0}
@@ -133,6 +200,18 @@ func Test_filter_and_modify()
call assert_fails('call filter(b, "remove(b, 0)")', 'E741:')
endfunc
+func Test_foreach_and_modify()
+ let l = [0]
+ " cannot change the list halfway thru foreach()
+ call assert_fails('call foreach(l, "let a = remove(l, 0)")', 'E741:')
+
+ let d = #{a: 0, b: 0, c: 0}
+ call assert_fails('call foreach(d, "let a = remove(d, v:key)")', 'E741:')
+
+ let b = 0z1234
+ call assert_fails('call foreach(b, "let a = remove(b, 0)")', 'E741:')
+endfunc
+
func Test_mapnew_dict()
let din = #{one: 1, two: 2}
let dout = mapnew(din, {k, v -> string(v)})
@@ -160,6 +239,36 @@ func Test_mapnew_blob()
call assert_equal(0z129956, bout)
endfunc
+func Test_foreach_blob()
+ let lines =<< trim END
+ LET g:test_variable = []
+ call assert_equal(0z0001020304, foreach(0z0001020304, 'call add(g:test_variable, v:val)'))
+ call assert_equal([0, 1, 2, 3, 4], g:test_variable)
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ func! s:foreach1(index, val) abort
+ call add(g:test_variable, a:val)
+ return [ 11, 12, 13, 14 ]
+ endfunc
+ let g:test_variable = []
+ call assert_equal(0z0001020304, foreach(0z0001020304, function('s:foreach1')))
+ call assert_equal([0, 1, 2, 3, 4], g:test_variable)
+
+ let lines =<< trim END
+ def Foreach1(_, val: any): list<number>
+ add(g:test_variable, val)
+ return [ 11, 12, 13, 14 ]
+ enddef
+ g:test_variable = []
+ assert_equal(0z0001020304, foreach(0z0001020304, Foreach1))
+ assert_equal([0, 1, 2, 3, 4], g:test_variable)
+ END
+ call v9.CheckDefSuccess(lines)
+
+ call remove(g:, 'test_variable')
+endfunc
+
" Test for using map(), filter() and mapnew() with a string
func Test_filter_map_string()
" filter()
@@ -219,6 +328,37 @@ func Test_filter_map_string()
END
call v9.CheckLegacyAndVim9Success(lines)
+ " foreach()
+ let lines =<< trim END
+ VAR s = "abc"
+ LET g:test_variable = []
+ call assert_equal(s, foreach(s, 'call add(g:test_variable, v:val)'))
+ call assert_equal(['a', 'b', 'c'], g:test_variable)
+ LET g:test_variable = []
+ LET s = 'あiうえお'
+ call assert_equal(s, foreach(s, 'call add(g:test_variable, v:val)'))
+ call assert_equal(['あ', 'i', 'う', 'え', 'お'], g:test_variable)
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+ func! s:foreach1(index, val) abort
+ call add(g:test_variable, a:val)
+ return [ 11, 12, 13, 14 ]
+ endfunc
+ let g:test_variable = []
+ call assert_equal('abcd', foreach('abcd', function('s:foreach1')))
+ call assert_equal(['a', 'b', 'c', 'd'], g:test_variable)
+ let lines =<< trim END
+ def Foreach1(_, val: string): list<number>
+ add(g:test_variable, val)
+ return [ 11, 12, 13, 14 ]
+ enddef
+ g:test_variable = []
+ assert_equal('abcd', foreach('abcd', Foreach1))
+ assert_equal(['a', 'b', 'c', 'd'], g:test_variable)
+ END
+ call v9.CheckDefSuccess(lines)
+ call remove(g:, 'test_variable')
+
let lines =<< trim END
#" map() and filter()
call assert_equal('[あ][⁈][a][😊][⁉][💕][💕][b][💕]', map(filter('あx⁈ax😊x⁉💕💕b💕x', '"x" != v:val'), '"[" .. v:val .. "]"'))
diff --git a/src/testdir/test_format.vim b/src/testdir/test_format.vim
index af364c2..785348b 100644
--- a/src/testdir/test_format.vim
+++ b/src/testdir/test_format.vim
@@ -105,67 +105,6 @@ func Test_printf_pos_misc()
END
call v9.CheckLegacyAndVim9Success(lines)
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:")
-
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:")
- call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:")
endfunc
func Test_printf_pos_float()
@@ -287,8 +226,6 @@ func Test_printf_pos_float()
call assert_equal('nan', printf('%1$S', -0.0 / 0.0))
END
call v9.CheckLegacyAndVim9Success(lines)
-
- call v9.CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:')
endfunc
func Test_printf_pos_errors()
@@ -299,6 +236,111 @@ func Test_printf_pos_errors()
call v9.CheckLegacyAndVim9Failure(['echo printf("%1$s")'], 'E1503:')
call v9.CheckLegacyAndVim9Failure(['echo printf("%1$d", 1.2)'], 'E805:')
call v9.CheckLegacyAndVim9Failure(['echo printf("%1$f")'], 'E1503:')
+
+ call v9.CheckLegacyAndVim9Failure(['echo printf("%f", "a")'], 'E807:')
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 1, 3, 4)"], "E767:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$d%d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d%d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$*1$d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$.*1$d%d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%d%2$.*1$d', 1, 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$%')"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$')"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$_')"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*d', 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*2$d', 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*.*d', 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%*.*1$d', 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*d', 3)"], "E1500:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%*1$.*1$d', 3)"], "E1500:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$d', 3, 3)"], "E1501:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$d %1$ld', 3, 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$*1$d', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %2$*1$d', 3, 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$ld', 3)"], "E1502:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*1$ld', 3)"], "E1502:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$d%2$d', 3)"], "E1503:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$d %1$s', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$s', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$ud %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$f', 3.0)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$d %1$ld', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$p %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$f %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$lud %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$llud %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$lld %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$s %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$c %1$d', 3)"], "E1504:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$ld %1$d', 3)"], "E1504:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$.2$d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%01$d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%01$0d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3.*2$d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.2$d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*3$.*2d', 3)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.5d', 5)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.1$d', 5)"], "E1505:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$1$.1$d', 5)"], "E1505:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%.123456789$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%.123456789d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789d', 5)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.123456789d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.987654321d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.987654321d', 5)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.123456789d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.5d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.987654321d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.987654321d', 5)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$5.*123456789$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$987654321.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$123456789.*987654321$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$5.*987654321$d', 5)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*123456789d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*987654321$.*1$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*123456789$.*987654321$d', 5)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%123456789$*1$.*987654321$d', 5)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$.*1$d', 5, 9999)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*1$.*2$d', 5, 9999)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$*3$.*1$d', 5, 9123, 9321)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$.*3$d', 5, 9123, 9321)"], "E1510:")
+ call v9.CheckLegacyAndVim9Failure(["call printf('%2$*1$.*3$d', 5, 9123, 9312)"], "E1510:")
+
+ call v9.CheckLegacyAndVim9Failure(["call printf('%1$*2$d', 5, 9999)"], "E1510:")
endfunc
func Test_printf_pos_64bit()
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index 85ccc78..fecebc4 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -3953,4 +3953,55 @@ func Test_glob_extended_mswin()
call delete('Xtestglob', 'rf')
endfunc
+" Tests for the slice() function.
+func Test_slice()
+ let lines =<< trim END
+ call assert_equal([1, 2, 3, 4, 5], slice(range(6), 1))
+ call assert_equal([2, 3, 4, 5], slice(range(6), 2))
+ call assert_equal([2, 3], slice(range(6), 2, 4))
+ call assert_equal([0, 1, 2, 3], slice(range(6), 0, 4))
+ call assert_equal([1, 2, 3], slice(range(6), 1, 4))
+ call assert_equal([1, 2, 3, 4], slice(range(6), 1, -1))
+ call assert_equal([1, 2], slice(range(6), 1, -3))
+ call assert_equal([1], slice(range(6), 1, -4))
+ call assert_equal([], slice(range(6), 1, -5))
+ call assert_equal([], slice(range(6), 1, -6))
+
+ call assert_equal(0z1122334455, slice(0z001122334455, 1))
+ call assert_equal(0z22334455, slice(0z001122334455, 2))
+ call assert_equal(0z2233, slice(0z001122334455, 2, 4))
+ call assert_equal(0z00112233, slice(0z001122334455, 0, 4))
+ call assert_equal(0z112233, slice(0z001122334455, 1, 4))
+ call assert_equal(0z11223344, slice(0z001122334455, 1, -1))
+ call assert_equal(0z1122, slice(0z001122334455, 1, -3))
+ call assert_equal(0z11, slice(0z001122334455, 1, -4))
+ call assert_equal(0z, slice(0z001122334455, 1, -5))
+ call assert_equal(0z, slice(0z001122334455, 1, -6))
+
+ call assert_equal('12345', slice('012345', 1))
+ call assert_equal('2345', slice('012345', 2))
+ call assert_equal('23', slice('012345', 2, 4))
+ call assert_equal('0123', slice('012345', 0, 4))
+ call assert_equal('123', slice('012345', 1, 4))
+ call assert_equal('1234', slice('012345', 1, -1))
+ call assert_equal('12', slice('012345', 1, -3))
+ call assert_equal('1', slice('012345', 1, -4))
+ call assert_equal('', slice('012345', 1, -5))
+ call assert_equal('', slice('012345', 1, -6))
+
+ #" Composing chars are treated as a part of the preceding base char.
+ call assert_equal('β̳́γ̳̂δ̳̃ε̳̄ζ̳̅', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1))
+ call assert_equal('γ̳̂δ̳̃ε̳̄ζ̳̅', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(2))
+ call assert_equal('γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(2, 4))
+ call assert_equal('ὰ̳β̳́γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(0, 4))
+ call assert_equal('β̳́γ̳̂δ̳̃', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, 4))
+ call assert_equal('β̳́γ̳̂δ̳̃ε̳̄', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -1))
+ call assert_equal('β̳́γ̳̂', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -3))
+ call assert_equal('β̳́', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -4))
+ call assert_equal('', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -5))
+ call assert_equal('', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -6))
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_goto.vim b/src/testdir/test_goto.vim
index 85c156a..357a8d2 100644
--- a/src/testdir/test_goto.vim
+++ b/src/testdir/test_goto.vim
@@ -312,18 +312,23 @@ func Test_gd_string_only()
call XTest_goto_decl('gd', lines, 5, 10)
endfunc
-" Check that setting 'cursorline' does not change curswant
-func Test_cursorline_keep_col()
+" Check that setting some options does not change curswant
+func Test_set_options_keep_col()
new
call setline(1, ['long long long line', 'short line'])
normal ggfi
let pos = getcurpos()
normal j
- set cursorline
+ set invhlsearch spell spelllang=en,cjk spelloptions=camel textwidth=80
+ set cursorline cursorcolumn cursorlineopt=line colorcolumn=+1
+ set background=dark
+ set background=light
normal k
call assert_equal(pos, getcurpos())
bwipe!
- set nocursorline
+ set hlsearch& spell& spelllang& spelloptions& textwidth&
+ set cursorline& cursorcolumn& cursorlineopt& colorcolumn&
+ set background&
endfunc
func Test_gd_local_block()
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index e7f7c6d..2ff8d34 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -111,6 +111,8 @@ func Test_getfontname_without_arg()
endfunc
func Test_getwinpos()
+ CheckX11
+
call assert_match('Window position: X \d\+, Y \d\+', execute('winpos'))
call assert_true(getwinposx() >= 0)
call assert_true(getwinposy() >= 0)
@@ -633,7 +635,7 @@ endfunc
func Test_set_guiligatures()
CheckX11BasedGui
- if has('gui_gtk') || has('gui_gtk2') || has('gui_gnome') || has('gui_gtk3')
+ if has('gui_gtk') || has('gui_gtk2') || has('gui_gnome') || has('gui_gtk3') || has('win32')
" Try correct value
set guiligatures=<>=ab
call assert_equal("<>=ab", &guiligatures)
@@ -897,7 +899,7 @@ func Test_set_term()
endfunc
func Test_windowid_variable()
- if g:x11_based_gui || has('win32')
+ if (g:x11_based_gui && empty($WAYLAND_DISPLAY)) || has('win32')
call assert_true(v:windowid > 0)
else
call assert_equal(0, v:windowid)
@@ -1708,7 +1710,11 @@ func Test_gui_lowlevel_keyevent()
" Test for <Ctrl-A> to <Ctrl-Z> keys
for kc in range(65, 90)
call SendKeys([0x11, kc])
- let ch = getcharstr()
+ try
+ let ch = getcharstr()
+ catch /^Vim:Interrupt$/
+ let ch = "\<c-c>"
+ endtry
call assert_equal(nr2char(kc - 64), ch)
endfor
diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim
index af60503..d6c9f50 100644
--- a/src/testdir/test_highlight.vim
+++ b/src/testdir/test_highlight.vim
@@ -557,22 +557,23 @@ func Test_cursorline_after_yank()
call StopVimInTerminal(buf)
endfunc
-" test for issue #4862
+" Test for issue #4862: pasting above 'cursorline' redraws properly.
func Test_put_before_cursorline()
new
only!
- call setline(1, 'A')
+ call setline(1, ['A', 'B', 'C'])
+ call cursor(2, 1)
redraw
- let std_attr = screenattr(1, 1)
+ let std_attr = screenattr(2, 1)
set cursorline
redraw
- let cul_attr = screenattr(1, 1)
+ let cul_attr = screenattr(2, 1)
normal yyP
redraw
- " Line 1 has cursor so it should be highlighted with CursorLine.
- call assert_equal(cul_attr, screenattr(1, 1))
- " And CursorLine highlighting from the second line should be gone.
- call assert_equal(std_attr, screenattr(2, 1))
+ " Line 2 has cursor so it should be highlighted with CursorLine.
+ call assert_equal(cul_attr, screenattr(2, 1))
+ " And CursorLine highlighting from line 3 should be gone.
+ call assert_equal(std_attr, screenattr(3, 1))
set nocursorline
bwipe!
endfunc
@@ -888,6 +889,16 @@ func Test_highlight_ctermul()
highlight Normal ctermul=NONE
endfunc
+" Test for 'ctermfont' in a highlight group
+func Test_highlight_ctermfont()
+ CheckNotGui
+ call assert_notmatch('ctermfont=', HighlightArgs('Normal'))
+ highlight Normal ctermfont=3
+ call assert_match('ctermfont=3', HighlightArgs('Normal'))
+ call assert_equal('3', synIDattr(synIDtrans(hlID('Normal')), 'font'))
+ highlight Normal ctermfont=NONE
+endfunc
+
" Test for specifying 'start' and 'stop' in a highlight group
func Test_highlight_start_stop()
hi HlGrp1 start=<Esc>[27h;<Esc>[<Space>r;
@@ -1314,6 +1325,7 @@ func Test_hlset()
call hlset([{'name': 'hlg11', 'ctermfg': ''}])
call hlset([{'name': 'hlg11', 'ctermbg': ''}])
call hlset([{'name': 'hlg11', 'ctermul': ''}])
+ call hlset([{'name': 'hlg11', 'ctermfont': ''}])
call hlset([{'name': 'hlg11', 'font': ''}])
call hlset([{'name': 'hlg11', 'gui': {}}])
call hlset([{'name': 'hlg11', 'guifg': ''}])
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index 61ddcd3..52306e8 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -412,6 +412,62 @@ func Test_completefunc_info()
set completefunc&
endfunc
+func CompleteInfoUserDefinedFn(findstart, query)
+ " User defined function (i_CTRL-X_CTRL-U)
+ if a:findstart
+ return col('.')
+ endif
+ return [{'word': 'foo'}, {'word': 'bar'}, {'word': 'baz'}, {'word': 'qux'}]
+endfunc
+
+func CompleteInfoTestUserDefinedFn(mvmt, idx, noselect)
+ new
+ if a:noselect
+ set completeopt=menuone,popup,noinsert,noselect
+ else
+ set completeopt=menu,preview
+ endif
+ set completefunc=CompleteInfoUserDefinedFn
+ call feedkeys("i\<C-X>\<C-U>" . a:mvmt . "\<C-R>\<C-R>=string(complete_info())\<CR>\<ESC>", "tx")
+ let completed = a:idx != -1 ? ['foo', 'bar', 'baz', 'qux']->get(a:idx) : ''
+ call assert_equal(completed. "{'pum_visible': 1, 'mode': 'function', 'selected': " . a:idx . ", 'items': [" .
+ \ "{'word': 'foo', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
+ \ "{'word': 'bar', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
+ \ "{'word': 'baz', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}, " .
+ \ "{'word': 'qux', 'menu': '', 'user_data': '', 'info': '', 'kind': '', 'abbr': ''}" .
+ \ "]}", getline(1))
+ bwipe!
+ set completeopt&
+ set completefunc&
+endfunc
+
+func Test_complete_info_user_defined_fn()
+ " forward
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>", 1, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>", 2, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>", 2, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>", 3, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-N>", -1, v:false)
+ " backward
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>", 2, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>", 1, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>", -1, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>", 3, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>", 2, v:false)
+ " forward backward
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-P>", 1, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-P>", 0, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-P>", 2, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 3, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-N>\<C-N>\<C-P>", 1, v:false)
+ " backward forward
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>\<C-N>", 0, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-N>", 2, v:true)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>\<C-N>", 1, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-P>\<C-P>\<C-N>", 3, v:false)
+ call CompleteInfoTestUserDefinedFn("\<C-P>\<C-N>\<C-N>", 1, v:false)
+endfunc
+
" Test that mouse scrolling/movement should not interrupt completion.
func Test_mouse_scroll_move_during_completion()
new
@@ -2345,7 +2401,7 @@ func Test_complete_info_index()
call assert_equal(-1, g:compl_info['selected'])
call feedkeys("Go\<C-X>\<C-N>\<C-P>\<F5>\<Esc>_dd", 'tx')
- call assert_equal(0, g:compl_info['selected'])
+ call assert_equal(5, g:compl_info['selected'])
call assert_equal(6 , len(g:compl_info['items']))
call assert_equal("fff", g:compl_info['items'][g:compl_info['selected']]['word'])
call feedkeys("Go\<C-X>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<C-N>\<F5>\<Esc>_dd", 'tx')
@@ -2359,4 +2415,18 @@ func Test_complete_info_index()
bwipe!
endfunc
-" vim: shiftwidth=2 sts=2 expandtab
+func Test_complete_changed_complete_info()
+ CheckRunVimInTerminal
+ " this used to crash vim, see #13929
+ let lines =<< trim END
+ set completeopt=menuone
+ autocmd CompleteChanged * call complete_info(['items'])
+ call feedkeys("iii\<cr>\<c-p>")
+ END
+ call writefile(lines, 'Xsegfault', 'D')
+ let buf = RunVimInTerminal('-S Xsegfault', #{rows: 5})
+ call WaitForAssert({-> assert_match('^ii', term_getline(buf, 1))}, 1000)
+ call StopVimInTerminal(buf)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/testdir/test_listchars.vim b/src/testdir/test_listchars.vim
index 57ddcda..46e96de 100644
--- a/src/testdir/test_listchars.vim
+++ b/src/testdir/test_listchars.vim
@@ -429,52 +429,52 @@ func Test_listchars_invalid()
call assert_fails('set listchars=leadmultispace', 'E474:')
" Too short
- call assert_fails('set listchars=space:', 'E474:')
- call assert_fails('set listchars=tab:x', 'E474:')
- call assert_fails('set listchars=multispace:', 'E474:')
- call assert_fails('set listchars=leadmultispace:', 'E474:')
+ call assert_fails('set listchars=space:', 'E1511:')
+ call assert_fails('set listchars=tab:x', 'E1511:')
+ call assert_fails('set listchars=multispace:', 'E1511:')
+ call assert_fails('set listchars=leadmultispace:', 'E1511:')
" One occurrence too short
- call assert_fails('set listchars=space:,space:x', 'E474:')
- call assert_fails('set listchars=space:x,space:', 'E474:')
- call assert_fails('set listchars=tab:x,tab:xx', 'E474:')
- call assert_fails('set listchars=tab:xx,tab:x', 'E474:')
- call assert_fails('set listchars=multispace:,multispace:x', 'E474:')
- call assert_fails('set listchars=multispace:x,multispace:', 'E474:')
- call assert_fails('set listchars=leadmultispace:,leadmultispace:x', 'E474:')
- call assert_fails('set listchars=leadmultispace:x,leadmultispace:', 'E474:')
+ call assert_fails('set listchars=space:x,space:', 'E1511:')
+ call assert_fails('set listchars=space:,space:x', 'E1511:')
+ call assert_fails('set listchars=tab:xx,tab:x', 'E1511:')
+ call assert_fails('set listchars=tab:x,tab:xx', 'E1511:')
+ call assert_fails('set listchars=multispace:,multispace:x', 'E1511:')
+ call assert_fails('set listchars=multispace:x,multispace:', 'E1511:')
+ call assert_fails('set listchars=leadmultispace:,leadmultispace:x', 'E1511:')
+ call assert_fails('set listchars=leadmultispace:x,leadmultispace:', 'E1511:')
" Too long
- call assert_fails('set listchars=space:xx', 'E474:')
- call assert_fails('set listchars=tab:xxxx', 'E474:')
+ call assert_fails('set listchars=space:xx', 'E1511:')
+ call assert_fails('set listchars=tab:xxxx', 'E1511:')
" Has double-width character
- call assert_fails('set listchars=space:·', 'E474:')
- call assert_fails('set listchars=tab:·x', 'E474:')
- call assert_fails('set listchars=tab:x·', 'E474:')
- call assert_fails('set listchars=tab:xx·', 'E474:')
- call assert_fails('set listchars=multispace:·', 'E474:')
- call assert_fails('set listchars=multispace:xxx·', 'E474:')
- call assert_fails('set listchars=leadmultispace:·', 'E474:')
- call assert_fails('set listchars=leadmultispace:xxx·', 'E474:')
+ call assert_fails('set listchars=space:·', 'E1512:')
+ call assert_fails('set listchars=tab:·x', 'E1512:')
+ call assert_fails('set listchars=tab:x·', 'E1512:')
+ call assert_fails('set listchars=tab:xx·', 'E1512:')
+ call assert_fails('set listchars=multispace:·', 'E1512:')
+ call assert_fails('set listchars=multispace:xxx·', 'E1512:')
+ call assert_fails('set listchars=leadmultispace:·', 'E1512:')
+ call assert_fails('set listchars=leadmultispace:xxx·', 'E1512:')
" Has control character
- call assert_fails("set listchars=space:\x01", 'E474:')
- call assert_fails("set listchars=tab:\x01x", 'E474:')
- call assert_fails("set listchars=tab:x\x01", 'E474:')
- call assert_fails("set listchars=tab:xx\x01", 'E474:')
- call assert_fails("set listchars=multispace:\x01", 'E474:')
- call assert_fails("set listchars=multispace:xxx\x01", 'E474:')
- call assert_fails('set listchars=space:\\x01', 'E474:')
- call assert_fails('set listchars=tab:\\x01x', 'E474:')
- call assert_fails('set listchars=tab:x\\x01', 'E474:')
- call assert_fails('set listchars=tab:xx\\x01', 'E474:')
- call assert_fails('set listchars=multispace:\\x01', 'E474:')
- call assert_fails('set listchars=multispace:xxx\\x01', 'E474:')
- call assert_fails("set listchars=leadmultispace:\x01", 'E474:')
- call assert_fails('set listchars=leadmultispace:\\x01', 'E474:')
- call assert_fails("set listchars=leadmultispace:xxx\x01", 'E474:')
- call assert_fails('set listchars=leadmultispace:xxx\\x01', 'E474:')
+ call assert_fails("set listchars=space:\x01", 'E1512:')
+ call assert_fails("set listchars=tab:\x01x", 'E1512:')
+ call assert_fails("set listchars=tab:x\x01", 'E1512:')
+ call assert_fails("set listchars=tab:xx\x01", 'E1512:')
+ call assert_fails("set listchars=multispace:\x01", 'E1512:')
+ call assert_fails("set listchars=multispace:xxx\x01", 'E1512:')
+ call assert_fails('set listchars=space:\\x01', 'E1512:')
+ call assert_fails('set listchars=tab:\\x01x', 'E1512:')
+ call assert_fails('set listchars=tab:x\\x01', 'E1512:')
+ call assert_fails('set listchars=tab:xx\\x01', 'E1512:')
+ call assert_fails('set listchars=multispace:\\x01', 'E1512:')
+ call assert_fails('set listchars=multispace:xxx\\x01', 'E1512:')
+ call assert_fails("set listchars=leadmultispace:\x01", 'E1512:')
+ call assert_fails('set listchars=leadmultispace:\\x01', 'E1512:')
+ call assert_fails("set listchars=leadmultispace:xxx\x01", 'E1512:')
+ call assert_fails('set listchars=leadmultispace:xxx\\x01', 'E1512:')
enew!
set ambiwidth& listchars& ff&
diff --git a/src/testdir/test_listlbr.vim b/src/testdir/test_listlbr.vim
index bcbd886..68bf948 100644
--- a/src/testdir/test_listlbr.vim
+++ b/src/testdir/test_listlbr.vim
@@ -374,13 +374,13 @@ endfunc
func Test_linebreak_no_break_after_whitespace_only()
call s:test_windows('setl ts=4 linebreak wrap')
- call setline(1, "\tabcdefghijklmnopqrstuvwxyz" ..
+ call setline(1, "\t abcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
-\ " abcdefghijklmnop",
-\ "qrstuvwxyzabcdefghij",
-\ "klmnopqrstuvwxyz ",
+\ " abcdefghijklmn",
+\ "opqrstuvwxyzabcdefgh",
+\ "ijklmnopqrstuvwxyz ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
diff --git a/src/testdir/test_listlbr_utf8.vim b/src/testdir/test_listlbr_utf8.vim
index 1bbbd2d..313ff30 100644
--- a/src/testdir/test_listlbr_utf8.vim
+++ b/src/testdir/test_listlbr_utf8.vim
@@ -9,6 +9,7 @@ CheckFeature conceal
CheckFeature signs
source view_util.vim
+source screendump.vim
func s:screen_lines(lnum, width) abort
return ScreenLines(a:lnum, a:width)
@@ -358,4 +359,24 @@ func Test_unprintable_char_on_wrap_column()
call s:close_windows()
endfunc
+" Test that Visual selection is drawn correctly when 'linebreak' is set and
+" selection ends before multibyte 'showbreak'.
+func Test_visual_ends_before_showbreak()
+ CheckScreendump
+
+ let lines =<< trim END
+ vim9script
+ &wrap = true
+ &linebreak = true
+ &showbreak = '↪ '
+ ['xxxxx ' .. 'y'->repeat(&columns - 6) .. ' zzzz']->setline(1)
+ normal! wvel
+ END
+ call writefile(lines, 'XvisualEndsBeforeShowbreak', 'D')
+ let buf = RunVimInTerminal('-S XvisualEndsBeforeShowbreak', #{rows: 6})
+ call VerifyScreenDump(buf, 'Test_visual_ends_before_showbreak', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim
index 7e72728..a99affc 100644
--- a/src/testdir/test_lua.vim
+++ b/src/testdir/test_lua.vim
@@ -28,21 +28,37 @@ func TearDown()
endfunc
" Check that switching to another buffer does not trigger ml_get error.
-func Test_lua_command_new_no_ml_get_error()
+func Test_lua_luado_change_buffer()
new
+
let wincount = winnr('$')
call setline(1, ['one', 'two', 'three'])
luado vim.command("new")
call assert_equal(wincount + 1, winnr('$'))
+
%bwipe!
endfunc
-" Test vim.command()
-func Test_lua_command()
+" Check that :luado deleting lines does not trigger ml_get error.
+func Test_lua_luado_delete_lines()
new
+
+ call setline(1, ['one', 'two', 'three'])
+ luado vim.command("%d_")
+ call assert_equal([''], getline(1, '$'))
+
call setline(1, ['one', 'two', 'three'])
luado vim.command("1,2d_")
call assert_equal(['three'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ luado vim.command("2,3d_"); return "REPLACED"
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3luado vim.command("1,2d_"); return "REPLACED"
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
endfunc
diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim
index e81173d..71d9046 100644
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -120,7 +120,7 @@ func Test_map_langmap()
unmap x
bwipe!
- " 'langnoremap' follows 'langremap' and vise versa
+ " 'langnoremap' follows 'langremap' and vice versa
set langremap
set langnoremap
call assert_equal(0, &langremap)
@@ -247,6 +247,24 @@ func Test_map_meta_multibyte()
iunmap <M-á>
endfunc
+func Test_map_super_quotes()
+ if has('gui_gtk') || has('gui_gtk3') || has("macos")
+ imap <D-"> foo
+ call feedkeys("Go-\<*D-\">-\<Esc>", "xt")
+ call assert_equal("-foo-", getline('$'))
+ set nomodified
+ iunmap <D-">
+ endif
+endfunc
+
+func Test_map_super_multibyte()
+ if has('gui_gtk') || has('gui_gtk3') || has("macos")
+ imap <D-á> foo
+ call assert_match('i <D-á>\s*foo', execute('imap'))
+ iunmap <D-á>
+ endif
+endfunc
+
func Test_abbr_after_line_join()
new
abbr foo bar
diff --git a/src/testdir/test_matchparen.vim b/src/testdir/test_matchparen.vim
index 6d09b6f..70aa38f 100644
--- a/src/testdir/test_matchparen.vim
+++ b/src/testdir/test_matchparen.vim
@@ -61,6 +61,31 @@ func Test_matchparen_clear_highlight()
call StopVimInTerminal(buf)
endfunc
+" Test for matchparen highlight when switching buffer in win_execute()
+func Test_matchparen_win_execute()
+ CheckScreendump
+
+ let lines =<< trim END
+ source $VIMRUNTIME/plugin/matchparen.vim
+ let s:win = win_getid()
+ call setline(1, '{}')
+ split
+
+ func SwitchBuf()
+ call win_execute(s:win, 'enew | buffer #')
+ endfunc
+ END
+ call writefile(lines, 'XMatchparenWinExecute', 'D')
+ let buf = RunVimInTerminal('-S XMatchparenWinExecute', #{rows: 5})
+ call VerifyScreenDump(buf, 'Test_matchparen_win_execute_1', {})
+
+ " Switching buffer away and back shouldn't change matchparen highlight.
+ call term_sendkeys(buf, ":call SwitchBuf()\<CR>:\<Esc>")
+ call VerifyScreenDump(buf, 'Test_matchparen_win_execute_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test for scrolling that modifies buffer during visual block
func Test_matchparen_pum_clear()
CheckScreendump
diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim
index 48f1bab..46f3368 100644
--- a/src/testdir/test_messages.vim
+++ b/src/testdir/test_messages.vim
@@ -162,8 +162,18 @@ func Test_echospace()
call assert_equal(&columns - 12, v:echospace)
set showcmd ruler
call assert_equal(&columns - 29, v:echospace)
+ set showcmdloc=statusline
+ call assert_equal(&columns - 19, v:echospace)
+ set showcmdloc=tabline
+ call assert_equal(&columns - 19, v:echospace)
+ call assert_fails('set showcmdloc=leap', 'E474:')
+ call assert_equal(&columns - 19, v:echospace)
+ set showcmdloc=last
+ call assert_equal(&columns - 29, v:echospace)
+ call assert_fails('set showcmdloc=jump', 'E474:')
+ call assert_equal(&columns - 29, v:echospace)
- set ruler& showcmd&
+ set ruler& showcmd& showcmdloc&
endfunc
func Test_warning_scroll()
diff --git a/src/testdir/test_mswin_event.vim b/src/testdir/test_mswin_event.vim
index 4de0169..3b7b3c7 100644
--- a/src/testdir/test_mswin_event.vim
+++ b/src/testdir/test_mswin_event.vim
@@ -36,6 +36,17 @@ func SendKey(key)
call SendKeyWithModifiers(a:key, 0)
endfunc
+" getcharstr(0) but catch Vim:Interrupt
+func Getcharstr()
+ try
+ let ch = getcharstr(0)
+ catch /^Vim:Interrupt$/
+ let ch = "\<c-c>"
+ endtry
+ return ch
+endfunc
+
+
" Send a string of individual key-press events, without modifiers.
func SendKeyStr(keystring)
for k in a:keystring
@@ -347,7 +358,7 @@ func s:LoopTestKeyArray(arr)
for [kcodes, kstr] in a:arr
" Send as a sequence of key presses.
call SendKeyGroup(kcodes)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
" need to deal a bit differently with the non-printable ascii chars < 0x20
if kstr < 0x20 && index([s:VK.CONTROL, s:VK.LCONTROL, s:VK.RCONTROL], kcodes[0]) >= 0
call assert_equal(nr2char(kstr), $"{ch}")
@@ -374,7 +385,7 @@ func s:LoopTestKeyArray(arr)
endif
endfor
call SendKeyWithModifiers(key, modifiers)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
" need to deal a bit differently with the non-printable ascii chars < 0x20
if kstr < 0x20 && index([s:VK.CONTROL, s:VK.LCONTROL, s:VK.RCONTROL], kcodes[0]) >= 0
call assert_equal(nr2char(kstr), $"{ch}")
@@ -408,10 +419,10 @@ func Test_mswin_event_character_keys()
" (0x30 - 0x39) : VK_0 - VK_9 are the same as ASCII '0' - '9'
for kc in range(48, 57)
call SendKey(kc)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc), ch)
call SendKeyWithModifiers(kc, 0)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc), ch)
endfor
@@ -437,10 +448,10 @@ func Test_mswin_event_character_keys()
" numbered 32 higher than their uppercase versions.
for kc in range(65, 90)
call SendKey(kc)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc + 32), ch)
call SendKeyWithModifiers(kc, 0)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc + 32), ch)
endfor
@@ -449,23 +460,28 @@ func Test_mswin_event_character_keys()
for modkey in [s:VK.SHIFT, s:VK.LSHIFT, s:VK.RSHIFT]
for kc in range(65, 90)
call SendKeyGroup([modkey, kc])
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc), ch)
call SendKeyWithModifiers(kc, s:MOD_MASK_SHIFT)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc), ch)
endfor
endfor
- " Test for <Ctrl-A> to <Ctrl-Z> keys
- " Expect the unicode characters 0x01 to 0x1A
+" Test for <Ctrl-A> to <Ctrl-Z> keys
+" Expect the unicode characters 0x01 to 0x1A
+" Note: Skip C because it triggers an Interrupt (CTRL-C)
+" which causes a test failure
for modkey in [s:VK.CONTROL, s:VK.LCONTROL, s:VK.RCONTROL]
for kc in range(65, 90)
+ if kc == 67
+ continue
+ endif
call SendKeyGroup([modkey, kc])
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc - 64), ch)
call SendKeyWithModifiers(kc, s:MOD_MASK_CTRL)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(kc - 64), ch)
endfor
endfor
@@ -518,7 +534,7 @@ func Test_mswin_event_function_keys()
while getchar(0)
endwhile
call SendKeyWithModifiers(111+n, vim_mod_mask)
- let ch = getcharstr(0)
+ let ch = Getcharstr()
let mod_mask = getcharmod()
call assert_equal(keycode, $"{ch}", $"key = {kstr}")
" workaround for the virtual termcap maps changing the character
@@ -590,21 +606,21 @@ func Test_mswin_event_movement_keys()
while getchar(0)
endwhile
execute 'call feedkeys("\<' .. kstr .. '>")'
- let chstr_fk = getcharstr(0)
+ let chstr_fk = Getcharstr()
call assert_equal(chstr_eval, chstr_fk, $"feedkeys = <{kstr}>")
" flush out the typeahead buffer
while getchar(0)
endwhile
call SendKey(kcode)
- let chstr_alone = getcharstr(0)
+ let chstr_alone = Getcharstr()
let chstr_alone_end = chstr_alone[len(chstr_alone)-2:len(chstr_alone)-1]
" flush out the typeahead buffer
while getchar(0)
endwhile
call SendKeyGroup(mod_keycodes + [kcode])
- let chstr_mswin = getcharstr(0)
+ let chstr_mswin = Getcharstr()
let chstr_mswin_end = chstr_mswin[len(chstr_mswin)-2:len(chstr_mswin)-1]
let mod_mask = getcharmod()
@@ -653,7 +669,7 @@ func Test_QWERTY_Ctrl_minus()
new
call SendKeyGroup([s:VK.CONTROL, s:VK.OEM_MINUS])
- let ch = getcharstr(0)
+ let ch = Getcharstr()
call assert_equal(nr2char(0x1f),ch)
call SendKey(s:VK.KEY_I)
@@ -727,14 +743,14 @@ func Test_mswin_event_mouse()
if has('gui_running')
let args = { }
let args.row = 9
- let args.col = 7
+ let args.col = 5
let args.move = 1
let args.cell = 1
call test_mswin_event("mouse", args)
call feedkeys("\<Esc>", 'Lx!')
let pos = getmousepos()
call assert_equal(9, pos.screenrow)
- call assert_equal(7, pos.screencol)
+ call assert_equal(5, pos.screencol)
let args.cell = 0
call test_mswin_event("mouse", args)
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index 38ad810..b180637 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -2235,7 +2235,7 @@ func Test_normal29_brace()
[DATA]
call assert_equal(expected, getline(1, '$'))
- " Test with { in cpooptions
+ " Test with { in cpoptions
%d
call append(0, text)
set cpo+={
@@ -2347,23 +2347,31 @@ func Test_normal30_changecase()
norm! 1ggVu
call assert_equal('this is a simple test: äüöß', getline('.'))
norm! VU
- call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.'))
+ call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖẞ', getline('.'))
norm! guu
- call assert_equal('this is a simple test: äüöss', getline('.'))
+ call assert_equal('this is a simple test: äüöß', getline('.'))
norm! gUgU
- call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.'))
+ call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖẞ', getline('.'))
norm! gugu
- call assert_equal('this is a simple test: äüöss', getline('.'))
+ call assert_equal('this is a simple test: äüöß', getline('.'))
norm! gUU
- call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖSS', getline('.'))
+ call assert_equal('THIS IS A SIMPLE TEST: ÄÜÖẞ', getline('.'))
norm! 010~
- call assert_equal('this is a SIMPLE TEST: ÄÜÖSS', getline('.'))
+ call assert_equal('this is a SIMPLE TEST: ÄÜÖẞ', getline('.'))
norm! V~
- call assert_equal('THIS IS A simple test: äüöss', getline('.'))
+ call assert_equal('THIS IS A simple test: äüöß', getline('.'))
call assert_beeps('norm! c~')
%d
call assert_beeps('norm! ~')
+ " Test with multiple lines
+ call setline(1, ['AA', 'BBBB', 'CCCCCC', 'DDDDDDDD'])
+ norm! ggguG
+ call assert_equal(['aa', 'bbbb', 'cccccc', 'dddddddd'], getline(1, '$'))
+ norm! GgUgg
+ call assert_equal(['AA', 'BBBB', 'CCCCCC', 'DDDDDDDD'], getline(1, '$'))
+ %d
+
" Test for changing case across lines using 'whichwrap'
call setline(1, ['aaaaaa', 'aaaaaa'])
normal! gg10~
@@ -3158,7 +3166,7 @@ func Test_normal50_commandline()
CheckFeature cmdline_hist
func! DoTimerWork(id)
- call assert_equal('[Command Line]', bufname(''))
+ call assert_equal(1, getbufinfo('')[0].command)
" should fail, with E11, but does fail with E23?
"call feedkeys("\<c-^>", 'tm')
diff --git a/src/testdir/test_number.vim b/src/testdir/test_number.vim
index 81f8f73..b57c1ed 100644
--- a/src/testdir/test_number.vim
+++ b/src/testdir/test_number.vim
@@ -146,8 +146,7 @@ func Test_number_with_linewrap1()
call s:close_windows()
endfunc
-" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
-func XTest_number_with_linewrap2()
+func Test_number_with_linewrap2()
call s:test_windows(3, 20)
normal! 61ia
setl number wrap
@@ -164,8 +163,7 @@ func XTest_number_with_linewrap2()
call s:close_windows()
endfunc
-" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI
-func XTest_number_with_linewrap3()
+func Test_number_with_linewrap3()
call s:test_windows(4, 20)
normal! 81ia
setl number wrap
@@ -174,7 +172,7 @@ func XTest_number_with_linewrap3()
call s:validate_cursor()
let lines = s:screen_lines(1, 4)
let expect = [
-\ "aaaaaaaa",
+\ "<<<aaaaa",
\ "aaaaaaaa",
\ "aaaaaaaa",
\ "a ",
@@ -278,9 +276,7 @@ func Test_relativenumber_colors()
[CODE]
call writefile(lines, 'XTest_relnr', 'D')
- " Check that the balloon shows up after a mouse move
let buf = RunVimInTerminal('-S XTest_relnr', {'rows': 10, 'cols': 50})
- call TermWait(buf, 50)
" Default colors
call VerifyScreenDump(buf, 'Test_relnr_colors_1', {})
@@ -297,6 +293,36 @@ func Test_relativenumber_colors()
call StopVimInTerminal(buf)
endfunc
+func Test_relativenumber_colors_wrapped()
+ CheckScreendump
+
+ let lines =<< trim [CODE]
+ set display=lastline scrolloff=0
+ call setline(1, range(200)->map('v:val->string()->repeat(40)'))
+ 111
+ set number relativenumber
+ hi LineNr ctermbg=red ctermfg=black
+ hi LineNrAbove ctermbg=blue ctermfg=black
+ hi LineNrBelow ctermbg=green ctermfg=black
+ [CODE]
+ call writefile(lines, 'XTest_relnr_wrap', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_relnr_wrap', {'rows': 20, 'cols': 50})
+
+ call VerifyScreenDump(buf, 'Test_relnr_colors_wrapped_1', {})
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_relnr_colors_wrapped_2', {})
+ call term_sendkeys(buf, "2j")
+ call VerifyScreenDump(buf, 'Test_relnr_colors_wrapped_3', {})
+ call term_sendkeys(buf, "2j")
+ call VerifyScreenDump(buf, 'Test_relnr_colors_wrapped_4', {})
+ call term_sendkeys(buf, "k")
+ call VerifyScreenDump(buf, 'Test_relnr_colors_wrapped_5', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_relativenumber_callback()
CheckScreendump
CheckFeature timers
@@ -321,6 +347,31 @@ func Test_relativenumber_callback()
call StopVimInTerminal(buf)
endfunc
+" Test that line numbers below inserted/deleted lines are updated.
+func Test_number_insert_delete_lines()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, range(1, 7))
+ set number
+ call cursor(2, 1)
+ END
+ call writefile(lines, 'Xnumber_insert_delete_lines', 'D')
+
+ let buf = RunVimInTerminal('-S Xnumber_insert_delete_lines', #{rows: 8})
+ call VerifyScreenDump(buf, 'Test_number_insert_delete_lines_1', {})
+ call term_sendkeys(buf, "dd")
+ call VerifyScreenDump(buf, 'Test_number_insert_delete_lines_2', {})
+ call term_sendkeys(buf, "P")
+ call VerifyScreenDump(buf, 'Test_number_insert_delete_lines_1', {})
+ call term_sendkeys(buf, "2dd")
+ call VerifyScreenDump(buf, 'Test_number_insert_delete_lines_3', {})
+ call term_sendkeys(buf, "P")
+ call VerifyScreenDump(buf, 'Test_number_insert_delete_lines_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test for displaying line numbers with 'rightleft'
func Test_number_rightleft()
CheckFeature rightleft
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 37dc201..fbfbaae 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -1293,6 +1293,44 @@ func Test_shortmess_F2()
call assert_fails('call test_getvalue("abc")', 'E475:')
endfunc
+func Test_shortmess_F3()
+ call writefile(['foo'], 'X_dummy', 'D')
+
+ set hidden
+ set autoread
+ e X_dummy
+ e Xotherfile
+ call assert_equal(['foo'], getbufline('X_dummy', 1, '$'))
+ set shortmess+=F
+ echo ''
+
+ if has('nanotime')
+ sleep 10m
+ else
+ sleep 2
+ endif
+ call writefile(['bar'], 'X_dummy')
+ bprev
+ call assert_equal('', Screenline(&lines))
+ call assert_equal(['bar'], getbufline('X_dummy', 1, '$'))
+
+ if has('nanotime')
+ sleep 10m
+ else
+ sleep 2
+ endif
+ call writefile(['baz'], 'X_dummy')
+ checktime
+ call assert_equal('', Screenline(&lines))
+ call assert_equal(['baz'], getbufline('X_dummy', 1, '$'))
+
+ set shortmess&
+ set autoread&
+ set hidden&
+ bwipe X_dummy
+ bwipe Xotherfile
+endfunc
+
func Test_local_scrolloff()
set so=5
set siso=7
@@ -2234,4 +2272,34 @@ func Test_set_wrap()
set wrap& smoothscroll& scrolloff&
endfunc
+func Test_delcombine()
+ new
+ set backspace=indent,eol,start
+
+ set delcombine
+ call setline(1, 'β̳̈:β̳̈')
+ normal! 0x
+ call assert_equal('β̈:β̳̈', getline(1))
+ exe "normal! A\<BS>"
+ call assert_equal('β̈:β̈', getline(1))
+ normal! 0x
+ call assert_equal('β:β̈', getline(1))
+ exe "normal! A\<BS>"
+ call assert_equal('β:β', getline(1))
+ normal! 0x
+ call assert_equal(':β', getline(1))
+ exe "normal! A\<BS>"
+ call assert_equal(':', getline(1))
+
+ set nodelcombine
+ call setline(1, 'β̳̈:β̳̈')
+ normal! 0x
+ call assert_equal(':β̳̈', getline(1))
+ exe "normal! A\<BS>"
+ call assert_equal(':', getline(1))
+
+ set backspace& delcombine&
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_perl.vim b/src/testdir/test_perl.vim
index 681aaae..721179c 100644
--- a/src/testdir/test_perl.vim
+++ b/src/testdir/test_perl.vim
@@ -211,10 +211,25 @@ func Test_perldo()
call assert_false(search('\Cperl'))
bw!
- " Check deleting lines does not trigger ml_get error.
new
+
+ " Check deleting lines does not trigger ml_get error.
call setline(1, ['one', 'two', 'three'])
perldo VIM::DoCommand("%d_")
+ call assert_equal([''], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ perldo VIM::DoCommand("1,2d_")
+ call assert_equal(['three'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ perldo VIM::DoCommand("2,3d_"); $_ = "REPLACED"
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3perldo VIM::DoCommand("1,2d_"); $_ = "REPLACED"
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
" Check a Perl expression which gives an error.
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index a0a1f74..f5cb8b2 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1139,6 +1139,10 @@ func Test_CompleteChanged()
let g:event = copy(v:event)
let g:item = get(v:event, 'completed_item', {})
let g:word = get(g:item, 'word', v:null)
+ let l:line = getline('.')
+ if g:word == v:null && l:line == "bc"
+ let g:word = l:line
+ endif
endfunction
augroup AAAAA_Group
au!
@@ -1158,10 +1162,24 @@ func Test_CompleteChanged()
call assert_equal(v:null, g:word)
call feedkeys("a\<C-N>\<C-N>\<C-N>\<C-N>\<C-P>", 'tx')
call assert_equal('foobar', g:word)
+ call feedkeys("S\<C-N>bc", 'tx')
+ call assert_equal("bc", g:word)
+
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "one"}, #{word: "two"}, #{word: "five"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt=menu,menuone
+ call feedkeys("i\<C-X>\<C-O>\<BS>\<BS>\<BS>f", 'tx')
+ call assert_equal('five', g:word)
autocmd! AAAAA_Group
set complete& completeopt&
delfunc! OnPumChange
+ delfunc! Omni_test
bw!
endfunc
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index c20ff48..a397f70 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -4215,4 +4215,22 @@ func Test_popup_close_callback_recursive()
set maxfuncdepth&
endfunc
+func Test_popupwin_setbufvar_changing_window_view()
+ " Test for Github Issue https://github.com/vim/vim/issues/13863
+ " using setbufvar(buf, '&option') should not scroll
+ " the current window
+ 20new
+ call append(0, range(1, 25))
+ setlocal scrollbind
+ norm! G
+ let topline = winsaveview()['topline']
+ call setbufvar(winbufnr(popup_atcursor(['foobar'], {})), '&syntax', 'python')
+ " close popup
+ call popup_clear()
+ call assert_equal(topline, winsaveview()['topline'])
+
+ " clean up
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2
diff --git a/src/testdir/test_prompt_buffer.vim b/src/testdir/test_prompt_buffer.vim
index d173731..c5ef010 100644
--- a/src/testdir/test_prompt_buffer.vim
+++ b/src/testdir/test_prompt_buffer.vim
@@ -297,4 +297,49 @@ func Test_prompt_appending_while_hidden()
call StopVimInTerminal(buf)
endfunc
+" Modifying a hidden buffer while leaving a prompt buffer should not prevent
+" stopping of Insert mode, and returning to the prompt buffer later should
+" restore Insert mode.
+func Test_prompt_leave_modify_hidden()
+ call CanTestPromptBuffer()
+
+ let script =<< trim END
+ file hidden
+ set bufhidden=hide
+ enew
+ new prompt
+ set buftype=prompt
+
+ inoremap <buffer> w <Cmd>wincmd w<CR>
+ inoremap <buffer> q <Cmd>bwipe!<CR>
+ autocmd BufLeave prompt call appendbufline('hidden', '$', 'Leave')
+ autocmd BufEnter prompt call appendbufline('hidden', '$', 'Enter')
+ autocmd BufWinLeave prompt call appendbufline('hidden', '$', 'Close')
+ END
+ call writefile(script, 'XpromptLeaveModifyHidden', 'D')
+
+ let buf = RunVimInTerminal('-S XpromptLeaveModifyHidden', {'rows': 10})
+ call TermWait(buf)
+
+ call term_sendkeys(buf, "a")
+ call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, "w")
+ call WaitForAssert({-> assert_notmatch('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, "\<C-W>w")
+ call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, "q")
+ call WaitForAssert({-> assert_notmatch('-- INSERT --', term_getline(buf, 10))})
+
+ call term_sendkeys(buf, ":bwipe!\<CR>")
+ call WaitForAssert({-> assert_equal('Leave', term_getline(buf, 2))})
+ call WaitForAssert({-> assert_equal('Enter', term_getline(buf, 3))})
+ call WaitForAssert({-> assert_equal('Leave', term_getline(buf, 4))})
+ call WaitForAssert({-> assert_equal('Close', term_getline(buf, 5))})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim
index 72479ac..5b5c354 100644
--- a/src/testdir/test_put.vim
+++ b/src/testdir/test_put.vim
@@ -12,6 +12,16 @@ func Test_put_block()
bwipe!
endfunc
+func Test_put_block_unicode()
+ new
+ call setreg('a', "À\nÀÀ\naaaaaaaaaaaa", "\<C-V>")
+ call setline(1, [' 1', ' 2', ' 3'])
+ exe "norm! \<C-V>jj\"ap"
+ let expected = ['À 1', 'ÀÀ 2', 'aaaaaaaaaaaa3']
+ call assert_equal(expected, getline(1, 3))
+ bw!
+endfunc
+
func Test_put_char_block()
new
call setline(1, ['Line 1', 'Line 2'])
@@ -280,5 +290,33 @@ func Test_put_in_last_displayed_line()
call StopVimInTerminal(buf)
endfunc
+func Test_put_visual_replace_whole_fold()
+ new
+ let lines = repeat(['{{{1', 'foo', 'bar', ''], 2)
+ call setline(1, lines)
+ setlocal foldmethod=marker
+ call setreg('"', 'baz')
+ call setreg('1', '')
+ normal! Vp
+ call assert_equal("{{{1\nfoo\nbar\n\n", getreg('1'))
+ call assert_equal(['baz', '{{{1', 'foo', 'bar', ''], getline(1, '$'))
+
+ bwipe!
+endfunc
+
+func Test_put_visual_replace_fold_marker()
+ new
+ let lines = repeat(['{{{1', 'foo', 'bar', ''], 4)
+ call setline(1, lines)
+ setlocal foldmethod=marker
+ normal! Gkzo
+ call setreg('"', '{{{1')
+ call setreg('1', '')
+ normal! Vp
+ call assert_equal("{{{1\n", getreg('1'))
+ call assert_equal(lines, getline(1, '$'))
+
+ bwipe!
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_python2.vim b/src/testdir/test_python2.vim
index 066b4bd..ac43e60 100644
--- a/src/testdir/test_python2.vim
+++ b/src/testdir/test_python2.vim
@@ -56,10 +56,25 @@ func Test_AAA_python_setup()
endfunc
func Test_pydo()
- " Check deleting lines does not trigger an ml_get error.
new
+
+ " Check deleting lines does not trigger an ml_get error.
call setline(1, ['one', 'two', 'three'])
pydo vim.command("%d_")
+ call assert_equal([''], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ pydo vim.command("1,2d_")
+ call assert_equal(['three'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ pydo vim.command("2,3d_"); return "REPLACED"
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3pydo vim.command("1,2d_"); return "REPLACED"
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
" Check switching to another buffer does not trigger an ml_get error.
diff --git a/src/testdir/test_python3.vim b/src/testdir/test_python3.vim
index e975cb6..7f04f11 100644
--- a/src/testdir/test_python3.vim
+++ b/src/testdir/test_python3.vim
@@ -88,10 +88,25 @@ func Test_AAA_python3_setup()
endfunc
func Test_py3do()
- " Check deleting lines does not trigger an ml_get error.
new
+
+ " Check deleting lines does not trigger an ml_get error.
call setline(1, ['one', 'two', 'three'])
py3do vim.command("%d_")
+ call assert_equal([''], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ py3do vim.command("1,2d_")
+ call assert_equal(['three'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ py3do vim.command("2,3d_"); return "REPLACED"
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3py3do vim.command("1,2d_"); return "REPLACED"
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
" Check switching to another buffer does not trigger an ml_get error.
diff --git a/src/testdir/test_quotestar.vim b/src/testdir/test_quotestar.vim
index 1d26942..322ce62 100644
--- a/src/testdir/test_quotestar.vim
+++ b/src/testdir/test_quotestar.vim
@@ -139,8 +139,8 @@ func Test_quotestar()
if has('macunix')
let skipped = Do_test_quotestar_for_macunix()
elseif has('x11')
- if empty($DISPLAY)
- let skipped = "Test can only run when $DISPLAY is set."
+ if empty($DISPLAY) || !empty($WAYLAND_DISPLAY)
+ let skipped = "Test can only run when $DISPLAY is set and $WAYLAND_DISPLAY is not set."
else
let skipped = Do_test_quotestar_for_x11()
endif
diff --git a/src/testdir/test_registers.vim b/src/testdir/test_registers.vim
index 50f3910..cb4cee7 100644
--- a/src/testdir/test_registers.vim
+++ b/src/testdir/test_registers.vim
@@ -868,6 +868,8 @@ func Test_replay_charsearch_omap()
call timer_start(10, {-> feedkeys(",bar\<Esc>q", 't')})
call feedkeys('qrct[', 'xt!')
call assert_equal(',bar[blah]', getline(1))
+ call assert_equal("ct[\<Ignore>,bar\<Esc>", @r)
+ call assert_equal('ct[<Ignore>,bar<Esc>', keytrans(@r))
undo
call assert_equal('foo[blah]', getline(1))
call feedkeys('@r', 'xt!')
@@ -949,7 +951,7 @@ endfunc
func Test_insert_small_delete_replace_mode()
new
call setline(1, ['foo', 'bar', 'foobar', 'bar'])
- let @-='foo'
+ let @- = 'foo'
call cursor(2, 1)
exe ":norm! R\<C-R>-\<C-R>-"
call assert_equal('foofoo', getline(2))
@@ -960,9 +962,21 @@ func Test_insert_small_delete_replace_mode()
exe ":norm! R\<C-R>-ZZZZ"
call assert_equal(['foo', 'foofoo', '', 'bfoobarZZZZ'], getline(1, 4))
call cursor(1, 1)
- let @-=''
+ let @- = ''
exe ":norm! R\<C-R>-ZZZ"
call assert_equal(['ZZZ', 'foofoo', '', 'bfoobarZZZZ'], getline(1, 4))
+ let @- = 'βbβ'
+ call cursor(4, 1)
+ exe ":norm! R\<C-R>-"
+ call assert_equal(['ZZZ', 'foofoo', '', 'βbβobarZZZZ'], getline(1, 4))
+ let @- = 'bβb'
+ call cursor(4, 1)
+ exe ":norm! R\<C-R>-"
+ call assert_equal(['ZZZ', 'foofoo', '', 'bβbobarZZZZ'], getline(1, 4))
+ let @- = 'βbβ'
+ call cursor(4, 1)
+ exe ":norm! R\<C-R>-"
+ call assert_equal(['ZZZ', 'foofoo', '', 'βbβobarZZZZ'], getline(1, 4))
bwipe!
endfunc
diff --git a/src/testdir/test_remote.vim b/src/testdir/test_remote.vim
new file mode 100644
index 0000000..ae931fd
--- /dev/null
+++ b/src/testdir/test_remote.vim
@@ -0,0 +1,79 @@
+" Test for the --remote functionality
+
+source check.vim
+CheckFeature clientserver
+CheckFeature terminal
+
+source shared.vim
+source screendump.vim
+source mouse.vim
+source term_util.vim
+
+let s:remote_works = 0
+let s:skip = 'Skipped: --remote feature is not possible'
+
+" nees to be run as first test to verify, that vim --servername works
+func Verify_remote_feature_works()
+ CheckRunVimInTerminal
+ enew
+ let buf = RunVimInTerminal('--servername XVIMTEST', {'rows': 8})
+ call TermWait(buf)
+ let cmd = GetVimCommandCleanTerm() .. '--serverlist'
+ call term_sendkeys(buf, ":r! " .. cmd .. "\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":w! XVimRemoteTest.txt\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":q\<CR>")
+ call StopVimInTerminal(buf)
+ bw!
+ let result = readfile('XVimRemoteTest.txt')
+ call delete('XVimRemoteTest.txt')
+ if empty(result)
+ throw s:skip
+ endif
+ let s:remote = 1
+endfunc
+
+call Verify_remote_feature_works()
+
+if !s:remote
+ finish
+endif
+
+func Test_remote_servername()
+ CheckRunVimInTerminal
+
+ " That is the file we want the server to open,
+ " despite the wildignore setting
+ call writefile(range(1, 20), 'XTEST.txt', 'D')
+ " just a dummy file, so that the ':wq' further down is successful
+ call writefile(range(1, 20), 'Xdummy.log', 'D')
+
+ " Run Vim in a terminal and open a terminal window to run Vim in.
+ let lines =<< trim END
+ set wildignore=*.txt
+ END
+ call writefile(lines, 'XRemoteEditing.vim', 'D')
+ let buf = RunVimInTerminal('--servername XVIMTEST -S XRemoteEditing.vim Xdummy.log', {'rows': 8})
+ call TermWait(buf)
+ botright new
+ " wildignore setting should be ignored and the XVIMTEST server should now
+ " open XTEST.txt, if wildignore setting is not ignored, the server
+ " will continue with the Xdummy.log file
+ let buf2 = RunVimInTerminal('--servername XVIMTEST --remote-silent XTEST.txt', {'rows': 5, 'wait_for_ruler': 0})
+ " job should be no-longer running, so we can just close it
+ exe buf2 .. 'bw!'
+ call term_sendkeys(buf, ":sil :3,$d\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":wq!\<CR>")
+ call TermWait(buf)
+ if term_getstatus(buf) == 'running'
+ call StopVimInTerminal(buf)
+ endif
+ let buf_contents = readfile('XTEST.txt')
+ call assert_equal(2, len(buf_contents))
+ bw!
+ close
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_ruby.vim b/src/testdir/test_ruby.vim
index 2e0835c..f155168 100644
--- a/src/testdir/test_ruby.vim
+++ b/src/testdir/test_ruby.vim
@@ -11,10 +11,25 @@ func Test_ruby_change_buffer()
endfunc
func Test_rubydo()
- " Check deleting lines does not trigger ml_get error.
new
+
+ " Check deleting lines does not trigger ml_get error.
call setline(1, ['one', 'two', 'three'])
rubydo Vim.command("%d_")
+ call assert_equal(['one'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ rubydo Vim.command("1,2d_")
+ call assert_equal(['one'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ rubydo Vim.command("2,3d_"); $_ = "REPLACED"
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3rubydo Vim.command("1,2d_"); $_ = "REPLACED"
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
" Check switching to another buffer does not trigger ml_get error.
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index f41f0e2..3467760 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -960,4 +960,42 @@ func Test_smoothscroll_insert_bottom()
call StopVimInTerminal(buf)
endfunc
+func Test_smoothscroll_in_zero_width_window()
+ set cpo+=n number smoothscroll
+ set winwidth=99999 winminwidth=0
+
+ vsplit
+ call assert_equal(0, winwidth(winnr('#')))
+ call win_execute(win_getid(winnr('#')), "norm! \<C-Y>")
+
+ only!
+ set winwidth& winminwidth&
+ set cpo-=n nonumber nosmoothscroll
+endfunc
+
+func Test_smoothscroll_textoff_small_winwidth()
+ set smoothscroll number
+ call setline(1, 'llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch')
+ vsplit
+
+ let textoff = getwininfo(win_getid())[0].textoff
+ execute 'vertical resize' textoff + 1
+ redraw
+ call assert_equal(0, winsaveview().skipcol)
+ execute "normal! 0\<C-E>"
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+ execute 'vertical resize' textoff - 1
+ " This caused a signed integer overflow.
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+ execute 'vertical resize' textoff
+ " This caused an infinite loop.
+ redraw
+ call assert_equal(1, winsaveview().skipcol)
+
+ %bw!
+ set smoothscroll& number&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim
index 1a81318..7c70391 100644
--- a/src/testdir/test_startup.vim
+++ b/src/testdir/test_startup.vim
@@ -518,18 +518,24 @@ func Test_geometry()
call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry")
qall
[CODE]
+ " Hide menu because gtk insists to make the window wide enough to show it completely
" Some window managers have a bar at the top that pushes windows down,
" need to use at least 130, let's do 150
- if RunVim([], after, '-f -g -geometry 31x13+41+150')
+ if RunVim(['set guioptions-=m'], after, '-f -g -geometry 31x13+41+150')
let lines = readfile('Xtest_geometry')
" Depending on the GUI library and the windowing system the final size
" might be a bit different, allow for some tolerance. Tuned based on
" actual failures.
call assert_inrange(31, 35, str2nr(lines[0]))
- call assert_equal('13', lines[1])
- call assert_equal('41', lines[2])
- call assert_equal('150', lines[3])
- call assert_equal('[41, 150]', lines[4])
+ " for some reason, the window may contain fewer lines than requested
+ " for GTK, so allow some tolerance
+ call assert_inrange(8, 13, str2nr(lines[1]))
+ " on Wayland there is no way to set or retrieve window positions
+ if empty($WAYLAND_DISPLAY)
+ call assert_equal('41', lines[2])
+ call assert_equal('150', lines[3])
+ call assert_equal('[41, 150]', lines[4])
+ endif
endif
endif
diff --git a/src/testdir/test_tabpage.vim b/src/testdir/test_tabpage.vim
index 906fefc..a531f19 100644
--- a/src/testdir/test_tabpage.vim
+++ b/src/testdir/test_tabpage.vim
@@ -156,14 +156,85 @@ func Test_tabpage_drop()
tab split f3
normal! gt
call assert_equal(1, tabpagenr())
+ tab drop f4
+ call assert_equal(1, tabpagenr('#'))
tab drop f3
- call assert_equal(3, tabpagenr())
- call assert_equal(1, tabpagenr('#'))
+ call assert_equal(4, tabpagenr())
+ call assert_equal(2, tabpagenr('#'))
+ bwipe!
bwipe!
bwipe!
bwipe!
call assert_equal(1, tabpagenr('$'))
+
+ call assert_equal(1, winnr('$'))
+ call assert_equal('', bufname(''))
+ call writefile(['L1', 'L2'], 'Xdropfile', 'D')
+
+ " Test for ':tab drop single-file': reuse current buffer
+ let expected_nr = bufnr()
+ tab drop Xdropfile
+ call assert_equal(1, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ bwipe!
+
+ " Test for ':tab drop single-file': not reuse modified buffer
+ set modified
+ let expected_nr = bufnr() + 1
+ tab drop Xdropfile
+ call assert_equal(2, tabpagenr())
+ call assert_equal(2, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ bwipe!
+
+ " Test for ':tab drop single-file': multiple tabs already exist
+ tab split f2
+ tab split f3
+ let expected_nr = bufnr() + 1
+ tab drop Xdropfile
+ call assert_equal(4, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ %bwipe!
+
+ " Test for ':tab drop multi-files': reuse current buffer
+ let expected_nr = bufnr()
+ tab drop Xdropfile f1 f2 f3
+ call assert_equal(1, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ %bwipe!
+
+ " Test for ':tab drop multi-files': not reuse modified buffer
+ set modified
+ let expected_nr = bufnr() + 1
+ tab drop Xdropfile f1 f2 f3
+ call assert_equal(2, tabpagenr())
+ call assert_equal(5, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ %bwipe!
+
+ " Test for ':tab drop multi-files': multiple tabs already exist
+ tab split f2
+ tab split f3
+ let expected_nr = bufnr() + 1
+ tab drop a b c
+ call assert_equal(4, tabpagenr())
+ call assert_equal(6, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ let expected_nr = bufnr() + 3
+ tab drop Xdropfile f1 f2 f3
+ call assert_equal(5, tabpagenr())
+ call assert_equal(8, tabpagenr('$'))
+ call assert_equal(expected_nr, bufnr())
+ call assert_equal('L2', getline(2))
+ %bwipe!
endfunc
" Test autocommands
@@ -260,14 +331,14 @@ function Test_tabpage_with_autocmd_tab_drop()
let s:li = []
tab drop test1
- call assert_equal(['BufLeave', 'BufEnter'], s:li)
+ call assert_equal(['BufEnter'], s:li)
let s:li = []
tab drop test2 test3
call assert_equal([
\ 'TabLeave', 'TabEnter', 'TabLeave', 'TabEnter',
\ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter',
- \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'], s:li)
+ \ 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter', 'BufEnter'], s:li)
autocmd! TestTabpageGroup
augroup! TestTabpageGroup
@@ -922,4 +993,21 @@ func Test_tabpage_drop_tabmove()
bwipe!
endfunc
+" Test that settabvar() shouldn't change the last accessed tabpage.
+func Test_lastused_tabpage_settabvar()
+ tabonly!
+ tabnew
+ tabnew
+ tabnew
+ call assert_equal(3, tabpagenr('#'))
+
+ call settabvar(2, 'myvar', 'tabval')
+ call assert_equal('tabval', gettabvar(2, 'myvar'))
+ call assert_equal(3, tabpagenr('#'))
+
+ bwipe!
+ bwipe!
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim
index 8b85bd6..2abf1f6 100644
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -900,18 +900,33 @@ func Test_tag_stack()
endfor
call writefile(l, 'Xfoo', 'D')
- " Jump to a tag when the tag stack is full. Oldest entry should be removed.
enew
+ " Jump to a tag when the tag stack is full. Oldest entry should be removed.
for i in range(10, 30)
exe "tag var" .. i
endfor
- let l = gettagstack()
- call assert_equal(20, l.length)
- call assert_equal('var11', l.items[0].tagname)
+ let t = gettagstack()
+ call assert_equal(20, t.length)
+ call assert_equal('var11', t.items[0].tagname)
+ let full = deepcopy(t.items)
+ tag var31
+ let t = gettagstack()
+ call assert_equal('var12', t.items[0].tagname)
+ call assert_equal('var31', t.items[19].tagname)
+
+ " Jump to a tag when the tag stack is full, but with user data this time.
+ call foreach(full, {i, item -> extend(item, {'user_data': $'udata{i}'})})
+ call settagstack(0, {'items': full})
+ let t = gettagstack()
+ call assert_equal(20, t.length)
+ call assert_equal('var11', t.items[0].tagname)
+ call assert_equal('udata0', t.items[0].user_data)
tag var31
- let l = gettagstack()
- call assert_equal('var12', l.items[0].tagname)
- call assert_equal('var31', l.items[19].tagname)
+ let t = gettagstack()
+ call assert_equal('var12', t.items[0].tagname)
+ call assert_equal('udata1', t.items[0].user_data)
+ call assert_equal('var31', t.items[19].tagname)
+ call assert_false(has_key(t.items[19], 'user_data'))
" Use tnext with a single match
call assert_fails('tnext', 'E427:')
diff --git a/src/testdir/test_tcl.vim b/src/testdir/test_tcl.vim
index 4c6537f..68a6d63 100644
--- a/src/testdir/test_tcl.vim
+++ b/src/testdir/test_tcl.vim
@@ -11,10 +11,25 @@ func TclEval(tcl_expr)
endfunc
func Test_tcldo()
- " Check deleting lines does not trigger ml_get error.
new
+
+ " Check deleting lines does not trigger ml_get error.
call setline(1, ['one', 'two', 'three'])
tcldo ::vim::command %d_
+ call assert_equal(['one'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ tcldo ::vim::command 1,2d_
+ call assert_equal(['one'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ tcldo ::vim::command 2,3d_ ; set line REPLACED
+ call assert_equal(['REPLACED'], getline(1, '$'))
+
+ call setline(1, ['one', 'two', 'three'])
+ 2,3tcldo ::vim::command 1,2d_ ; set line REPLACED
+ call assert_equal(['three'], getline(1, '$'))
+
bwipe!
" Check that switching to another buffer does not trigger ml_get error.
diff --git a/src/testdir/test_terminal2.vim b/src/testdir/test_terminal2.vim
index 9798c51..05b228e 100644
--- a/src/testdir/test_terminal2.vim
+++ b/src/testdir/test_terminal2.vim
@@ -535,6 +535,7 @@ func Test_term_getcursor()
endfunc
" Test for term_gettitle()
+" Known to be flaky on Mac-OS X and the GH runners
func Test_term_gettitle()
" term_gettitle() returns an empty string for a non-terminal buffer
" and for a non-existing buffer.
@@ -544,6 +545,13 @@ func Test_term_gettitle()
if !has('title') || empty(&t_ts)
throw "Skipped: can't get/set title"
endif
+ if has('osx') && !empty($CI) && system('uname -m') =~# 'arm64'
+ " This test often fails with the following error message on Github runners
+ " MacOS-14
+ " '^\\[No Name\\] - VIM\\d*$' does not match 'e] - VIM'
+ " Why? Is the terminal that runs Vim too small?
+ throw 'Skipped: FIXME: Running this test on M1 Mac fails on GitHub Actions'
+ endif
let term = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile', '-c', 'set title'])
call TermWait(term)
diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim
index 97fea60..a06f733 100644
--- a/src/testdir/test_textprop.vim
+++ b/src/testdir/test_textprop.vim
@@ -3075,10 +3075,17 @@ func Test_prop_with_text_above_below_empty()
let vt = 'test'
call prop_type_add(vt, {'highlight': 'ToDo'})
for ln in range(1, line('$'))
- call prop_add(ln, 0, {'type': vt, 'text': '---', 'text_align': 'above'})
- call prop_add(ln, 0, {'type': vt, 'text': '+++', 'text_align': 'below'})
+ " use 1 character text to test for off-by-one regressions
+ call prop_add(ln, 0, {'type': vt, 'text': '-', 'text_align': 'above'})
+ call prop_add(ln, 0, {'type': vt, 'text': '+', 'text_align': 'below'})
endfor
normal G
+
+ func AddMore()
+ call prop_add(5, 0, {'type': g:vt, 'text': '!', 'text_align': 'above'})
+ call prop_add(5, 0, {'type': g:vt, 'text': '!', 'text_align': 'above'})
+ call prop_add(5, 0, {'type': g:vt, 'text': '!', 'text_align': 'above'})
+ endfunc
END
call writefile(lines, 'XscriptPropAboveBelowEmpty', 'D')
let buf = RunVimInTerminal('-S XscriptPropAboveBelowEmpty', #{rows: 16, cols: 60})
@@ -3100,6 +3107,12 @@ func Test_prop_with_text_above_below_empty()
call term_sendkeys(buf, "kk")
call VerifyScreenDump(buf, 'Test_prop_above_below_empty_5', {})
+ " This was drawing line number over cmdline and leaking memory.
+ call term_sendkeys(buf, ":call AddMore()\<CR>")
+ call term_sendkeys(buf, "gg")
+ call term_sendkeys(buf, "j")
+ call VerifyScreenDump(buf, 'Test_prop_above_below_empty_6', {})
+
call StopVimInTerminal(buf)
endfunc
@@ -4090,6 +4103,46 @@ func Test_text_below_nowrap()
call StopVimInTerminal(buf)
endfunc
+func Test_virtual_text_overlap_with_highlight()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ vim9script
+ setline(1, ['one', 'two', 'three', 'four', 'five'])
+ set number
+
+ prop_type_add('demo_highlight_warning', {highlight: 'WarningMsg'})
+ prop_type_add('demo_virtual_text_error', {highlight: 'Error'})
+
+ prop_add(2, 4, {
+ type: 'demo_highlight_warning',
+ end_col: 4,
+ })
+ prop_add(2, 0, {
+ type: 'demo_virtual_text_error',
+ text: 'syntax error',
+ text_align: 'below',
+ })
+ normal 2j
+
+ prop_add(4, 4, {
+ type: 'demo_highlight_warning',
+ end_lnum: 5,
+ end_col: 1,
+ })
+ prop_add(4, 0, {
+ type: 'demo_virtual_text_error',
+ text: 'other error',
+ text_align: 'right',
+ })
+ END
+ call writefile(lines, 'XVirtualTextOverlapWithHighlight', 'D')
+ let buf = RunVimInTerminal('-S XVirtualTextOverlapWithHighlight', #{rows: 8, cols: 60})
+ call VerifyScreenDump(buf, 'Test_virtual_text_overlap_with_highlight_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_virtual_text_in_popup_highlight()
CheckRunVimInTerminal
@@ -4425,4 +4478,81 @@ func Test_textprop_notype_join()
bwipe!
endfunc
+" This was causing text property corruption.
+func Test_textprop_backspace_fo_aw()
+ new
+ call setline(1, 'foobar')
+ call prop_type_add('test', {'highlight': 'ErrorMsg'})
+ call prop_add(1, 1, {'type': 'test', 'length': 3})
+ set backspace=indent,eol,start
+ setlocal formatoptions+=aw
+ call feedkeys("A \<CR>\<BS>\<Esc>", 'tx')
+ call assert_equal('foobar', getline(1))
+ call assert_equal([
+ \ #{id: 0, col: 1, start: 1, end: 1, type_bufnr: 0,
+ \ type: 'test', length: 3}], prop_list(1))
+
+ bwipe!
+ set backspace&
+ call prop_type_delete('test')
+endfunc
+
+func Test_textprop_with_wincolor()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ call setline(1, 'some text here')
+ call setline(2, 'some much longer text here')
+ call setline(3, 'more text here')
+ call prop_type_add('afterprop', #{highlight: 'Search'})
+ call prop_type_add('belowprop', #{highlight: 'DiffAdd'})
+ call prop_add(3, 0, #{type: 'afterprop', text: 'AFTER',
+ \ text_align: 'after', text_padding_left: 3})
+ call prop_add(1, 0, #{type: 'belowprop', text: 'BELOW',
+ \ text_align: 'below', text_padding_left: 3})
+ set wincolor=DiffChange wrap
+ END
+ call writefile(lines, 'XtextPropWincolor', 'D')
+ let buf = RunVimInTerminal('-S XtextPropWincolor', #{rows: 8, cols: 60})
+
+ call term_sendkeys(buf, ":\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_1', {})
+
+ call term_sendkeys(buf, ":set cursorline\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_2', {})
+
+ call term_sendkeys(buf, ":set nowrap\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_2', {})
+
+ call term_sendkeys(buf, ":set nocursorline\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_1', {})
+
+ call term_sendkeys(buf, ":set cursorline colorcolumn=30\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_3', {})
+
+ call term_sendkeys(buf, ":hi CursorLine ctermbg=Brown\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_4', {})
+
+ call term_sendkeys(buf, ":set cursorcolumn\<CR>:\<CR>")
+ call term_sendkeys(buf, '$')
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_5', {})
+
+ call term_sendkeys(buf, 'j')
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_6', {})
+
+ call term_sendkeys(buf, ":set virtualedit=all\<CR>:\<CR>")
+ call term_sendkeys(buf, 'l')
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_7', {})
+
+ call term_sendkeys(buf, 'k')
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_8', {})
+
+ if has('rightleft')
+ call term_sendkeys(buf, ":set rightleft\<CR>:\<CR>")
+ call VerifyScreenDump(buf, 'Test_prop_wincolor_9', {})
+ endif
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim
index 13c4990..eec0e0b 100644
--- a/src/testdir/test_undo.vim
+++ b/src/testdir/test_undo.vim
@@ -583,7 +583,7 @@ funct Test_undofile()
endif
call assert_equal('', undofile(''))
- " Test undofile() with 'undodir' set to to an existing directory.
+ " Test undofile() with 'undodir' set to an existing directory.
call mkdir('Xundodir')
set undodir=Xundodir
let cwd = getcwd()
diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim
index 57a093c..992329b 100644
--- a/src/testdir/test_user_func.vim
+++ b/src/testdir/test_user_func.vim
@@ -968,4 +968,23 @@ func Test_multidefer_with_exception()
delfunc Foo
endfunc
+func Test_func_curly_brace_invalid_name()
+ func Fail()
+ func Foo{'()'}bar()
+ endfunc
+ endfunc
+
+ call assert_fails('call Fail()', 'E475: Invalid argument: Foo()bar')
+
+ silent! call Fail()
+ call assert_equal([], getcompletion('Foo', 'function'))
+
+ set formatexpr=Fail()
+ normal! gqq
+ call assert_equal([], getcompletion('Foo', 'function'))
+
+ set formatexpr&
+ delfunc Fail
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_utf8_comparisons.vim b/src/testdir/test_utf8_comparisons.vim
index 3431226..2c1972b 100644
--- a/src/testdir/test_utf8_comparisons.vim
+++ b/src/testdir/test_utf8_comparisons.vim
@@ -93,4 +93,39 @@ func Test_gap()
call assert_equal(["ABCD", "", "defg"], getline(1,3))
endfunc
+" test that g~, ~ and gU correctly upper-cases ß
+func Test_uppercase_sharp_ss()
+ new
+ call setline(1, repeat(['ß'], 4))
+
+ call cursor(1, 1)
+ norm! ~
+ call assert_equal('ẞ', getline(line('.')))
+ norm! ~
+ call assert_equal('ß', getline(line('.')))
+
+ call cursor(2, 1)
+ norm! g~l
+ call assert_equal('ẞ', getline(line('.')))
+ norm! g~l
+ call assert_equal('ß', getline(line('.')))
+
+ call cursor(3, 1)
+ norm! gUl
+ call assert_equal('ẞ', getline(line('.')))
+ norm! vgU
+ call assert_equal('ẞ', getline(line('.')))
+ norm! vgu
+ call assert_equal('ß', getline(line('.')))
+ norm! gul
+ call assert_equal('ß', getline(line('.')))
+
+ call cursor(4, 1)
+ norm! vgU
+ call assert_equal('ẞ', getline(line('.')))
+ norm! vgu
+ call assert_equal('ß', getline(line('.')))
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index aef09c5..4414f55 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -3484,4 +3484,101 @@ def Test_assign_type_to_list_dict()
v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value')
enddef
+" Test for modifying a final variable using a compound operator
+def Test_final_var_modification_with_compound_op()
+ var lines =<< trim END
+ vim9script
+
+ final i: number = 1000
+ assert_fails('i += 2', 'E46: Cannot change read-only variable "i"')
+ assert_fails('i -= 2', 'E46: Cannot change read-only variable "i"')
+ assert_fails('i *= 2', 'E46: Cannot change read-only variable "i"')
+ assert_fails('i /= 2', 'E46: Cannot change read-only variable "i"')
+ assert_fails('i %= 2', 'E46: Cannot change read-only variable "i"')
+ assert_equal(1000, i)
+
+ final f: float = 1000.0
+ assert_fails('f += 2', 'E46: Cannot change read-only variable "f"')
+ assert_fails('f -= 2', 'E46: Cannot change read-only variable "f"')
+ assert_fails('f *= 2', 'E46: Cannot change read-only variable "f"')
+ assert_fails('f /= 2', 'E46: Cannot change read-only variable "f"')
+ assert_equal(1000.0, f)
+
+ final s: string = 'abc'
+ assert_fails('s ..= "y"', 'E46: Cannot change read-only variable "s"')
+ assert_equal('abc', s)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for modifying a final variable with a List value
+def Test_final_var_with_list_value()
+ var lines =<< trim END
+ vim9script
+
+ final listA: list<string> = []
+ var listB = listA
+
+ listB->add('a')
+ assert_true(listA is listB)
+ assert_equal(['a'], listA)
+ assert_equal(['a'], listB)
+
+ listB += ['b']
+ assert_true(listA is listB)
+ assert_equal(['a', 'b'], listA)
+ assert_equal(['a', 'b'], listB)
+
+ listA->add('c')
+ assert_true(listA is listB)
+ assert_equal(['a', 'b', 'c'], listA)
+ assert_equal(['a', 'b', 'c'], listB)
+
+ listA += ['d']
+ assert_true(listA is listB)
+ assert_equal(['a', 'b', 'c', 'd'], listA)
+ assert_equal(['a', 'b', 'c', 'd'], listB)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for modifying a final variable with a List value using "+=" from a legacy
+" function.
+func Test_final_var_with_list_value_legacy()
+ vim9cmd final g:TestVar = ['a']
+ vim9cmd g:TestVar += ['b']
+ call assert_equal(['a', 'b'], g:TestVar)
+endfunc
+
+" Test for modifying a final variable with a Blob value
+def Test_final_var_with_blob_value()
+ var lines =<< trim END
+ vim9script
+
+ final blobA: blob = 0z10
+ var blobB = blobA
+
+ blobB->add(32)
+ assert_true(blobA is blobB)
+ assert_equal(0z1020, blobA)
+ assert_equal(0z1020, blobB)
+
+ blobB += 0z30
+ assert_true(blobA is blobB)
+ assert_equal(0z102030, blobA)
+ assert_equal(0z102030, blobB)
+
+ blobA->add(64)
+ assert_true(blobA is blobB)
+ assert_equal(0z10203040, blobA)
+ assert_equal(0z10203040, blobB)
+
+ blobA += 0z50
+ assert_true(blobA is blobB)
+ assert_equal(0z1020304050, blobA)
+ assert_equal(0z1020304050, blobB)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 9af0d07..442d375 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -3409,7 +3409,7 @@ def Test_remote_foreground()
CheckFeature clientserver
# remote_foreground() doesn't fail on MS-Windows
CheckNotMSWindows
- CheckEnv DISPLAY
+ CheckX11
v9.CheckDefAndScriptFailure(['remote_foreground(10)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
assert_fails('remote_foreground("NonExistingServer")', 'E241:')
@@ -4101,30 +4101,10 @@ def Test_simplify()
enddef
def Test_slice()
- assert_equal('12345', slice('012345', 1))
- assert_equal('123', slice('012345', 1, 4))
- assert_equal('1234', slice('012345', 1, -1))
- assert_equal('1', slice('012345', 1, -4))
- assert_equal('', slice('012345', 1, -5))
- assert_equal('', slice('012345', 1, -6))
-
- assert_equal([1, 2, 3, 4, 5], slice(range(6), 1))
- assert_equal([1, 2, 3], slice(range(6), 1, 4))
- assert_equal([1, 2, 3, 4], slice(range(6), 1, -1))
- assert_equal([1], slice(range(6), 1, -4))
- assert_equal([], slice(range(6), 1, -5))
- assert_equal([], slice(range(6), 1, -6))
-
var lds: list<dict<string>> = [{key: 'value'}]
assert_equal(['val'], lds->slice(0, 1)->map((_, v) => 'val'))
assert_equal(['val'], lds[ : ]->map((_, v) => 'val'))
- assert_equal(0z1122334455, slice(0z001122334455, 1))
- assert_equal(0z112233, slice(0z001122334455, 1, 4))
- assert_equal(0z11223344, slice(0z001122334455, 1, -1))
- assert_equal(0z11, slice(0z001122334455, 1, -4))
- assert_equal(0z, slice(0z001122334455, 1, -5))
- assert_equal(0z, slice(0z001122334455, 1, -6))
v9.CheckDefAndScriptFailure(['slice({"a": 10}, 1)'], ['E1013: Argument 1: type mismatch, expected list<any> but got dict<number>', 'E1211: List required for argument 1'])
v9.CheckDefAndScriptFailure(['slice([1, 2, 3], "b")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2'])
v9.CheckDefAndScriptFailure(['slice("abc", 1, "c")'], ['E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3'])
@@ -5197,4 +5177,12 @@ def Test_passing_type_to_builtin()
v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value')
enddef
+def Test_getregion()
+ assert_equal(['x'], getregion(getpos('.'), getpos('.'))->map((_, _) => 'x'))
+
+ v9.CheckDefAndScriptFailure(['getregion(10, getpos("."))'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1211: List required for argument 1'])
+ assert_equal([''], getregion(getpos('.'), getpos('.')))
+ v9.CheckDefExecFailure(['getregion(getpos("a"), getpos("."))'], 'E1209:')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index b34d2ad..d6c55bf 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -67,7 +67,23 @@ def Test_class_basic()
END
v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
- # Use old "this." prefixed member variable declaration syntax (without intialization)
+ # Try to define a class with the same name as an existing variable
+ lines =<< trim END
+ vim9script
+ var Something: list<number> = [1]
+ class Thing
+ endclass
+ interface Api
+ endinterface
+ class Something extends Thing implements Api
+ var v1: string = ''
+ def Foo()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 7)
+
+ # Use old "this." prefixed member variable declaration syntax (without initialization)
lines =<< trim END
vim9script
class Something
@@ -76,7 +92,7 @@ def Test_class_basic()
END
v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: this.count: number', 3)
- # Use old "this." prefixed member variable declaration syntax (with intialization)
+ # Use old "this." prefixed member variable declaration syntax (with initialization)
lines =<< trim END
vim9script
class Something
@@ -9659,31 +9675,749 @@ def Test_const_class_object_variable()
v9.CheckSourceFailure(lines, 'E1022: Type or initialization required', 3)
enddef
-" Test for using double underscore prefix in a class/object method name.
-def Test_method_double_underscore_prefix()
- # class method
+" Test for compiling class/object methods using :defcompile
+def Test_defcompile_class()
+ # defcompile all the classes in the current script
var lines =<< trim END
vim9script
class A
- static def __foo()
- echo "foo"
+ def Foo()
+ var i = 10
+ enddef
+ endclass
+ class B
+ def Bar()
+ var i = 20
+ xxx
enddef
endclass
defcompile
END
- v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: xxx', 2)
- # object method
+ # defcompile a specific class
lines =<< trim END
vim9script
class A
- def __foo()
- echo "foo"
+ def Foo()
+ xxx
+ enddef
+ endclass
+ class B
+ def Bar()
+ yyy
+ enddef
+ endclass
+ defcompile B
+ END
+ v9.CheckSourceFailure(lines, 'E476: Invalid command: yyy', 1)
+
+ # defcompile a non-class
+ lines =<< trim END
+ vim9script
+ class A
+ def Foo()
+ enddef
+ endclass
+ var X: list<number> = []
+ defcompile X
+ END
+ v9.CheckSourceFailure(lines, 'E1061: Cannot find function X', 7)
+
+ # defcompile a class twice
+ lines =<< trim END
+ vim9script
+ class A
+ def new()
+ enddef
+ endclass
+ defcompile A
+ defcompile A
+ assert_equal('Function A.new does not need compiling', v:statusmsg)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # defcompile should not compile an imported class
+ lines =<< trim END
+ vim9script
+ export class A
+ def Foo()
+ xxx
enddef
endclass
+ END
+ writefile(lines, 'Xdefcompileimport.vim', 'D')
+ lines =<< trim END
+ vim9script
+
+ import './Xdefcompileimport.vim'
+ class B
+ endclass
defcompile
END
- v9.CheckSourceFailure(lines, 'E1034: Cannot use reserved name __foo()', 3)
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for cases common to all the object builtin methods
+def Test_object_builtin_method()
+ var lines =<< trim END
+ vim9script
+ class A
+ def abc()
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: abc()', 3)
+
+ for funcname in ["len", "string", "empty"]
+ lines =<< trim eval END
+ vim9script
+ class A
+ static def {funcname}(): number
+ enddef
+ endclass
+ END
+ v9.CheckSourceFailure(lines, 'E1413: Builtin class method not supported', 3)
+ endfor
+enddef
+
+" Test for using the empty() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_empty()
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(true, empty(afoo))
+ assert_equal(true, afoo->empty())
+ enddef
+
+ var a = A.new()
+ assert_equal(1, empty(a))
+ assert_equal(1, a->empty())
+ test_garbagecollect_now()
+ assert_equal(1, empty(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " empty() should return 1 without a builtin method
+ let lines =<< trim END
+ vim9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(1, empty(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal(1, empty(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the empty() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "empty": type mismatch, expected func(): bool but got func()', 4)
+
+ " Error when calling the empty() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ throw "Failed to check emptiness"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = empty(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('empty(a)', 'Failed to check emptiness')
+ assert_fails('Foo()', 'Failed to check emptiness')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using an object from a script
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal(true, afoo.empty())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using an object from a method
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal(true, afoo.empty())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call empty() using "this" from an object method
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ def Foo(): bool
+ return this.empty()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal(true, abar.Foo())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call empty() from a derived object
+ let lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return false
+ enddef
+ endclass
+ class B extends A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal(true, empty(afoo))
+ var bfoo = B.new()
+ assert_equal(true, empty(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal(1, empty(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking empty method using an interface
+ let lines =<< trim END
+ vim9script
+ interface A
+ def empty(): bool
+ endinterface
+ class B implements A
+ def empty(): bool
+ return false
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal(false, empty(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using the len() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_length()
+ let lines =<< trim END
+ vim9script
+ class A
+ var mylen: number = 0
+ def new(n: number)
+ this.mylen = n
+ enddef
+ def len(): number
+ return this.mylen
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new(12)
+ assert_equal(12, len(afoo))
+ assert_equal(12, afoo->len())
+ enddef
+
+ var a = A.new(22)
+ assert_equal(22, len(a))
+ assert_equal(22, a->len())
+ test_garbagecollect_now()
+ assert_equal(22, len(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " len() should return 0 without a builtin method
+ let lines =<< trim END
+ vim9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal(0, len(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal(0, len(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the len() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def len()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "len": type mismatch, expected func(): number but got func()', 4)
+
+ " Error when calling the len() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ throw "Failed to compute length"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = len(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('len(a)', 'Failed to compute length')
+ assert_fails('Foo()', 'Failed to compute length')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using an object from a script
+ let lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ return 5
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal(5, afoo.len())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using an object from a method
+ let lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ return 5
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal(5, afoo.len())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call len() using "this" from an object method
+ let lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ return 8
+ enddef
+ def Foo(): number
+ return this.len()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal(8, abar.Foo())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call len() from a derived object
+ let lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ return 10
+ enddef
+ endclass
+ class B extends A
+ def len(): number
+ return 20
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal(20, len(afoo))
+ var bfoo = B.new()
+ assert_equal(20, len(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal(20, len(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking len method using an interface
+ let lines =<< trim END
+ vim9script
+ interface A
+ def len(): number
+ endinterface
+ class B implements A
+ def len(): number
+ return 123
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal(123, len(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using the string() builtin method with an object
+" This is a legacy function to use the test_garbagecollect_now() function.
+func Test_object_string()
+ let lines =<< trim END
+ vim9script
+ class A
+ var name: string
+ def string(): string
+ return this.name
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new("foo-A")
+ assert_equal('foo-A', string(afoo))
+ assert_equal('foo-A', afoo->string())
+ enddef
+
+ var a = A.new("script-A")
+ assert_equal('script-A', string(a))
+ assert_equal('script-A', a->string())
+ assert_equal(['script-A'], execute('echo a')->split("\n"))
+ test_garbagecollect_now()
+ assert_equal('script-A', string(a))
+ Foo()
+ test_garbagecollect_now()
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " string() should return "object of A {}" without a builtin method
+ let lines =<< trim END
+ vim9script
+ class A
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ assert_equal('object of A {}', string(afoo))
+ enddef
+
+ var a = A.new()
+ assert_equal('object of A {}', string(a))
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Unsupported signature for the string() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def string()
+ enddef
+ endclass
+ END
+ call v9.CheckSourceFailure(lines, 'E1383: Method "string": type mismatch, expected func(): string but got func()', 4)
+
+ " Error when calling the string() method
+ let lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ throw "Failed to get text"
+ enddef
+ endclass
+
+ def Foo()
+ var afoo = A.new()
+ var i = string(afoo)
+ enddef
+
+ var a = A.new()
+ assert_fails('string(a)', 'Failed to get text')
+ assert_fails('Foo()', 'Failed to get text')
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using an object from a script
+ let lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ var afoo = A.new()
+ assert_equal('A', afoo.string())
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using an object from a method
+ let lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ def Foo()
+ var afoo = A.new()
+ assert_equal('A', afoo.string())
+ enddef
+ Foo()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " call string() using "this" from an object method
+ let lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ def Foo(): string
+ return this.string()
+ enddef
+ endclass
+ def Bar()
+ var abar = A.new()
+ assert_equal('A', abar.string())
+ enddef
+ Bar()
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Call string() from a derived object
+ let lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ class B extends A
+ def string(): string
+ return 'B'
+ enddef
+ endclass
+ def Foo(afoo: A)
+ assert_equal('B', string(afoo))
+ var bfoo = B.new()
+ assert_equal('B', string(bfoo))
+ enddef
+ var b = B.new()
+ assert_equal('B', string(b))
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+
+ " Invoking string method using an interface
+ let lines =<< trim END
+ vim9script
+ interface A
+ def string(): string
+ endinterface
+ class B implements A
+ def string(): string
+ return 'B'
+ enddef
+ endclass
+ def Foo(a: A)
+ assert_equal('B', string(a))
+ enddef
+ var b = B.new()
+ Foo(b)
+ END
+ call v9.CheckSourceSuccess(lines)
+endfunc
+
+" Test for using a class in the class definition
+def Test_Ref_Class_Within_Same_Class()
+ var lines =<< trim END
+ vim9script
+ class A
+ var n: number = 0
+ def Equals(other: A): bool
+ return this.n == other.n
+ enddef
+ endclass
+
+ var a1 = A.new(10)
+ var a2 = A.new(10)
+ var a3 = A.new(20)
+ assert_equal(true, a1.Equals(a2))
+ assert_equal(false, a2.Equals(a3))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+
+ class Foo
+ var num: number
+ def Clone(): Foo
+ return Foo.new(this.num)
+ enddef
+ endclass
+
+ var f1 = Foo.new(1)
+
+ def F()
+ var f2: Foo = f1.Clone()
+ assert_equal(false, f2 is f1)
+ assert_equal(true, f2.num == f1.num)
+ enddef
+ F()
+
+ var f3: Foo = f1.Clone()
+ assert_equal(false, f3 is f1)
+ assert_equal(true, f3.num == f1.num)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Test for trying to use a class to extend when defining the same class
+ lines =<< trim END
+ vim9script
+ class A extends A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1354: Cannot extend A', 3)
+
+ # Test for trying to use a class to implement when defining the same class
+ lines =<< trim END
+ vim9script
+ class A implements A
+ endclass
+ END
+ v9.CheckScriptFailure(lines, 'E1347: Not a valid interface: A', 3)
+enddef
+
+" Test for using a compound operator from a lambda function in an object method
+def Test_compound_op_in_objmethod_lambda()
+ # Test using the "+=" operator
+ var lines =<< trim END
+ vim9script
+ class A
+ var n: number = 10
+ def Foo()
+ var Fn = () => {
+ this.n += 1
+ }
+ Fn()
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ assert_equal(11, a.n)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ # Test using the "..=" operator
+ lines =<< trim END
+ vim9script
+ class A
+ var s: string = "a"
+ def Foo()
+ var Fn = () => {
+ this.s ..= "a"
+ }
+ Fn()
+ enddef
+ endclass
+
+ var a = A.new()
+ a.Foo()
+ a.Foo()
+ assert_equal("aaa", a.s)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" call a lambda function in one object from another object
+def Test_lambda_invocation_across_classes()
+ var lines =<< trim END
+ vim9script
+ class A
+ var s: string = "foo"
+ def GetFn(): func
+ var Fn = (): string => {
+ return this.s
+ }
+ return Fn
+ enddef
+ endclass
+
+ class B
+ var s: string = "bar"
+ def GetFn(): func
+ var a = A.new()
+ return a.GetFn()
+ enddef
+ endclass
+
+ var b = B.new()
+ var Fn = b.GetFn()
+ assert_equal("foo", Fn())
+ END
+ v9.CheckScriptSuccess(lines)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 1a192cc..1daef22 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -3273,4 +3273,199 @@ def Test_funcref_with_class()
unlet g:instr
enddef
+" Disassemble instructions for calls to a string() function in an object
+def Test_disassemble_object_string()
+ var lines =<< trim END
+ vim9script
+ class A
+ def string(): string
+ return 'A'
+ enddef
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = string(a)
+ s = string(A)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = string(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 METHODCALL A.string(argc 0)\_s*' ..
+ '4 STORE $1\_s*' ..
+ 's = string(A)\_s*' ..
+ '5 LOADSCRIPT A-0 from .*\_s*' ..
+ '6 BCALL string(argc 1)\_s*' ..
+ '7 STORE $1\_s*' ..
+ '8 RETURN void', g:instr)
+ unlet g:instr
+
+ # Use the default string() function for a class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = string(a)
+ s = string(A)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = string(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 BCALL string(argc 1)\_s*' ..
+ '4 STORE $1\_s*' ..
+ 's = string(A)\_s*' ..
+ '5 LOADSCRIPT A-0 from .*\_s*' ..
+ '6 BCALL string(argc 1)\_s*' ..
+ '7 STORE $1\_s*' ..
+ '8 RETURN void', g:instr)
+ unlet g:instr
+enddef
+
+" Disassemble instructions for calls to a empty() function in an object
+def Test_disassemble_object_empty()
+ var lines =<< trim END
+ vim9script
+ class A
+ def empty(): bool
+ return true
+ enddef
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = empty(a)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = empty(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 METHODCALL A.empty(argc 0)\_s*' ..
+ '4 STORE $1\_s*' ..
+ '5 RETURN void', g:instr)
+ unlet g:instr
+
+ # Use the default empty() function for a class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = empty(a)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = empty(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 BCALL empty(argc 1)\_s*' ..
+ '4 STORE $1\_s*' ..
+ '5 RETURN void', g:instr)
+ unlet g:instr
+enddef
+
+" Disassemble instructions for calls to a len() function in an object
+def Test_disassemble_object_len()
+ var lines =<< trim END
+ vim9script
+ class A
+ def len(): number
+ return 10
+ enddef
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = len(a)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = len(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 METHODCALL A.len(argc 0)\_s*' ..
+ '4 STORE $1\_s*' ..
+ '5 RETURN void', g:instr)
+ unlet g:instr
+
+ # Use the default len() function for a class
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ def Bar()
+ var a = A.new()
+ var s = len(a)
+ enddef
+ g:instr = execute('disassemble Bar')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<SNR>\d*_Bar\_s*' ..
+ 'var a = A.new()\_s*' ..
+ '0 DCALL new(argc 0)\_s*' ..
+ '1 STORE $0\_s*' ..
+ 'var s = len(a)\_s*' ..
+ '2 LOAD $0\_s*' ..
+ '3 BCALL len(argc 1)\_s*' ..
+ '4 STORE $1\_s*' ..
+ '5 RETURN void', g:instr)
+ unlet g:instr
+enddef
+
+" Disassemble instructions for using a compound operator in a closure
+def Test_disassemble_compound_op_in_closure()
+ var lines =<< trim END
+ vim9script
+ class A
+ var foo: number = 1
+ def Foo(): func
+ var Fn = () => {
+ this.foo += 1
+ }
+ return Fn
+ enddef
+ endclass
+ var a = A.new()
+ var Lambda = a.Foo()
+ var num = matchstr(string(Lambda), '\d\+')
+ g:instr = execute($'disassemble <lambda>{num}')
+ END
+ v9.CheckScriptSuccess(lines)
+ assert_match('<lambda>\d\+\_s*' ..
+ 'this.foo += 1\_s*' ..
+ '0 LOADOUTER level 0 $0\_s*' ..
+ '1 OBJ_MEMBER 0\_s*' ..
+ '2 PUSHNR 1\_s*' ..
+ '3 OPNR +\_s*' ..
+ '4 PUSHNR 0\_s*' ..
+ '5 LOADOUTER level 0 $0\_s*' ..
+ '6 STOREINDEX object\_s*' ..
+ '7 RETURN void', g:instr)
+ unlet g:instr
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 6285ba1..7764b37 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -311,6 +311,33 @@ def Test_expr2()
assert_equal([1], g:vals)
END
v9.CheckDefAndScriptSuccess(lines)
+
+ # test the short-circuit operation
+ lines =<< trim END
+ assert_equal(true, true && true)
+ assert_equal(false, true && !true)
+ assert_equal(false, !true && true)
+ assert_equal(false, !true && !true)
+
+ assert_equal(true, true || true)
+ assert_equal(true, true || !true)
+ assert_equal(true, !true || true)
+ assert_equal(false, !true || !true)
+
+ assert_equal(false, false && false)
+ assert_equal(false, false && !false)
+ assert_equal(false, !false && false)
+ assert_equal(true, !false && !false)
+
+ assert_equal(false, false || false)
+ assert_equal(true, false || !false)
+ assert_equal(true, !false || false)
+ assert_equal(true, !false || !false)
+
+ assert_equal(false, !true && !true && !true)
+ assert_equal(true, !false || !false || !false)
+ END
+ v9.CheckDefAndScriptSuccess(lines)
enddef
def Test_expr2_vimscript()
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index d470a34..fa1aeb1 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -2929,5 +2929,54 @@ def Test_export_in_conditional_block()
v9.CheckScriptSuccess(lines)
enddef
+" Import fails when an autoloaded script is imported again.
+" Github issue #14171
+def Test_import_autloaded_script()
+ mkdir('Ximporttwice', 'pR')
+ mkdir('Ximporttwice/plugin')
+ mkdir('Ximporttwice/autoload')
+ var save_rtp = &rtp
+ exe 'set rtp^=' .. getcwd() .. '/Ximporttwice'
+
+ var lines =<< trim END
+ vim9script
+
+ export def H(): number
+ return 10
+ enddef
+ END
+ writefile(lines, 'Ximporttwice/autoload/hello.vim')
+
+ lines =<< trim END
+ vim9script
+
+ import "./hello.vim"
+ export def W(): number
+ return 20
+ enddef
+ END
+ writefile(lines, 'Ximporttwice/autoload/world.vim')
+
+ lines =<< trim END
+ vim9script
+
+ import autoload '../autoload/hello.vim'
+ import autoload '../autoload/world.vim'
+
+ command Hello echo hello.H()
+ command World echo world.W()
+ END
+ writefile(lines, 'Ximporttwice/plugin/main.vim')
+
+ lines =<< trim END
+ vim9script
+
+ source ./Ximporttwice/plugin/main.vim
+ assert_equal(['20'], execute('World')->split("\n"))
+ END
+ v9.CheckScriptSuccess(lines)
+
+ &rtp = save_rtp
+enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index 7ae4d55..77b8831 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -4906,6 +4906,31 @@ def Test_for_stmt_space_before_type()
v9.CheckSourceFailure(lines, 'E1059: No white space allowed before colon: :number in range(10)', 2)
enddef
+" This test used to cause an use-after-free memory access
+def Test_for_empty_line_after_lambda()
+ var lines =<< trim END
+ vim9script
+ echomsg range(0, 2)->map((_, v) => {
+ return 1
+ })
+
+ assert_equal('[1, 1, 1]', v:statusmsg)
+ END
+ v9.CheckSourceSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ echomsg range(0, 1)->map((_, v) => {
+ return 1
+ }) range(0, 1)->map((_, v) => {
+ return 2
+ }) # comment
+
+ assert_equal('[1, 1] [2, 2]', v:statusmsg)
+ END
+ v9.CheckSourceSuccess(lines)
+enddef
+
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
diff --git a/src/testdir/test_vim9_typealias.vim b/src/testdir/test_vim9_typealias.vim
index 4155744..998079c 100644
--- a/src/testdir/test_vim9_typealias.vim
+++ b/src/testdir/test_vim9_typealias.vim
@@ -641,7 +641,7 @@ def Test_type_as_func_argument_or_return_value()
END
v9.CheckScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
- # check defered function using typealias as arg
+ # check deferred function using typealias as arg
lines =<< trim END
vim9script
type A = number
@@ -764,7 +764,7 @@ def Test_class_as_func_argument_or_return_value()
END
v9.CheckScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
- # check defered function using class typealias as arg
+ # check deferred function using class typealias as arg
lines =<< trim END
vim9script
class C
diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim
index ec2b276..48d4aa0 100644
--- a/src/testdir/test_virtualedit.vim
+++ b/src/testdir/test_virtualedit.vim
@@ -77,13 +77,30 @@ endfunc
func Test_edit_change()
new
set virtualedit=all
+
call setline(1, "\t⒌")
normal Cx
call assert_equal('x', getline(1))
+
+ call setline(1, "\ta̳")
+ normal Cx
+ call assert_equal('x', getline(1))
+
+ call setline(1, "\tβ̳")
+ normal Cx
+ call assert_equal('x', getline(1))
+
+ if has('arabic')
+ call setline(1, "\tلا")
+ normal Cx
+ call assert_equal('x', getline(1))
+ endif
+
" Do a visual block change
call setline(1, ['a', 'b', 'c'])
exe "normal gg3l\<C-V>2jcx"
call assert_equal(['a x', 'b x', 'c x'], getline(1, '$'))
+
bwipe!
set virtualedit=
endfunc
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 401a350..fd174a9 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -3,6 +3,7 @@
source shared.vim
source check.vim
source screendump.vim
+import './vim9.vim' as v9
func Test_block_shift_multibyte()
" Uses double-wide character.
@@ -1008,7 +1009,7 @@ endfunc
" Test for changing case
func Test_visual_change_case()
new
- " gUe must uppercase a whole word, also when ß changes to SS
+ " gUe must uppercase a whole word, also when ß changes to ẞ
exe "normal Gothe youtußeuu end\<Esc>Ypk0wgUe\r"
" gUfx must uppercase until x, inclusive.
exe "normal O- youßtußexu -\<Esc>0fogUfx\r"
@@ -1020,9 +1021,9 @@ func Test_visual_change_case()
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, '$'))
+ call assert_equal(['the YOUTUẞEUU end', '- yOUẞTUẞEXu -',
+ \ 'THE YOUTUẞEUU END', '111THE YOUTUẞEUU END', 'BLAH DI', 'DOH DUT',
+ \ '222the yoUTUẞEUU END', '333THE YOUTUßeuu end'], getline(2, '$'))
bwipe!
endfunc
@@ -1622,4 +1623,336 @@ func Test_visual_drag_out_of_window()
bwipe!
endfunc
+func Test_visual_substitute_visual()
+ new
+ call setline(1, ['one', 'two', 'three'])
+ call feedkeys("Gk\<C-V>j$:s/\\%V\\_.*\\%V/foobar\<CR>", 'tx')
+ call assert_equal(['one', 'foobar'], getline(1, '$'))
+ bwipe!
+endfunc
+
+func Test_visual_getregion()
+ let lines =<< trim END
+ new
+
+ call setline(1, ['one', 'two', 'three'])
+
+ #" Visual mode
+ call cursor(1, 1)
+ call feedkeys("\<ESC>vjl", 'tx')
+ call assert_equal(['one', 'tw'],
+ \ 'v'->getpos()->getregion(getpos('.')))
+ call assert_equal(['one', 'tw'],
+ \ '.'->getpos()->getregion(getpos('v')))
+ call assert_equal(['o'],
+ \ 'v'->getpos()->getregion(getpos('v')))
+ call assert_equal(['w'],
+ \ '.'->getpos()->getregion(getpos('.'), {'type': 'v' }))
+ call assert_equal(['one', 'two'],
+ \ getpos('.')->getregion(getpos('v'), {'type': 'V' }))
+ call assert_equal(['on', 'tw'],
+ \ getpos('.')->getregion(getpos('v'), {'type': "\<C-v>" }))
+
+ #" Line visual mode
+ call cursor(1, 1)
+ call feedkeys("\<ESC>Vl", 'tx')
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('v'), {'type': 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('v'), {'type': 'V' }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('.'), {'type': 'V' }))
+ call assert_equal(['on'],
+ \ getpos('.')->getregion(getpos('v'), {'type': 'v' }))
+ call assert_equal(['on'],
+ \ getpos('.')->getregion(getpos('v'), {'type': "\<C-v>" }))
+
+ #" Block visual mode
+ call cursor(1, 1)
+ call feedkeys("\<ESC>\<C-v>ll", 'tx')
+ call assert_equal(['one'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal(['one'],
+ \ getregion(getpos('.'), getpos('v'), {'type': "\<C-v>" }))
+ call assert_equal(['o'],
+ \ getregion(getpos('v'), getpos('v'), {'type': "\<C-v>" }))
+ call assert_equal(['e'],
+ \ getregion(getpos('.'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal(['one'],
+ \ '.'->getpos()->getregion(getpos('v'), {'type': 'V' }))
+ call assert_equal(['one'],
+ \ '.'->getpos()->getregion(getpos('v'), {'type': 'v' }))
+
+ #" Using Marks
+ call setpos("'a", [0, 2, 3, 0])
+ call cursor(1, 1)
+ call assert_equal(['one', 'two'],
+ \ "'a"->getpos()->getregion(getpos('.'), {'type': 'v' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), {'type': 'v' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), {'type': 'V' }))
+ call assert_equal(['two'],
+ \ "'a"->getpos()->getregion(getpos("'a"), {'type': 'V' }))
+ call assert_equal(['one', 'two'],
+ \ "."->getpos()->getregion(getpos("'a"), {'type': "\<c-v>" }))
+
+ #" Using List
+ call cursor(1, 1)
+ call assert_equal(['one', 'two'],
+ \ [0, 2, 3, 0]->getregion(getpos('.'), {'type': 'v' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], {'type': 'v' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], {'type': 'V' }))
+ call assert_equal(['two'],
+ \ [0, 2, 3, 0]->getregion([0, 2, 3, 0], {'type': 'V' }))
+ call assert_equal(['one', 'two'],
+ \ '.'->getpos()->getregion([0, 2, 3, 0], {'type': "\<c-v>" }))
+
+ #" Multiline with line visual mode
+ call cursor(1, 1)
+ call feedkeys("\<ESC>Vjj", 'tx')
+ call assert_equal(['one', 'two', 'three'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'V' }))
+
+ #" Multiline with block visual mode
+ call cursor(1, 1)
+ call feedkeys("\<ESC>\<C-v>jj", 'tx')
+ call assert_equal(['o', 't', 't'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 1)
+ call feedkeys("\<ESC>\<C-v>jj$", 'tx')
+ call assert_equal(['one', 'two', 'three'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ #" 'virtualedit'
+ set virtualedit=all
+ call cursor(1, 1)
+ call feedkeys("\<ESC>\<C-v>10ljj$", 'tx')
+ call assert_equal(['one ', 'two ', 'three '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ set virtualedit&
+
+ #" using wrong types for positions
+ call cursor(1, 1)
+ call feedkeys("\<ESC>vjj$", 'tx')
+ call assert_fails("call getregion(1, 2)", 'E1211:')
+ call assert_fails("call getregion(getpos('.'), {})", 'E1211:')
+ call assert_fails(':echo "."->getpos()->getregion("$", [])', 'E1211:')
+
+ #" using invalid value for "type"
+ call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '' })", 'E475:')
+
+ #" using a mark from another buffer to current buffer
+ new
+ LET g:buf = bufnr()
+ call setline(1, range(10))
+ normal! GmA
+ wincmd p
+ call assert_equal([g:buf, 10, 1, 0], getpos("'A"))
+ call assert_equal([], getregion(getpos('.'), getpos("'A"), {'type': 'v' }))
+ call assert_equal([], getregion(getpos("'A"), getpos('.'), {'type': 'v' }))
+
+ #" using two marks from another buffer
+ wincmd p
+ normal! GmB
+ wincmd p
+ call assert_equal([g:buf, 10, 1, 0], getpos("'B"))
+ call assert_equal(['9'], getregion(getpos("'B"), getpos("'A"), {'type': 'v' }))
+
+ #" using two positions from another buffer
+ for type in ['v', 'V', "\<C-V>"]
+ for exclusive in [v:false, v:true]
+ call assert_equal(range(10)->mapnew('string(v:val)'),
+ \ getregion([g:buf, 1, 1, 0], [g:buf, 10, 2, 0],
+ \ {'type': type, 'exclusive': exclusive }))
+ call assert_equal(range(10)->mapnew('string(v:val)'),
+ \ getregion([g:buf, 10, 2, 0], [g:buf, 1, 1, 0],
+ \ {'type': type, 'exclusive': exclusive }))
+ endfor
+ endfor
+
+ #" using invalid positions in buffer
+ call assert_fails('call getregion([g:buf, 0, 1, 0], [g:buf, 10, 2, 0])', 'E966:')
+ call assert_fails('call getregion([g:buf, 10, 2, 0], [g:buf, 0, 1, 0])', 'E966:')
+ call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 11, 2, 0])', 'E966:')
+ call assert_fails('call getregion([g:buf, 11, 2, 0], [g:buf, 1, 1, 0])', 'E966:')
+ call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 0, 0])', 'E964:')
+ call assert_fails('call getregion([g:buf, 10, 0, 0], [g:buf, 1, 1, 0])', 'E964:')
+ call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 3, 0])', 'E964:')
+ call assert_fails('call getregion([g:buf, 10, 3, 0], [g:buf, 1, 1, 0])', 'E964:')
+
+ #" using invalid buffer
+ call assert_fails('call getregion([10000, 10, 1, 0], [10000, 10, 1, 0])', 'E681:')
+
+ exe $':{g:buf}bwipe!'
+ unlet g:buf
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ bwipe!
+
+ let lines =<< trim END
+ #" 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(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call cursor(1, 4)
+ call feedkeys("\<Esc>\<C-v>ljj", 'xt')
+ call assert_equal(['de', "\U0001f1e7", '45'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call cursor(1, 5)
+ call feedkeys("\<Esc>\<C-v>jj", 'xt')
+ call assert_equal(['e', ' ', '5'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>vj", 'xt')
+ call assert_equal(['abcdefghijk«', "\U0001f1e6"],
+ \ getregion(getpos('v'), getpos('.'), {'type': '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(getpos("'a"), getpos("'b"), {'type': "\<c-v>" }))
+ call assert_equal(['k«', '🇦«🇧«🇨'],
+ \ getregion(getpos("'a"), getpos("'b"), {'type': 'v' }))
+ call assert_equal(['k«'],
+ \ getregion(getpos("'a"), getpos("'c"), {'type': 'v' }))
+
+ #" use inclusive selection, although 'selection' is exclusive
+ call setpos("'a", [0, 1, 11, 0])
+ call setpos("'b", [0, 1, 1, 0])
+ call assert_equal(['abcdefghijk'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': "\<c-v>", 'exclusive': v:false }))
+ call assert_equal(['abcdefghij'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': "\<c-v>", 'exclusive': v:true }))
+ call assert_equal(['abcdefghijk'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': 'v', 'exclusive': 0 }))
+ call assert_equal(['abcdefghij'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': 'v', 'exclusive': 1 }))
+ call assert_equal(['abcdefghijk«'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': 'V', 'exclusive': 0 }))
+ call assert_equal(['abcdefghijk«'],
+ \ getregion(getpos("'a"), getpos("'b"),
+ \ {'type': 'V', 'exclusive': 1 }))
+ :set selection&
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ bwipe!
+
+ let lines =<< trim END
+ #" 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(getpos('v'), getpos('.'), {'type': 'v' }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v$G", 'xt')
+ call assert_equal(["a\tc", "x\tz", ''],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v$j", 'xt')
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>\<C-v>$j", 'xt')
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>\<C-v>$G", 'xt')
+ call assert_equal(["a", "x", '', ''],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>wv2j", 'xt')
+ call assert_equal(["c", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ set selection&
+
+ #" Exclusive selection 2
+ new
+ call setline(1, ["a\tc", "x\tz", '', ''])
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v2l", 'xt')
+ call assert_equal(["a\t"],
+ \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v$G", 'xt')
+ call assert_equal(["a\tc", "x\tz", ''],
+ \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v$j", 'xt')
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>\<C-v>$j", 'xt')
+ call assert_equal(["a\tc", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>\<C-v>$G", 'xt')
+ call assert_equal(["a", "x", '', ''],
+ \ getregion(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>wv2j", 'xt')
+ call assert_equal(["c", "x\tz"],
+ \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
+ #" virtualedit
+ set selection=exclusive
+ set virtualedit=all
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2lv2lj", 'xt')
+ call assert_equal([' c', 'x '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ set virtualedit&
+ set selection&
+
+ bwipe!
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+endfunc
+
+func Test_getregion_invalid_buf()
+ new
+ help
+ call cursor(5, 7)
+ norm! mA
+ call cursor(5, 18)
+ norm! mB
+ call assert_equal(['Move around:'], getregion(getpos("'A"), getpos("'B")))
+ " close the help window
+ q
+ call assert_fails("call getregion(getpos(\"'A\"), getpos(\"'B\"))", 'E681:')
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim
index 6b7dccb..91adb9b 100644
--- a/src/testdir/test_window_cmd.vim
+++ b/src/testdir/test_window_cmd.vim
@@ -131,64 +131,6 @@ func Test_window_quit()
bw Xa Xb
endfunc
-func Test_window_curwin_not_prevwin()
- botright split
- call assert_equal(2, winnr())
- call assert_equal(1, winnr('#'))
- quit
- call assert_equal(1, winnr())
- call assert_equal(0, winnr('#'))
-
- botright split
- botright split
- call assert_equal(3, winnr())
- call assert_equal(2, winnr('#'))
- 1quit
- call assert_equal(2, winnr())
- call assert_equal(1, winnr('#'))
-
- botright split
- call assert_equal(1, tabpagenr())
- call assert_equal(3, winnr())
- call assert_equal(2, winnr('#'))
- wincmd T
- call assert_equal(2, tabpagenr())
- call assert_equal(1, winnr())
- call assert_equal(0, winnr('#'))
- tabfirst
- call assert_equal(1, tabpagenr())
- call assert_equal(2, winnr())
- call assert_equal(0, winnr('#'))
-
- tabonly
- botright split
- wincmd t
- wincmd p
- call assert_equal(3, winnr())
- call assert_equal(1, winnr('#'))
- quit
- call assert_equal(2, winnr())
- call assert_equal(1, winnr('#'))
-
- botright split
- wincmd t
- wincmd p
- call assert_equal(1, tabpagenr())
- call assert_equal(3, winnr())
- call assert_equal(1, winnr('#'))
- wincmd T
- call assert_equal(2, tabpagenr())
- call assert_equal(1, winnr())
- call assert_equal(0, winnr('#'))
- tabfirst
- call assert_equal(1, tabpagenr())
- call assert_equal(2, winnr())
- call assert_equal(1, winnr('#'))
-
- tabonly
- only
-endfunc
-
func Test_window_horizontal_split()
call assert_equal(1, winnr('$'))
3wincmd s
@@ -276,6 +218,20 @@ func Test_window_split_edit_bufnr()
%bw!
endfunc
+func s:win_layout_info(tp = tabpagenr()) abort
+ return #{
+ \ layout: winlayout(a:tp),
+ \ pos_sizes: range(1, tabpagewinnr(a:tp, '$'))
+ \ ->map({_, nr -> win_getid(nr, a:tp)->getwininfo()[0]})
+ \ ->map({_, wininfo -> #{id: wininfo.winid,
+ \ row: wininfo.winrow,
+ \ col: wininfo.wincol,
+ \ width: wininfo.width,
+ \ height: wininfo.height}})
+ \ ->sort({a, b -> a.id - b.id})
+ \ }
+endfunc
+
func Test_window_split_no_room()
" N horizontal windows need >= 2*N + 1 lines:
" - 1 line + 1 status line in each window
@@ -290,6 +246,14 @@ func Test_window_split_no_room()
for s in range(1, hor_split_count) | split | endfor
call assert_fails('split', 'E36:')
+ botright vsplit
+ wincmd |
+ let info = s:win_layout_info()
+ call assert_fails('wincmd J', 'E36:')
+ call assert_fails('wincmd K', 'E36:')
+ call assert_equal(info, s:win_layout_info())
+ only
+
" N vertical windows need >= 2*(N - 1) + 1 columns:
" - 1 column + 1 separator for each window (except last window)
" - 1 column for the last window which does not have separator
@@ -302,7 +266,37 @@ func Test_window_split_no_room()
for s in range(1, ver_split_count) | vsplit | endfor
call assert_fails('vsplit', 'E36:')
+ split
+ wincmd |
+ let info = s:win_layout_info()
+ call assert_fails('wincmd H', 'E36:')
+ call assert_fails('wincmd L', 'E36:')
+ call assert_equal(info, s:win_layout_info())
+
+ " Check that the last statusline isn't lost.
+ " Set its window's width to 2 for the test.
+ wincmd j
+ set laststatus=0 winminwidth=0
+ vertical resize 2
+ " Update expected positions/sizes after the resize. Layout is unchanged.
+ let info.pos_sizes = s:win_layout_info().pos_sizes
+ set winminwidth&
+ call setwinvar(winnr('k'), '&statusline', '@#')
+ let last_stl_row = win_screenpos(0)[0] - 1
+ redraw
+ call assert_equal('@#|', GetScreenStr(last_stl_row))
+ call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
+
+ call assert_fails('wincmd H', 'E36:')
+ call assert_fails('wincmd L', 'E36:')
+ call assert_equal(info, s:win_layout_info())
+ call setwinvar(winnr('k'), '&statusline', '=-')
+ redraw
+ call assert_equal('=-|', GetScreenStr(last_stl_row))
+ call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
+
%bw!
+ set laststatus&
endfunc
func Test_window_exchange()
@@ -1066,6 +1060,18 @@ func Test_win_splitmove()
leftabove split b
leftabove vsplit c
leftabove split d
+
+ " win_splitmove doesn't actually create or close any windows, so expect an
+ " unchanged winid and no WinNew/WinClosed events, like :wincmd H/J/K/L.
+ let s:triggered = []
+ augroup WinSplitMove
+ au!
+ au WinNewPre * let s:triggered += ['WinNewPre']
+ au WinNew * let s:triggered += ['WinNew', win_getid()]
+ au WinClosed * let s:triggered += ['WinClosed', str2nr(expand('<afile>'))]
+ augroup END
+ let winid = win_getid()
+
call assert_equal(0, win_splitmove(winnr(), winnr('l')))
call assert_equal(bufname(winbufnr(1)), 'c')
call assert_equal(bufname(winbufnr(2)), 'd')
@@ -1088,6 +1094,11 @@ func Test_win_splitmove()
call assert_equal(bufname(winbufnr(3)), 'a')
call assert_equal(bufname(winbufnr(4)), 'd')
call assert_fails('call win_splitmove(winnr(), winnr("k"), test_null_dict())', 'E1297:')
+ call assert_equal([], s:triggered)
+ call assert_equal(winid, win_getid())
+
+ unlet! s:triggered
+ au! WinSplitMove
only | bd
call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
@@ -1097,6 +1108,54 @@ func Test_win_splitmove()
tabnew
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
tabclose
+
+ split
+ augroup WinSplitMove
+ au!
+ au WinEnter * ++once call win_gotoid(win_getid(winnr('#')))
+ augroup END
+ call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
+
+ augroup WinSplitMove
+ au!
+ au WinLeave * ++once quit
+ augroup END
+ call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
+
+ split
+ split
+ augroup WinSplitMove
+ au!
+ au WinEnter * ++once let s:triggered = v:true
+ \| call assert_fails('call win_splitmove(winnr(), winnr("$"))', 'E242:')
+ \| call assert_fails('call win_splitmove(winnr("$"), winnr())', 'E242:')
+ augroup END
+ quit
+ call assert_equal(v:true, s:triggered)
+ unlet! s:triggered
+
+ new
+ augroup WinSplitMove
+ au!
+ au BufHidden * ++once let s:triggered = v:true
+ \| call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E1159:')
+ augroup END
+ hide
+ call assert_equal(v:true, s:triggered)
+ unlet! s:triggered
+
+ split
+ let close_win = winnr('#')
+ augroup WinSplitMove
+ au!
+ au WinEnter * ++once quit!
+ augroup END
+ call win_splitmove(close_win, winnr())
+ call assert_equal(0, win_id2win(close_win))
+
+ au! WinSplitMove
+ augroup! WinSplitMove
+ %bw!
endfunc
" Test for the :only command
@@ -2060,24 +2119,160 @@ func Test_new_help_window_on_error()
call assert_equal(expand("<cword>"), "'mod'")
endfunc
-func Test_smoothscroll_in_zero_width_window()
- let save_lines = &lines
- let save_columns = &columns
+func Test_splitmove_flatten_frame()
+ split
+ vsplit
- winsize 0 24
- set cpo+=n
- exe "noremap 0 \<C-W>n\<C-W>L"
- norm 000000
- set number smoothscroll
- exe "norm \<C-Y>"
+ wincmd L
+ let layout = winlayout()
+ wincmd K
+ wincmd L
+ call assert_equal(winlayout(), layout)
only!
- let &lines = save_lines
- let &columns = save_columns
- set cpo-=n
- unmap 0
- set nonumber nosmoothscroll
endfunc
+func Test_autocmd_window_force_room()
+ " Open as many windows as possible
+ while v:true
+ try
+ split
+ catch /E36:/
+ break
+ endtry
+ endwhile
+ while v:true
+ try
+ vsplit
+ catch /E36:/
+ break
+ endtry
+ endwhile
+
+ wincmd j
+ vsplit
+ call assert_fails('wincmd H', 'E36:')
+ call assert_fails('wincmd J', 'E36:')
+ call assert_fails('wincmd K', 'E36:')
+ call assert_fails('wincmd L', 'E36:')
+
+ edit unload me
+ enew
+ bunload! unload\ me
+ augroup AucmdWinForceRoom
+ au!
+ au BufEnter * ++once let s:triggered = v:true
+ \| call assert_equal('autocmd', win_gettype())
+ augroup END
+ let info = s:win_layout_info()
+ " bufload opening the autocommand window shouldn't give E36.
+ call bufload('unload me')
+ call assert_equal(v:true, s:triggered)
+ call assert_equal(info, s:win_layout_info())
+
+ unlet! s:triggered
+ au! AucmdWinForceRoom
+ augroup! AucmdWinForceRoom
+ %bw!
+endfunc
+
+func Test_win_gotoid_splitmove_textlock_cmdwin()
+ call setline(1, 'foo')
+ new
+ let curwin = win_getid()
+ call setline(1, 'bar')
+
+ set debug+=throw indentexpr=win_gotoid(win_getid(winnr('#')))
+ call assert_fails('normal! ==', 'E565:')
+ call assert_equal(curwin, win_getid())
+ " No error if attempting to switch to curwin; nothing happens.
+ set indentexpr=assert_equal(1,win_gotoid(win_getid()))
+ normal! ==
+ call assert_equal(curwin, win_getid())
+
+ set indentexpr=win_splitmove(winnr('#'),winnr())
+ call assert_fails('normal! ==', 'E565:')
+ call assert_equal(curwin, win_getid())
+
+ %bw!
+ set debug-=throw indentexpr&
+
+ call feedkeys('q:'
+ \ .. ":call assert_fails('call win_splitmove(winnr(''#''), winnr())', 'E11:')\<CR>"
+ \ .. ":call assert_equal('command', win_gettype())\<CR>"
+ \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
+
+ call feedkeys('q:'
+ \ .. ":call assert_fails('call win_gotoid(win_getid(winnr(''#'')))', 'E11:')\<CR>"
+ "\ No error if attempting to switch to curwin; nothing happens.
+ \ .. ":call assert_equal(1, win_gotoid(win_getid()))\<CR>"
+ \ .. ":call assert_equal('command', win_gettype())\<CR>"
+ \ .. ":call assert_equal('', win_gettype(winnr('#')))\<CR>", 'ntx')
+endfunc
+
+func Test_winfixsize_positions()
+ " Check positions are correct when closing a window in a non-current tabpage
+ " causes non-adjacent window to fill the space due to 'winfix{width,height}'.
+ tabnew
+ vsplit
+ wincmd |
+ split
+ set winfixheight
+ split foo
+ tabfirst
+
+ bwipe! foo
+ " Save actual values before entering the tabpage.
+ let info = s:win_layout_info(2)
+ tabnext
+ " Compare it with the expected value (after win_comp_pos) from entering.
+ call assert_equal(s:win_layout_info(), info)
+
+ $tabnew
+ split
+ split
+ wincmd k
+ belowright vsplit
+ set winfixwidth
+ belowright vsplit foo
+ tabprevious
+
+ bwipe! foo
+ " Save actual values before entering the tabpage.
+ let info = s:win_layout_info(3)
+ tabnext
+ " Compare it with the expected value (after win_comp_pos) from entering.
+ call assert_equal(s:win_layout_info(), info)
+
+ " Check positions unchanged when failing to move a window, if 'winfix{width,
+ " height}' would otherwise cause a non-adjacent window to fill the space.
+ %bwipe
+ call assert_fails('execute "split|"->repeat(&lines)', 'E36:')
+ wincmd p
+ vsplit
+ set winfixwidth
+ vsplit
+ set winfixwidth
+ vsplit
+ vsplit
+ set winfixwidth
+ wincmd p
+
+ let info = s:win_layout_info()
+ call assert_fails('wincmd J', 'E36:')
+ call assert_equal(info, s:win_layout_info())
+
+ only
+ call assert_fails('execute "vsplit|"->repeat(&columns)', 'E36:')
+ belowright split
+ set winfixheight
+ belowright split
+
+ let info = s:win_layout_info()
+ call assert_fails('wincmd H', 'E36:')
+ call assert_equal(info, s:win_layout_info())
+
+ %bwipe
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_winfixbuf.vim b/src/testdir/test_winfixbuf.vim
new file mode 100644
index 0000000..04043f8
--- /dev/null
+++ b/src/testdir/test_winfixbuf.vim
@@ -0,0 +1,3286 @@
+" Test 'winfixbuf'
+
+source check.vim
+
+" Find the number of open windows in the current tab
+func s:get_windows_count()
+ return tabpagewinnr(tabpagenr(), '$')
+endfunc
+
+" Create some unnamed buffers.
+func s:make_buffers_list()
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file middle
+ let l:middle = bufnr()
+
+ enew
+ file last
+ let l:last = bufnr()
+
+ set winfixbuf
+
+ return [l:first, l:last]
+endfunc
+
+" Create some unnamed buffers and add them to an args list
+func s:make_args_list()
+ let [l:first, l:last] = s:make_buffers_list()
+
+ args! first middle last
+
+ return [l:first, l:last]
+endfunc
+
+" Create two buffers and then set the window to 'winfixbuf'
+func s:make_buffer_pairs(...)
+ let l:reversed = get(a:, 1, 0)
+
+ if l:reversed == 1
+ enew
+ file original
+
+ set winfixbuf
+
+ enew!
+ file other
+ let l:other = bufnr()
+
+ return l:other
+ endif
+
+ enew
+ file other
+ let l:other = bufnr()
+
+ enew
+ file current
+
+ set winfixbuf
+
+ return l:other
+endfunc
+
+" Create 3 quick buffers and set the window to 'winfixbuf'
+func s:make_buffer_trio()
+ edit first
+ let l:first = bufnr()
+ edit second
+ let l:second = bufnr()
+
+ set winfixbuf
+
+ edit! third
+ let l:third = bufnr()
+
+ execute ":buffer! " . l:second
+
+ return [l:first, l:second, l:third]
+endfunc
+
+" Create a location list with at least 2 entries + a 'winfixbuf' window.
+func s:make_simple_location_list()
+ enew
+ file middle
+ let l:middle = bufnr()
+ call append(0, ["winfix search-term", "another line"])
+
+ enew!
+ file first
+ let l:first = bufnr()
+ call append(0, "first search-term")
+
+ enew!
+ file last
+ let l:last = bufnr()
+ call append(0, "last search-term")
+
+ call setloclist(
+ \ 0,
+ \ [
+ \ {
+ \ "filename": "first",
+ \ "bufnr": l:first,
+ \ "lnum": 1,
+ \ },
+ \ {
+ \ "filename": "middle",
+ \ "bufnr": l:middle,
+ \ "lnum": 1,
+ \ },
+ \ {
+ \ "filename": "middle",
+ \ "bufnr": l:middle,
+ \ "lnum": 2,
+ \ },
+ \ {
+ \ "filename": "last",
+ \ "bufnr": l:last,
+ \ "lnum": 1,
+ \ },
+ \ ]
+ \)
+
+ set winfixbuf
+
+ return [l:first, l:middle, l:last]
+endfunc
+
+" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window.
+func s:make_simple_quickfix()
+ enew
+ file current
+ let l:current = bufnr()
+ call append(0, ["winfix search-term", "another line"])
+
+ enew!
+ file first
+ let l:first = bufnr()
+ call append(0, "first search-term")
+
+ enew!
+ file last
+ let l:last = bufnr()
+ call append(0, "last search-term")
+
+ call setqflist(
+ \ [
+ \ {
+ \ "filename": "first",
+ \ "bufnr": l:first,
+ \ "lnum": 1,
+ \ },
+ \ {
+ \ "filename": "current",
+ \ "bufnr": l:current,
+ \ "lnum": 1,
+ \ },
+ \ {
+ \ "filename": "current",
+ \ "bufnr": l:current,
+ \ "lnum": 2,
+ \ },
+ \ {
+ \ "filename": "last",
+ \ "bufnr": l:last,
+ \ "lnum": 1,
+ \ },
+ \ ]
+ \)
+
+ set winfixbuf
+
+ return [l:current, l:last]
+endfunc
+
+" Create a quickfix with at least 2 entries that are in the current 'winfixbuf' window.
+func s:make_quickfix_windows()
+ let [l:current, _] = s:make_simple_quickfix()
+ execute "buffer! " . l:current
+
+ split
+ let l:first_window = win_getid()
+ execute "normal \<C-w>j"
+ let l:winfix_window = win_getid()
+
+ " Open the quickfix in a separate split and go to it
+ copen
+ let l:quickfix_window = win_getid()
+
+ return [l:first_window, l:winfix_window, l:quickfix_window]
+endfunc
+
+" Revert all changes that occurred in any past test
+func s:reset_all_buffers()
+ %bwipeout!
+ set nowinfixbuf
+
+ call setqflist([])
+
+ for l:window_info in getwininfo()
+ call setloclist(l:window_info["winid"], [])
+ endfor
+
+ delmarks A-Z0-9
+endfunc
+
+" Find and set the first quickfix entry that points to `buffer`
+func s:set_quickfix_by_buffer(buffer)
+ let l:index = 1 " quickfix indices start at 1
+ for l:entry in getqflist()
+ if l:entry["bufnr"] == a:buffer
+ execute l:index . "cc"
+
+ return
+ endif
+
+ let l:index += 1
+ endfor
+
+ echoerr 'No quickfix entry matching "' . a:buffer . '" could be found.'
+endfunc
+
+" Fail to call :Next on a 'winfixbuf' window unless :Next! is used.
+func Test_Next()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("Next", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ Next!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Call :argdo and choose the next available 'nowinfixbuf' window.
+func Test_argdo_choose_available_window()
+ call s:reset_all_buffers()
+
+ let [_, l:last] = s:make_args_list()
+
+ " Make a split window that is 'nowinfixbuf' but make it the second-to-last
+ " window so that :argdo will first try the 'winfixbuf' window, pass over it,
+ " and prefer the other 'nowinfixbuf' window, instead.
+ "
+ " +-------------------+
+ " | 'nowinfixbuf' |
+ " +-------------------+
+ " | 'winfixbuf' | <-- Cursor is here
+ " +-------------------+
+ split
+ let l:nowinfixbuf_window = win_getid()
+ " Move to the 'winfixbuf' window now
+ execute "normal \<C-w>j"
+ let l:winfixbuf_window = win_getid()
+ let l:expected_windows = s:get_windows_count()
+
+ argdo echo ''
+ call assert_equal(l:nowinfixbuf_window, win_getid())
+ call assert_equal(l:last, bufnr())
+ call assert_equal(l:expected_windows, s:get_windows_count())
+endfunc
+
+" Call :argdo and create a new split window if all available windows are 'winfixbuf'.
+func Test_argdo_make_new_window()
+ call s:reset_all_buffers()
+
+ let [l:first, l:last] = s:make_args_list()
+ let l:current = win_getid()
+ let l:current_windows = s:get_windows_count()
+
+ argdo echo ''
+ call assert_notequal(l:current, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:first, bufnr())
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+endfunc
+
+" Fail :argedit but :argedit! is allowed
+func Test_argedit()
+ call s:reset_all_buffers()
+
+ args! first middle last
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file middle
+ let l:middle = bufnr()
+
+ enew
+ file last
+ let l:last = bufnr()
+
+ set winfixbuf
+
+ let l:current = bufnr()
+ call assert_fails("argedit first middle last", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ argedit! first middle last
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :arglocal but :arglocal! is allowed
+func Test_arglocal()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+ argglobal! other
+ execute "buffer! " . l:current
+
+ call assert_fails("arglocal other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ arglocal! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :argglobal but :argglobal! is allowed
+func Test_argglobal()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("argglobal other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ argglobal! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :args but :args! is allowed
+func Test_args()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_buffers_list()
+ let l:current = bufnr()
+
+ call assert_fails("args first middle last", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ args! first middle last
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :bNext but :bNext! is allowed
+func Test_bNext()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ call assert_fails("bNext", "E1513:")
+ let l:current = bufnr()
+
+ call assert_equal(l:current, bufnr())
+
+ bNext!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Allow :badd because it doesn't actually change the current window's buffer
+func Test_badd()
+ call s:reset_all_buffers()
+
+ call s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ badd other
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Allow :balt because it doesn't actually change the current window's buffer
+func Test_balt()
+ call s:reset_all_buffers()
+
+ call s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ balt other
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Fail :bfirst but :bfirst! is allowed
+func Test_bfirst()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("bfirst", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ bfirst!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :blast but :blast! is allowed
+func Test_blast()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs(1)
+ bfirst!
+ let l:current = bufnr()
+
+ call assert_fails("blast", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ blast!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :bmodified but :bmodified! is allowed
+func Test_bmodified()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ execute "buffer! " . l:other
+ set modified
+ execute "buffer! " . l:current
+
+ call assert_fails("bmodified", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ bmodified!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :bnext but :bnext! is allowed
+func Test_bnext()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("bnext", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ bnext!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :bprevious but :bprevious! is allowed
+func Test_bprevious()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("bprevious", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ bprevious!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :brewind but :brewind! is allowed
+func Test_brewind()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("brewind", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ brewind!
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :browse edit but :browse edit! is allowed
+func Test_browse_edit_fail()
+ " A GUI dialog may stall the test.
+ CheckNotGui
+
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("browse edit other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ try
+ browse edit! other
+ call assert_equal(l:other, bufnr())
+ catch /^Vim\%((\a\+)\)\=:E338:/
+ " Ignore E338, which occurs if console Vim is built with +browse.
+ " Console Vim without +browse will treat this as a regular :edit.
+ endtry
+endfunc
+
+" Allow :browse w because it doesn't change the buffer in the current file
+func Test_browse_edit_pass()
+ " A GUI dialog may stall the test.
+ CheckNotGui
+
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ try
+ browse write other
+ catch /^Vim\%((\a\+)\)\=:E338:/
+ " Ignore E338, which occurs if console Vim is built with +browse.
+ " Console Vim without +browse will treat this as a regular :write.
+ endtry
+
+ call delete("other")
+endfunc
+
+" Call :bufdo and choose the next available 'nowinfixbuf' window.
+func Test_bufdo_choose_available_window()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+
+ " Make a split window that is 'nowinfixbuf' but make it the second-to-last
+ " window so that :bufdo will first try the 'winfixbuf' window, pass over it,
+ " and prefer the other 'nowinfixbuf' window, instead.
+ "
+ " +-------------------+
+ " | 'nowinfixbuf' |
+ " +-------------------+
+ " | 'winfixbuf' | <-- Cursor is here
+ " +-------------------+
+ split
+ let l:nowinfixbuf_window = win_getid()
+ " Move to the 'winfixbuf' window now
+ execute "normal \<C-w>j"
+ let l:winfixbuf_window = win_getid()
+
+ let l:current = bufnr()
+ let l:expected_windows = s:get_windows_count()
+
+ call assert_notequal(l:current, l:other)
+
+ bufdo echo ''
+ call assert_equal(l:nowinfixbuf_window, win_getid())
+ call assert_notequal(l:other, bufnr())
+ call assert_equal(l:expected_windows, s:get_windows_count())
+endfunc
+
+" Call :bufdo and create a new split window if all available windows are 'winfixbuf'.
+func Test_bufdo_make_new_window()
+ call s:reset_all_buffers()
+
+ let [l:first, l:last] = s:make_buffers_list()
+ execute "buffer! " . l:first
+ let l:current = win_getid()
+ let l:current_windows = s:get_windows_count()
+
+ bufdo echo ''
+ call assert_notequal(l:current, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:first, bufnr())
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+endfunc
+
+" Fail :buffer but :buffer! is allowed
+func Test_buffer()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("buffer " . l:other, "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ execute "buffer! " . l:other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Allow :buffer on a 'winfixbuf' window if there is no change in buffer
+func Test_buffer_same_buffer()
+ call s:reset_all_buffers()
+
+ call s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ execute "buffer " . l:current
+ call assert_equal(l:current, bufnr())
+
+ execute "buffer! " . l:current
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Allow :cNext but the 'nowinfixbuf' window is selected, instead
+func Test_cNext()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cNext` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cNext
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :cNfile but the 'nowinfixbuf' window is selected, instead
+func Test_cNfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cNfile` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+ cnext!
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cNfile
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :caddexpr because it doesn't change the current buffer
+func Test_caddexpr()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file_path = tempname()
+ call writefile(["Error - bad-thing-found"], l:file_path)
+ execute "edit " . l:file_path
+ let l:file_buffer = bufnr()
+ let l:current = bufnr()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+
+ edit! other.unittest
+
+ set winfixbuf
+
+ execute "buffer! " . l:file_buffer
+
+ execute 'caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")'
+ call assert_equal(l:current, bufnr())
+
+ call delete(l:file_path)
+endfunc
+
+" Fail :cbuffer but :cbuffer! is allowed
+func Test_cbuffer()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file_path = tempname()
+ call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path)
+ execute "edit " . l:file_path
+ let l:file_buffer = bufnr()
+ let l:current = bufnr()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+
+ edit! other.unittest
+
+ set winfixbuf
+
+ execute "buffer! " . l:file_buffer
+
+ call assert_fails("cbuffer " . l:file_buffer)
+ call assert_equal(l:current, bufnr())
+
+ execute "cbuffer! " . l:file_buffer
+ call assert_equal("first.unittest", expand("%:t"))
+
+ call delete(l:file_path)
+endfunc
+
+" Allow :cc but the 'nowinfixbuf' window is selected, instead
+func Test_cc()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+ " Go up one line in the quickfix window to an quickfix entry that doesn't
+ " point to a winfixbuf buffer
+ normal k
+ " Attempt to make the previous window, winfixbuf buffer, to go to the
+ " non-winfixbuf quickfix entry
+ .cc
+
+ " Confirm that :.cc did not change the winfixbuf-enabled window
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Call :cdo and choose the next available 'nowinfixbuf' window.
+func Test_cdo_choose_available_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current, l:last] = s:make_simple_quickfix()
+ execute "buffer! " . l:current
+
+ " Make a split window that is 'nowinfixbuf' but make it the second-to-last
+ " window so that :cdo will first try the 'winfixbuf' window, pass over it,
+ " and prefer the other 'nowinfixbuf' window, instead.
+ "
+ " +-------------------+
+ " | 'nowinfixbuf' |
+ " +-------------------+
+ " | 'winfixbuf' | <-- Cursor is here
+ " +-------------------+
+ split
+ let l:nowinfixbuf_window = win_getid()
+ " Move to the 'winfixbuf' window now
+ execute "normal \<C-w>j"
+ let l:winfixbuf_window = win_getid()
+ let l:expected_windows = s:get_windows_count()
+
+ cdo echo ''
+
+ call assert_equal(l:nowinfixbuf_window, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:current, bufnr())
+ call assert_equal(l:expected_windows, s:get_windows_count())
+endfunc
+
+" Call :cdo and create a new split window if all available windows are 'winfixbuf'.
+func Test_cdo_make_new_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current_buffer, l:last] = s:make_simple_quickfix()
+ execute "buffer! " . l:current_buffer
+
+ let l:current_window = win_getid()
+ let l:current_windows = s:get_windows_count()
+
+ cdo echo ''
+ call assert_notequal(l:current_window, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:current_buffer, bufnr())
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+endfunc
+
+" Fail :cexpr but :cexpr! is allowed
+func Test_cexpr()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ let l:entry = '["' . l:file . ':1:bar"]'
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("cexpr " . l:entry)
+ call assert_equal(l:current, bufnr())
+
+ execute "cexpr! " . l:entry
+ call assert_equal(fnamemodify(l:file, ":t"), expand("%:t"))
+endfunc
+
+" Call :cfdo and choose the next available 'nowinfixbuf' window.
+func Test_cfdo_choose_available_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current, l:last] = s:make_simple_quickfix()
+ execute "buffer! " . l:current
+
+ " Make a split window that is 'nowinfixbuf' but make it the second-to-last
+ " window so that :cfdo will first try the 'winfixbuf' window, pass over it,
+ " and prefer the other 'nowinfixbuf' window, instead.
+ "
+ " +-------------------+
+ " | 'nowinfixbuf' |
+ " +-------------------+
+ " | 'winfixbuf' | <-- Cursor is here
+ " +-------------------+
+ split
+ let l:nowinfixbuf_window = win_getid()
+ " Move to the 'winfixbuf' window now
+ execute "normal \<C-w>j"
+ let l:winfixbuf_window = win_getid()
+ let l:expected_windows = s:get_windows_count()
+
+ cfdo echo ''
+
+ call assert_equal(l:nowinfixbuf_window, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:current, bufnr())
+ call assert_equal(l:expected_windows, s:get_windows_count())
+endfunc
+
+" Call :cfdo and create a new split window if all available windows are 'winfixbuf'.
+func Test_cfdo_make_new_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current_buffer, l:last] = s:make_simple_quickfix()
+ execute "buffer! " . l:current_buffer
+
+ let l:current_window = win_getid()
+ let l:current_windows = s:get_windows_count()
+
+ cfdo echo ''
+ call assert_notequal(l:current_window, win_getid())
+ call assert_equal(l:last, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:current_buffer, bufnr())
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+endfunc
+
+" Fail :cfile but :cfile! is allowed
+func Test_cfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+ write
+ let l:first = bufnr()
+
+ edit! second.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ let l:file = tempname()
+ call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file)
+
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails(":cfile " . l:file)
+ call assert_equal(l:current, bufnr())
+
+ execute ":cfile! " . l:file
+ call assert_equal(l:first, bufnr())
+
+ call delete(l:file)
+ call delete("first.unittest")
+ call delete("second.unittest")
+endfunc
+
+" Allow :cfirst but the 'nowinfixbuf' window is selected, instead
+func Test_cfirst()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cfirst` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cfirst
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :clast but the 'nowinfixbuf' window is selected, instead
+func Test_clast()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:clast` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ clast
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :cnext but the 'nowinfixbuf' window is selected, instead
+" Make sure no new windows are created and previous windows are reused
+func Test_cnext()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+ let l:expected = s:get_windows_count()
+
+ " The call to `:cnext` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ cnext!
+ call assert_equal(l:expected, s:get_windows_count())
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cnext
+ call assert_equal(l:first_window, win_getid())
+ call assert_equal(l:expected, s:get_windows_count())
+endfunc
+
+" Make sure :cnext creates a split window if no previous window exists
+func Test_cnext_no_previous_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current, _] = s:make_simple_quickfix()
+ execute "buffer! " . l:current
+
+ let l:expected = s:get_windows_count()
+
+ " Open the quickfix in a separate split and go to it
+ copen
+
+ call assert_equal(l:expected + 1, s:get_windows_count())
+endfunc
+
+" Allow :cnext and create a 'nowinfixbuf' window if none exists
+func Test_cnext_make_new_window()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:current, _] = s:make_simple_quickfix()
+ let l:current = win_getid()
+
+ cfirst!
+
+ let l:windows = s:get_windows_count()
+ let l:expected = l:windows + 1 " We're about to create a new split window
+
+ cnext
+ call assert_equal(l:expected, s:get_windows_count())
+
+ cnext!
+ call assert_equal(l:expected, s:get_windows_count())
+endfunc
+
+" Allow :cprevious but the 'nowinfixbuf' window is selected, instead
+func Test_cprevious()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cprevious` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cprevious
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :cnfile but the 'nowinfixbuf' window is selected, instead
+func Test_cnfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cnfile` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+ cnext!
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cnfile
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :cpfile but the 'nowinfixbuf' window is selected, instead
+func Test_cpfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:cpfile` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+ cnext!
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ cpfile
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow :crewind but the 'nowinfixbuf' window is selected, instead
+func Test_crewind()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first_window, l:winfix_window, l:quickfix_window] = s:make_quickfix_windows()
+
+ " The call to `:crewind` succeeds but it selects the window with 'nowinfixbuf' instead
+ call s:set_quickfix_by_buffer(winbufnr(l:winfix_window))
+ cnext!
+
+ " Make sure the previous window has 'winfixbuf' so we can test that our
+ " "skip 'winfixbuf' window" logic works.
+ call win_gotoid(l:winfix_window)
+ call win_gotoid(l:quickfix_window)
+
+ crewind
+ call assert_equal(l:first_window, win_getid())
+endfunc
+
+" Allow <C-w>f because it opens in a new split
+func Test_ctrl_w_f()
+ call s:reset_all_buffers()
+
+ enew
+ let l:file_name = tempname()
+ call writefile([], l:file_name)
+ let l:file_buffer = bufnr()
+
+ enew
+ file other
+ let l:other_buffer = bufnr()
+
+ set winfixbuf
+
+ call setline(1, l:file_name)
+ let l:current_windows = s:get_windows_count()
+ execute "normal \<C-w>f"
+
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+
+ call delete(l:file_name)
+endfunc
+
+" Fail :djump but :djump! is allowed
+func Test_djump()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile(["min(1, 12);",
+ \ '#include "' . l:include_file . '"'
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("djump 1 /min/", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ djump! 1 /min/
+ call assert_notequal(l:current, bufnr())
+
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail :drop but :drop! is allowed
+func Test_drop()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("drop other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ drop! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :edit but :edit! is allowed
+func Test_edit()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("edit other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ edit! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :enew but :enew! is allowed
+func Test_enew()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("enew", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ enew!
+ call assert_notequal(l:other, bufnr())
+ call assert_notequal(3, bufnr())
+endfunc
+
+" Fail :ex but :ex! is allowed
+func Test_ex()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("ex other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ ex! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :find but :find! is allowed
+func Test_find()
+ call s:reset_all_buffers()
+
+ let l:current = bufnr()
+ let l:file = tempname()
+ call writefile([], l:file)
+ let l:file = fnamemodify(l:file, ':p') " In case it's Windows 8.3-style.
+ let l:directory = fnamemodify(l:file, ":p:h")
+ let l:name = fnamemodify(l:file, ":p:t")
+
+ let l:original_path = &path
+ execute "set path=" . l:directory
+
+ set winfixbuf
+
+ call assert_fails("execute 'find " . l:name . "'", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ execute "find! " . l:name
+ call assert_equal(l:file, expand("%:p"))
+
+ execute "set path=" . l:original_path
+ call delete(l:file)
+endfunc
+
+" Fail :first but :first! is allowed
+func Test_first()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("first", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ first!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :grep but :grep! is allowed
+func Test_grep()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:first = bufnr()
+
+ edit current.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:current = bufnr()
+
+ edit! last.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:last = bufnr()
+
+ set winfixbuf
+
+ buffer! current.unittest
+
+ call assert_fails("silent! grep some-search-term *.unittest", "E1513:")
+ call assert_equal(l:current, bufnr())
+ execute "edit! " . l:first
+
+ silent! grep! some-search-term *.unittest
+ call assert_notequal(l:first, bufnr())
+
+ call delete("first.unittest")
+ call delete("current.unittest")
+ call delete("last.unittest")
+endfunc
+
+" Fail :ijump but :ijump! is allowed
+func Test_ijump()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile([
+ \ '#include "' . l:include_file . '"'
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ set define=^\\s*#\\s*define
+ set include=^\\s*#\\s*include
+ set path=.,/usr/include,,
+
+ call assert_fails("ijump /min/", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set nowinfixbuf
+
+ ijump! /min/
+ call assert_notequal(l:current, bufnr())
+
+ set define&
+ set include&
+ set path&
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail :lNext but :lNext! is allowed
+func Test_lNext()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, _] = s:make_simple_location_list()
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+
+ lnext!
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ call assert_fails("lNext", "E1513:")
+ " Ensure the entry didn't change.
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ lnext!
+ call assert_equal(3, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ lNext!
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ lNext!
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :lNfile but :lNfile! is allowed
+func Test_lNfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:current, _] = s:make_simple_location_list()
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+
+ lnext!
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:current, bufnr())
+
+ call assert_fails("lNfile", "E1513:")
+ " Ensure the entry didn't change.
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:current, bufnr())
+
+ lnext!
+ call assert_equal(3, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:current, bufnr())
+
+ lNfile!
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Allow :laddexpr because it doesn't change the current buffer
+func Test_laddexpr()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file_path = tempname()
+ call writefile(["Error - bad-thing-found"], l:file_path)
+ execute "edit " . l:file_path
+ let l:file_buffer = bufnr()
+ let l:current = bufnr()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+
+ edit! other.unittest
+
+ set winfixbuf
+
+ execute "buffer! " . l:file_buffer
+
+ execute 'laddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")'
+ call assert_equal(l:current, bufnr())
+
+ call delete(l:file_path)
+endfunc
+
+" Fail :last but :last! is allowed
+func Test_last()
+ call s:reset_all_buffers()
+
+ let [_, l:last] = s:make_args_list()
+ next!
+
+ call assert_fails("last", "E1513:")
+ call assert_notequal(l:last, bufnr())
+
+ last!
+ call assert_equal(l:last, bufnr())
+endfunc
+
+" Fail :lbuffer but :lbuffer! is allowed
+func Test_lbuffer()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file_path = tempname()
+ call writefile(["first.unittest:1:Error - bad-thing-found"], l:file_path)
+ execute "edit " . l:file_path
+ let l:file_buffer = bufnr()
+ let l:current = bufnr()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+
+ edit! other.unittest
+
+ set winfixbuf
+
+ execute "buffer! " . l:file_buffer
+
+ call assert_fails("lbuffer " . l:file_buffer)
+ call assert_equal(l:current, bufnr())
+
+ execute "lbuffer! " . l:file_buffer
+ call assert_equal("first.unittest", expand("%:t"))
+
+ call delete(l:file_path)
+endfunc
+
+" Fail :ldo but :ldo! is allowed
+func Test_ldo()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, l:last] = s:make_simple_location_list()
+ lnext!
+
+ call assert_fails('execute "ldo buffer ' . l:first . '"', "E1513:")
+ call assert_equal(l:middle, bufnr())
+ execute "ldo! buffer " . l:first
+ call assert_notequal(l:last, bufnr())
+endfunc
+
+" Fail :lfdo but :lfdo! is allowed
+func Test_lexpr()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ let l:entry = '["' . l:file . ':1:bar"]'
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("lexpr " . l:entry)
+ call assert_equal(l:current, bufnr())
+
+ execute "lexpr! " . l:entry
+ call assert_equal(fnamemodify(l:file, ":t"), expand("%:t"))
+endfunc
+
+" Fail :lfdo but :lfdo! is allowed
+func Test_lfdo()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, l:last] = s:make_simple_location_list()
+ lnext!
+
+ call assert_fails('execute "lfdo buffer ' . l:first . '"', "E1513:")
+ call assert_equal(l:middle, bufnr())
+ execute "lfdo! buffer " . l:first
+ call assert_notequal(l:last, bufnr())
+endfunc
+
+" Fail :lfile but :lfile! is allowed
+func Test_lfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term bad-thing-found"])
+ write
+ let l:first = bufnr()
+
+ edit! second.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ let l:file = tempname()
+ call writefile(["first.unittest:1:Error - bad-thing-found was detected"], l:file)
+
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails(":lfile " . l:file)
+ call assert_equal(l:current, bufnr())
+
+ execute ":lfile! " . l:file
+ call assert_equal(l:first, bufnr())
+
+ call delete(l:file)
+ call delete("first.unittest")
+ call delete("second.unittest")
+endfunc
+
+" Fail :ll but :ll! is allowed
+func Test_ll()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, l:last] = s:make_simple_location_list()
+ lopen
+ lfirst!
+ execute "normal \<C-w>j"
+ normal j
+
+ call assert_fails(".ll", "E1513:")
+ execute "normal \<C-w>k"
+ call assert_equal(l:first, bufnr())
+ execute "normal \<C-w>j"
+ .ll!
+ execute "normal \<C-w>k"
+ call assert_equal(l:middle, bufnr())
+endfunc
+
+" Fail :llast but :llast! is allowed
+func Test_llast()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, _, l:last] = s:make_simple_location_list()
+ lfirst!
+
+ call assert_fails("llast", "E1513:")
+ call assert_equal(l:first, bufnr())
+
+ llast!
+ call assert_equal(l:last, bufnr())
+endfunc
+
+" Fail :lnext but :lnext! is allowed
+func Test_lnext()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, l:last] = s:make_simple_location_list()
+ ll!
+
+ call assert_fails("lnext", "E1513:")
+ call assert_equal(l:first, bufnr())
+
+ lnext!
+ call assert_equal(l:middle, bufnr())
+endfunc
+
+" Fail :lnfile but :lnfile! is allowed
+func Test_lnfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [_, l:current, l:last] = s:make_simple_location_list()
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+
+ lnext!
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:current, bufnr())
+
+ call assert_fails("lnfile", "E1513:")
+ " Ensure the entry didn't change.
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:current, bufnr())
+
+ lnfile!
+ call assert_equal(4, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:last, bufnr())
+endfunc
+
+" Fail :lpfile but :lpfile! is allowed
+func Test_lpfile()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:current, _] = s:make_simple_location_list()
+ lnext!
+
+ call assert_fails("lpfile", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ lnext! " Reset for the next test call
+
+ lpfile!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :lprevious but :lprevious! is allowed
+func Test_lprevious()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, _] = s:make_simple_location_list()
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+
+ lnext!
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ call assert_fails("lprevious", "E1513:")
+ " Ensure the entry didn't change.
+ call assert_equal(2, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:middle, bufnr())
+
+ lprevious!
+ call assert_equal(1, getloclist(0, #{idx: 0}).idx)
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :lrewind but :lrewind! is allowed
+func Test_lrewind()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ let [l:first, l:middle, _] = s:make_simple_location_list()
+ lnext!
+
+ call assert_fails("lrewind", "E1513:")
+ call assert_equal(l:middle, bufnr())
+
+ lrewind!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail :ltag but :ltag! is allowed
+func Test_ltag()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+ execute "normal \<C-]>"
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("ltag one", "E1513:")
+
+ ltag! one
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail vim.command if we try to change buffers while 'winfixbuf' is set
+func Test_lua_command()
+ CheckFeature lua
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let l:previous = bufnr()
+
+ enew
+ file second
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails('lua vim.command("buffer " .. ' . l:previous . ')')
+ call assert_equal(l:current, bufnr())
+
+ execute 'lua vim.command("buffer! " .. ' . l:previous . ')'
+ call assert_equal(l:previous, bufnr())
+endfunc
+
+" Fail :lvimgrep but :lvimgrep! is allowed
+func Test_lvimgrep()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ edit winfix.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ edit! last.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:last = bufnr()
+
+ buffer! winfix.unittest
+
+ call assert_fails("lvimgrep /some-search-term/ *.unittest", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ lvimgrep! /some-search-term/ *.unittest
+ call assert_notequal(l:current, bufnr())
+
+ call delete("first.unittest")
+ call delete("winfix.unittest")
+ call delete("last.unittest")
+endfunc
+
+" Fail :lvimgrepadd but :lvimgrepadd! is allowed
+func Test_lvimgrepadd()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ edit winfix.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ edit! last.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:last = bufnr()
+
+ buffer! winfix.unittest
+
+ call assert_fails("lvimgrepadd /some-search-term/ *.unittest")
+ call assert_equal(l:current, bufnr())
+
+ lvimgrepadd! /some-search-term/ *.unittest
+ call assert_notequal(l:current, bufnr())
+
+ call delete("first.unittest")
+ call delete("winfix.unittest")
+ call delete("last.unittest")
+endfunc
+
+" Don't allow global marks to change the current 'winfixbuf' window
+func Test_marks_mappings_fail()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+ execute "buffer! " . l:other
+ normal mA
+ execute "buffer! " . l:current
+ normal mB
+
+ call assert_fails("normal `A", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ call assert_fails("normal 'A", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set nowinfixbuf
+
+ normal `A
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Allow global marks in a 'winfixbuf' window if the jump is the same buffer
+func Test_marks_mappings_pass_intra_move()
+ call s:reset_all_buffers()
+
+ let l:current = bufnr()
+ call append(0, ["some line", "another line"])
+ normal mA
+ normal j
+ normal mB
+
+ set winfixbuf
+
+ normal `A
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Fail :next but :next! is allowed
+func Test_next()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ first!
+
+ call assert_fails("next", "E1513:")
+ call assert_equal(l:first, bufnr())
+
+ next!
+ call assert_notequal(l:first, bufnr())
+endfunc
+
+" Ensure :mksession saves 'winfixbuf' details
+func Test_mksession()
+ CheckFeature mksession
+ call s:reset_all_buffers()
+
+ set sessionoptions+=options
+ set winfixbuf
+
+ mksession test_winfixbuf_Test_mksession.vim
+
+ call s:reset_all_buffers()
+ let l:winfixbuf = &winfixbuf
+ call assert_equal(0, l:winfixbuf)
+
+ source test_winfixbuf_Test_mksession.vim
+
+ let l:winfixbuf = &winfixbuf
+ call assert_equal(1, l:winfixbuf)
+
+ set sessionoptions&
+ call delete("test_winfixbuf_Test_mksession.vim")
+endfunc
+
+" Allow :next if the next index is the same as the current buffer
+func Test_next_same_buffer()
+ call s:reset_all_buffers()
+
+ enew
+ file foo
+ enew
+ file bar
+ enew
+ file fizz
+ enew
+ file buzz
+ args foo foo bar fizz buzz
+
+ edit foo
+ set winfixbuf
+ let l:current = bufnr()
+
+ " Allow :next because the args list is `[foo] foo bar fizz buzz
+ next
+ call assert_equal(l:current, bufnr())
+
+ " Fail :next because the args list is `foo [foo] bar fizz buzz
+ " and the next buffer would be bar, which is a different buffer
+ call assert_fails("next", "E1513:")
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Fail to jump to a tag with g<C-]> if 'winfixbuf' is enabled
+func Test_normal_g_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal g\<C-]>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with g<RightMouse> if 'winfixbuf' is enabled
+func Test_normal_g_rightmouse()
+ call s:reset_all_buffers()
+ set mouse=n
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+ execute "normal \<C-]>"
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal g\<RightMouse>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ set mouse&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with g] if 'winfixbuf' is enabled
+func Test_normal_g_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal g]", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with <C-RightMouse> if 'winfixbuf' is enabled
+func Test_normal_ctrl_rightmouse()
+ call s:reset_all_buffers()
+ set mouse=n
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+ execute "normal \<C-]>"
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal \<C-RightMouse>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ set mouse&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with <C-t> if 'winfixbuf' is enabled
+func Test_normal_ctrl_t()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+ execute "normal \<C-]>"
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal \<C-t>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Disallow <C-^> in 'winfixbuf' windows
+func Test_normal_ctrl_hat()
+ call s:reset_all_buffers()
+ clearjumps
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file current
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("normal \<C-^>", "E1513:")
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Allow <C-i> in 'winfixbuf' windows if the movement stays within the buffer
+func Test_normal_ctrl_i_pass()
+ call s:reset_all_buffers()
+ clearjumps
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew!
+ file current
+ let l:current = bufnr()
+ " Add some lines so we can populate a jumplist"
+ call append(0, ["some line", "another line"])
+ " Add an entry to the jump list
+ " Go up another line
+ normal m`
+ normal k
+ execute "normal \<C-o>"
+
+ set winfixbuf
+
+ let l:line = getcurpos()[1]
+ execute "normal 1\<C-i>"
+ call assert_notequal(l:line, getcurpos()[1])
+endfunc
+
+" Disallow <C-o> in 'winfixbuf' windows if it would cause the buffer to switch
+func Test_normal_ctrl_o_fail()
+ call s:reset_all_buffers()
+ clearjumps
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file current
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("normal \<C-o>", "E1513:")
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Allow <C-o> in 'winfixbuf' windows if the movement stays within the buffer
+func Test_normal_ctrl_o_pass()
+ call s:reset_all_buffers()
+ clearjumps
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew!
+ file current
+ let l:current = bufnr()
+ " Add some lines so we can populate a jumplist
+ call append(0, ["some line", "another line"])
+ " Add an entry to the jump list
+ " Go up another line
+ normal m`
+ normal k
+
+ set winfixbuf
+
+ execute "normal \<C-o>"
+ call assert_equal(l:current, bufnr())
+endfunc
+
+" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled
+func Test_normal_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal \<C-]>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Allow <C-w><C-]> with 'winfixbuf' enabled because it runs in a new, split window
+func Test_normal_ctrl_w_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current_windows = s:get_windows_count()
+ execute "normal \<C-w>\<C-]>"
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Allow <C-w>g<C-]> with 'winfixbuf' enabled because it runs in a new, split window
+func Test_normal_ctrl_w_g_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current_windows = s:get_windows_count()
+ execute "normal \<C-w>g\<C-]>"
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with <C-]> if 'winfixbuf' is enabled
+func Test_normal_gt()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one", "two", "three"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal \<C-]>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Prevent gF from switching a 'winfixbuf' window's buffer
+func Test_normal_gF()
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ call append(0, [l:file])
+ call writefile([], l:file)
+ " Place the cursor onto the line that has `l:file`
+ normal gg
+ " Prevent Vim from erroring with "No write since last change @ command
+ " line" when we try to call gF, later.
+ set hidden
+
+ set winfixbuf
+
+ let l:buffer = bufnr()
+
+ call assert_fails("normal gF", "E1513:")
+ call assert_equal(l:buffer, bufnr())
+
+ set nowinfixbuf
+
+ normal gF
+ call assert_notequal(l:buffer, bufnr())
+
+ call delete(l:file)
+endfunc
+
+" Prevent gf from switching a 'winfixbuf' window's buffer
+func Test_normal_gf()
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ call append(0, [l:file])
+ call writefile([], l:file)
+ " Place the cursor onto the line that has `l:file`
+ normal gg
+ " Prevent Vim from erroring with "No write since last change @ command
+ " line" when we try to call gf, later.
+ set hidden
+
+ set winfixbuf
+
+ let l:buffer = bufnr()
+
+ call assert_fails("normal gf", "E1513:")
+ call assert_equal(l:buffer, bufnr())
+
+ set nowinfixbuf
+
+ normal gf
+ call assert_notequal(l:buffer, bufnr())
+
+ call delete(l:file)
+endfunc
+
+" Fail "goto file under the cursor" (using [f, which is the same as `:normal gf`)
+func Test_normal_square_bracket_left_f()
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ call append(0, [l:file])
+ call writefile([], l:file)
+ " Place the cursor onto the line that has `l:file`
+ normal gg
+ " Prevent Vim from erroring with "No write since last change @ command
+ " line" when we try to call gf, later.
+ set hidden
+
+ set winfixbuf
+
+ let l:buffer = bufnr()
+
+ call assert_fails("normal [f", "E1513:")
+ call assert_equal(l:buffer, bufnr())
+
+ set nowinfixbuf
+
+ normal [f
+ call assert_notequal(l:buffer, bufnr())
+
+ call delete(l:file)
+endfunc
+
+" Fail to go to a C macro with [<C-d> if 'winfixbuf' is enabled
+func Test_normal_square_bracket_left_ctrl_d()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile(["min(1, 12);",
+ \ '#include "' . l:include_file . '"'
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+ normal ]\<C-d>
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal [\<C-d>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set nowinfixbuf
+
+ execute "normal [\<C-d>"
+ call assert_notequal(l:current, bufnr())
+
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail to go to a C macro with ]<C-d> if 'winfixbuf' is enabled
+func Test_normal_square_bracket_right_ctrl_d()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile(["min(1, 12);",
+ \ '#include "' . l:include_file . '"'
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal ]\<C-d>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set nowinfixbuf
+
+ execute "normal ]\<C-d>"
+ call assert_notequal(l:current, bufnr())
+
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail to go to a C macro with [<C-i> if 'winfixbuf' is enabled
+func Test_normal_square_bracket_left_ctrl_i()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile(['#include "' . l:include_file . '"',
+ \ "min(1, 12);",
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+ " Move to the line with `min(1, 12);` on it"
+ normal j
+
+ set define=^\\s*#\\s*define
+ set include=^\\s*#\\s*include
+ set path=.,/usr/include,,
+
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("normal [\<C-i>", "E1513:")
+
+ set nowinfixbuf
+
+ execute "normal [\<C-i>"
+ call assert_notequal(l:current, bufnr())
+
+ set define&
+ set include&
+ set path&
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail to go to a C macro with ]<C-i> if 'winfixbuf' is enabled
+func Test_normal_square_bracket_right_ctrl_i()
+ call s:reset_all_buffers()
+
+ let l:include_file = tempname() . ".h"
+ call writefile(["min(1, 12);",
+ \ '#include "' . l:include_file . '"'
+ \ ],
+ \ "main.c")
+ call writefile(["#define min(X, Y) ((X) < (Y) ? (X) : (Y))"], l:include_file)
+ edit main.c
+
+ set winfixbuf
+
+ set define=^\\s*#\\s*define
+ set include=^\\s*#\\s*include
+ set path=.,/usr/include,,
+
+ let l:current = bufnr()
+
+ call assert_fails("normal ]\<C-i>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set nowinfixbuf
+
+ execute "normal ]\<C-i>"
+ call assert_notequal(l:current, bufnr())
+
+ set define&
+ set include&
+ set path&
+ call delete("main.c")
+ call delete(l:include_file)
+endfunc
+
+" Fail "goto file under the cursor" (using ]f, which is the same as `:normal gf`)
+func Test_normal_square_bracket_right_f()
+ call s:reset_all_buffers()
+
+ let l:file = tempname()
+ call append(0, [l:file])
+ call writefile([], l:file)
+ " Place the cursor onto the line that has `l:file`
+ normal gg
+ " Prevent Vim from erroring with "No write since last change @ command
+ " line" when we try to call gf, later.
+ set hidden
+
+ set winfixbuf
+
+ let l:buffer = bufnr()
+
+ call assert_fails("normal ]f", "E1513:")
+ call assert_equal(l:buffer, bufnr())
+
+ set nowinfixbuf
+
+ normal ]f
+ call assert_notequal(l:buffer, bufnr())
+
+ call delete(l:file)
+endfunc
+
+" Fail to jump to a tag with v<C-]> if 'winfixbuf' is enabled
+func Test_normal_v_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal v\<C-]>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail to jump to a tag with vg<C-]> if 'winfixbuf' is enabled
+func Test_normal_v_g_ctrl_square_bracket_right()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("normal vg\<C-]>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Allow :pedit because, unlike :edit, it uses a separate window
+func Test_pedit()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+
+ pedit other
+
+ execute "normal \<C-w>w"
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :pop but :pop! is allowed
+func Test_pop()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "thesame\tXfile\t1;\"\td\tfile:",
+ \ "thesame\tXfile\t2;\"\td\tfile:",
+ \ "thesame\tXfile\t3;\"\td\tfile:",
+ \ ],
+ \ "Xtags")
+ call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
+ call writefile(["thesame one"], "Xother")
+ edit Xother
+
+ tag thesame
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("pop", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ pop!
+ call assert_notequal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail :previous but :previous! is allowed
+func Test_previous()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("previous", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ previous!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Fail pyxdo if it changes a window with 'winfixbuf' is set
+func Test_pythonx_pyxdo()
+ CheckFeature pythonx
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let g:_previous_buffer = bufnr()
+
+ enew
+ file second
+
+ set winfixbuf
+
+ pythonx << EOF
+import vim
+
+def test_winfixbuf_Test_pythonx_pyxdo_set_buffer():
+ buffer = vim.vars['_previous_buffer']
+ vim.current.buffer = vim.buffers[buffer]
+EOF
+
+ try
+ pyxdo test_winfixbuf_Test_pythonx_pyxdo_set_buffer()
+ catch /Vim\%((\a\+)\)\=:E1513:/
+ let l:caught = 1
+ endtry
+
+ call assert_equal(1, l:caught)
+
+ unlet g:_previous_buffer
+endfunc
+
+" Fail pyxfile if it changes a window with 'winfixbuf' is set
+func Test_pythonx_pyxfile()
+ CheckFeature pythonx
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let g:_previous_buffer = bufnr()
+
+ enew
+ file second
+
+ set winfixbuf
+
+ call writefile(["import vim",
+ \ "buffer = vim.vars['_previous_buffer']",
+ \ "vim.current.buffer = vim.buffers[buffer]",
+ \ ],
+ \ "file.py")
+
+ try
+ pyxfile file.py
+ catch /Vim\%((\a\+)\)\=:E1513:/
+ let l:caught = 1
+ endtry
+
+ call assert_equal(1, l:caught)
+
+ call delete("file.py")
+ unlet g:_previous_buffer
+endfunc
+
+" Fail vim.current.buffer if 'winfixbuf' is set
+func Test_pythonx_vim_current_buffer()
+ CheckFeature pythonx
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let g:_previous_buffer = bufnr()
+
+ enew
+ file second
+
+ let l:caught = 0
+
+ set winfixbuf
+
+ try
+ pythonx << EOF
+import vim
+
+buffer = vim.vars["_previous_buffer"]
+vim.current.buffer = vim.buffers[buffer]
+EOF
+ catch /Vim\%((\a\+)\)\=:E1513:/
+ let l:caught = 1
+ endtry
+
+ call assert_equal(1, l:caught)
+ unlet g:_previous_buffer
+endfunc
+
+" Ensure remapping to a disabled action still triggers failures
+func Test_remap_key_fail()
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file current
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ nnoremap g <C-^>
+
+ call assert_fails("normal g", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ nunmap g
+endfunc
+
+" Ensure remapping a disabled key to something valid does trigger any failures
+func Test_remap_key_pass()
+ call s:reset_all_buffers()
+
+ enew
+ file first
+ let l:first = bufnr()
+
+ enew
+ file current
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ call assert_fails("normal \<C-^>", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ " Disallow <C-^> by default but allow it if the command does something else
+ nnoremap <C-^> :echo "hello!"
+
+ execute "normal \<C-^>"
+ call assert_equal(l:current, bufnr())
+
+ nunmap <C-^>
+endfunc
+
+" Fail :rewind but :rewind! is allowed
+func Test_rewind()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("rewind", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ rewind!
+ call assert_equal(l:first, bufnr())
+endfunc
+
+" Allow :sblast because it opens the buffer in a new, split window
+func Test_sblast()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs(1)
+ bfirst!
+ let l:current = bufnr()
+
+ sblast
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :sbprevious but :sbprevious! is allowed
+func Test_sbprevious()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ sbprevious
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Make sure 'winfixbuf' can be set using 'winfixbuf' or 'wfb'
+func Test_short_option()
+ call s:reset_all_buffers()
+
+ call s:make_buffer_pairs()
+
+ set winfixbuf
+ call assert_fails("edit something_else", "E1513")
+
+ set nowinfixbuf
+ set wfb
+ call assert_fails("edit another_place", "E1513")
+
+ set nowfb
+ edit last_place
+endfunc
+
+" Allow :snext because it makes a new window
+func Test_snext()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ first!
+
+ let l:current_window = win_getid()
+
+ snext
+ call assert_notequal(l:current_window, win_getid())
+ call assert_notequal(l:first, bufnr())
+endfunc
+
+" Ensure the first has 'winfixbuf' and a new split window is 'nowinfixbuf'
+func Test_split_window()
+ call s:reset_all_buffers()
+
+ split
+ execute "normal \<C-w>j"
+
+ set winfixbuf
+
+ let l:winfix_window_1 = win_getid()
+ vsplit
+ let l:winfix_window_2 = win_getid()
+
+ call assert_equal(1, getwinvar(l:winfix_window_1, "&winfixbuf"))
+ call assert_equal(0, getwinvar(l:winfix_window_2, "&winfixbuf"))
+endfunc
+
+" Fail :tNext but :tNext! is allowed
+func Test_tNext()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "thesame\tXfile\t1;\"\td\tfile:",
+ \ "thesame\tXfile\t2;\"\td\tfile:",
+ \ "thesame\tXfile\t3;\"\td\tfile:",
+ \ ],
+ \ "Xtags")
+ call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
+ call writefile(["thesame one"], "Xother")
+ edit Xother
+
+ tag thesame
+ execute "normal \<C-^>"
+ tnext!
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tNext", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tNext!
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Call :tabdo and choose the next available 'nowinfixbuf' window.
+func Test_tabdo_choose_available_window()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+
+ " Make a split window that is 'nowinfixbuf' but make it the second-to-last
+ " window so that :tabdo will first try the 'winfixbuf' window, pass over it,
+ " and prefer the other 'nowinfixbuf' window, instead.
+ "
+ " +-------------------+
+ " | 'nowinfixbuf' |
+ " +-------------------+
+ " | 'winfixbuf' | <-- Cursor is here
+ " +-------------------+
+ split
+ let l:nowinfixbuf_window = win_getid()
+ " Move to the 'winfixbuf' window now
+ execute "normal \<C-w>j"
+ let l:winfixbuf_window = win_getid()
+
+ let l:expected_windows = s:get_windows_count()
+ tabdo echo ''
+ call assert_equal(l:nowinfixbuf_window, win_getid())
+ call assert_equal(l:first, bufnr())
+ call assert_equal(l:expected_windows, s:get_windows_count())
+endfunc
+
+" Call :tabdo and create a new split window if all available windows are 'winfixbuf'.
+func Test_tabdo_make_new_window()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_buffers_list()
+ execute "buffer! " . l:first
+
+ let l:current = win_getid()
+ let l:current_windows = s:get_windows_count()
+
+ tabdo echo ''
+ call assert_notequal(l:current, win_getid())
+ call assert_equal(l:first, bufnr())
+ execute "normal \<C-w>j"
+ call assert_equal(l:first, bufnr())
+ call assert_equal(l:current_windows + 1, s:get_windows_count())
+endfunc
+
+" Fail :tag but :tag! is allowed
+func Test_tag()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tag one", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tag! one
+ call assert_notequal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+
+" Fail :tfirst but :tfirst! is allowed
+func Test_tfirst()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tfirst", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tfirst!
+ call assert_notequal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail :tjump but :tjump! is allowed
+func Test_tjump()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ call writefile(["one"], "Xother")
+ edit Xother
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tjump one", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tjump! one
+ call assert_notequal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail :tlast but :tlast! is allowed
+func Test_tlast()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "one\tXfile\t1",
+ \ "three\tXfile\t3",
+ \ "two\tXfile\t2"],
+ \ "Xtags")
+ call writefile(["one", "two", "three"], "Xfile")
+ edit Xfile
+ tjump one
+ edit Xfile
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tlast", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tlast!
+ call assert_equal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+endfunc
+
+" Fail :tnext but :tnext! is allowed
+func Test_tnext()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "thesame\tXfile\t1;\"\td\tfile:",
+ \ "thesame\tXfile\t2;\"\td\tfile:",
+ \ "thesame\tXfile\t3;\"\td\tfile:",
+ \ ],
+ \ "Xtags")
+ call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
+ call writefile(["thesame one"], "Xother")
+ edit Xother
+
+ tag thesame
+ execute "normal \<C-^>"
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tnext", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tnext!
+ call assert_notequal(l:current, bufnr())
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail :tprevious but :tprevious! is allowed
+func Test_tprevious()
+ call s:reset_all_buffers()
+
+ set tags=Xtags
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "thesame\tXfile\t1;\"\td\tfile:",
+ \ "thesame\tXfile\t2;\"\td\tfile:",
+ \ "thesame\tXfile\t3;\"\td\tfile:",
+ \ ],
+ \ "Xtags")
+ call writefile(["thesame one", "thesame two", "thesame three"], "Xfile")
+ call writefile(["thesame one"], "Xother")
+ edit Xother
+
+ tag thesame
+ execute "normal \<C-^>"
+ tnext!
+
+ set winfixbuf
+
+ let l:current = bufnr()
+
+ call assert_fails("tprevious", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ tprevious!
+
+ set tags&
+ call delete("Xtags")
+ call delete("Xfile")
+ call delete("Xother")
+endfunc
+
+" Fail :view but :view! is allowed
+func Test_view()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("view other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ view! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :visual but :visual! is allowed
+func Test_visual()
+ call s:reset_all_buffers()
+
+ let l:other = s:make_buffer_pairs()
+ let l:current = bufnr()
+
+ call assert_fails("visual other", "E1513:")
+ call assert_equal(l:current, bufnr())
+
+ visual! other
+ call assert_equal(l:other, bufnr())
+endfunc
+
+" Fail :vimgrep but :vimgrep! is allowed
+func Test_vimgrep()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ edit winfix.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ edit! last.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:last = bufnr()
+
+ buffer! winfix.unittest
+
+ call assert_fails("vimgrep /some-search-term/ *.unittest")
+ call assert_equal(l:current, bufnr())
+
+ " Don't error and also do swap to the first match because ! was included
+ vimgrep! /some-search-term/ *.unittest
+ call assert_notequal(l:current, bufnr())
+
+ call delete("first.unittest")
+ call delete("winfix.unittest")
+ call delete("last.unittest")
+endfunc
+
+" Fail :vimgrepadd but ::vimgrepadd! is allowed
+func Test_vimgrepadd()
+ CheckFeature quickfix
+ call s:reset_all_buffers()
+
+ edit first.unittest
+ call append(0, ["some-search-term"])
+ write
+
+ edit winfix.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:current = bufnr()
+
+ set winfixbuf
+
+ edit! last.unittest
+ call append(0, ["some-search-term"])
+ write
+ let l:last = bufnr()
+
+ buffer! winfix.unittest
+
+ call assert_fails("vimgrepadd /some-search-term/ *.unittest")
+ call assert_equal(l:current, bufnr())
+
+ vimgrepadd! /some-search-term/ *.unittest
+ call assert_notequal(l:current, bufnr())
+ call delete("first.unittest")
+ call delete("winfix.unittest")
+ call delete("last.unittest")
+endfunc
+
+" Fail :wNext but :wNext! is allowed
+func Test_wNext()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("wNext", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ wNext!
+ call assert_equal(l:first, bufnr())
+
+ call delete("first")
+ call delete("middle")
+ call delete("last")
+endfunc
+
+" Allow :windo unless `:windo foo` would change a 'winfixbuf' window's buffer
+func Test_windo()
+ call s:reset_all_buffers()
+
+ let l:current_window = win_getid()
+ let l:current_buffer = bufnr()
+ split
+ enew
+ file some_other_buffer
+
+ set winfixbuf
+
+ let l:current = win_getid()
+
+ windo echo ''
+ call assert_equal(l:current_window, win_getid())
+
+ call assert_fails('execute "windo buffer ' . l:current_buffer . '"', "E1513:")
+ call assert_equal(l:current_window, win_getid())
+
+ execute "windo buffer! " . l:current_buffer
+ call assert_equal(l:current_window, win_getid())
+endfunc
+
+" Fail :wnext but :wnext! is allowed
+func Test_wnext()
+ call s:reset_all_buffers()
+
+ let [_, l:last] = s:make_args_list()
+ next!
+
+ call assert_fails("wnext", "E1513:")
+ call assert_notequal(l:last, bufnr())
+
+ wnext!
+ call assert_equal(l:last, bufnr())
+
+ call delete("first")
+ call delete("middle")
+ call delete("last")
+endfunc
+
+" Fail :wprevious but :wprevious! is allowed
+func Test_wprevious()
+ call s:reset_all_buffers()
+
+ let [l:first, _] = s:make_args_list()
+ next!
+
+ call assert_fails("wprevious", "E1513:")
+ call assert_notequal(l:first, bufnr())
+
+ wprevious!
+ call assert_equal(l:first, bufnr())
+
+ call delete("first")
+ call delete("middle")
+ call delete("last")
+endfunc
+
+func Test_quickfix_switchbuf_invalid_prevwin()
+ call s:reset_all_buffers()
+
+ call s:make_simple_quickfix()
+ call assert_equal(1, getqflist(#{idx: 0}).idx)
+
+ set switchbuf=uselast
+ split
+ copen
+ execute winnr('#') 'quit'
+ call assert_equal(2, winnr('$'))
+
+ cnext " Would've triggered a null pointer member access
+ call assert_equal(2, getqflist(#{idx: 0}).idx)
+
+ set switchbuf&
+endfunc
+
+func Test_listdo_goto_prevwin()
+ call s:reset_all_buffers()
+ call s:make_buffers_list()
+
+ new
+ call assert_equal(0, &winfixbuf)
+ wincmd p
+ call assert_equal(1, &winfixbuf)
+ call assert_notequal(bufnr(), bufnr('#'))
+
+ augroup ListDoGotoPrevwin
+ au!
+ au BufLeave * let s:triggered = 1
+ \| call assert_equal(bufnr(), winbufnr(winnr()))
+ augroup END
+ " Should correctly switch to the window without 'winfixbuf', and curbuf should
+ " be consistent with curwin->w_buffer for autocommands.
+ bufdo "
+ call assert_equal(0, &winfixbuf)
+ call assert_equal(1, s:triggered)
+ unlet! s:triggered
+ au! ListDoGotoPrevwin
+
+ set winfixbuf
+ wincmd p
+ call assert_equal(2, winnr('$'))
+ " Both curwin and prevwin have 'winfixbuf' set, so should split a new window
+ " without it set.
+ bufdo "
+ call assert_equal(0, &winfixbuf)
+ call assert_equal(3, winnr('$'))
+
+ quit
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, &winfixbuf)
+ augroup ListDoGotoPrevwin
+ au!
+ au WinEnter * ++once set winfixbuf
+ augroup END
+ " Same as before, but naughty autocommands set 'winfixbuf' for the new window.
+ " :bufdo should give up in this case.
+ call assert_fails('bufdo "', 'E1513:')
+
+ au! ListDoGotoPrevwin
+ augroup! ListDoGotoPrevwin
+endfunc
+
+func Test_quickfix_changed_split_failed()
+ call s:reset_all_buffers()
+
+ call s:make_simple_quickfix()
+ call assert_equal(1, winnr('$'))
+
+ " Quickfix code will open a split in an attempt to get a 'nowinfixbuf' window
+ " to switch buffers in. Interfere with things by setting 'winfixbuf' in it.
+ augroup QfChanged
+ au!
+ au WinEnter * ++once call assert_equal(2, winnr('$'))
+ \| set winfixbuf | call setqflist([], 'f')
+ augroup END
+ call assert_fails('cnext', ['E1513:', 'E925:'])
+ " Check that the split was automatically closed.
+ call assert_equal(1, winnr('$'))
+
+ au! QfChanged
+ augroup! QfChanged
+endfunc
+
+func Test_bufdo_cnext_splitwin_fails()
+ call s:reset_all_buffers()
+ call s:make_simple_quickfix()
+ call assert_equal(1, getqflist(#{idx: 0}).idx)
+ " Make sure there is not enough room to
+ " split the winfixedbuf window
+ let &winheight=&lines
+ let &winminheight=&lines-2
+ " Still want E1513, or it may not be clear why a split was attempted and why
+ " it failing caused the commands to abort.
+ call assert_fails(':bufdo echo 1', ['E36:', 'E1513:'])
+ call assert_fails(':cnext', ['E36:', 'E1513:'])
+ " Ensure the entry didn't change.
+ call assert_equal(1, getqflist(#{idx: 0}).idx)
+ set winminheight&vim winheight&vim
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab