aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-11-30 10:43:38 +0800
committerGitHub <noreply@github.com>2022-11-30 10:43:38 +0800
commitbfdddec8bab85340b4b293f1467a56f495e26c2f (patch)
treef99301f305306dcfa08db2bce4638f42449989aa /src
parentd311c5481dede47d752fa39cb00d728ba18ef5bc (diff)
parent22622df950fe90abcaaf2e6710cd6cd9451cdc34 (diff)
downloadrneovim-bfdddec8bab85340b4b293f1467a56f495e26c2f.tar.gz
rneovim-bfdddec8bab85340b4b293f1467a56f495e26c2f.tar.bz2
rneovim-bfdddec8bab85340b4b293f1467a56f495e26c2f.zip
Merge pull request #21238 from zeertzjq/vim-8.2.5080
vim-patch:8.2.{5080,5082,5102,5103,5104,5105,5108}: when indenting gets out of hand it is hard to stop
Diffstat (limited to 'src')
-rw-r--r--src/nvim/ex_cmds.c181
-rw-r--r--src/nvim/generators/gen_ex_cmds.lua1
-rw-r--r--src/nvim/indent.c194
-rw-r--r--src/nvim/testdir/runtest.vim23
-rw-r--r--src/nvim/testdir/test_escaped_glob.vim2
-rw-r--r--src/nvim/testdir/test_getcwd.vim2
-rw-r--r--src/nvim/testdir/test_hide.vim2
-rw-r--r--src/nvim/testdir/test_retab.vim32
-rw-r--r--src/nvim/testing.c1
9 files changed, 234 insertions, 204 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 96e61c13fb..8ae7646268 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -719,187 +719,6 @@ sortend:
}
}
-/// ":retab".
-void ex_retab(exarg_T *eap)
-{
- linenr_T lnum;
- bool got_tab = false;
- long num_spaces = 0;
- long num_tabs;
- long len;
- long col;
- long vcol;
- long start_col = 0; // For start of white-space string
- long start_vcol = 0; // For start of white-space string
- long old_len;
- char *ptr;
- char *new_line = (char *)1; // init to non-NULL
- bool did_undo; // called u_save for current line
- long *new_vts_array = NULL;
- char *new_ts_str; // string value of tab argument
-
- int save_list;
- linenr_T first_line = 0; // first changed line
- linenr_T last_line = 0; // last changed line
-
- save_list = curwin->w_p_list;
- curwin->w_p_list = 0; // don't want list mode here
-
- new_ts_str = eap->arg;
- if (!tabstop_set(eap->arg, &new_vts_array)) {
- return;
- }
- while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
- (eap->arg)++;
- }
-
- // This ensures that either new_vts_array and new_ts_str are freshly
- // allocated, or new_vts_array points to an existing array and new_ts_str
- // is null.
- if (new_vts_array == NULL) {
- new_vts_array = curbuf->b_p_vts_array;
- new_ts_str = NULL;
- } else {
- new_ts_str = xstrnsave(new_ts_str, (size_t)(eap->arg - new_ts_str));
- }
- for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
- ptr = ml_get(lnum);
- col = 0;
- vcol = 0;
- did_undo = false;
- for (;;) {
- if (ascii_iswhite(ptr[col])) {
- if (!got_tab && num_spaces == 0) {
- // First consecutive white-space
- start_vcol = vcol;
- start_col = col;
- }
- if (ptr[col] == ' ') {
- num_spaces++;
- } else {
- got_tab = true;
- }
- } else {
- if (got_tab || (eap->forceit && num_spaces > 1)) {
- // Retabulate this string of white-space
-
- // len is virtual length of white string
- len = num_spaces = vcol - start_vcol;
- num_tabs = 0;
- if (!curbuf->b_p_et) {
- int t, s;
-
- tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
- curbuf->b_p_ts, new_vts_array, &t, &s);
- num_tabs = t;
- num_spaces = s;
- }
- if (curbuf->b_p_et || got_tab
- || (num_spaces + num_tabs < len)) {
- if (did_undo == false) {
- did_undo = true;
- if (u_save((linenr_T)(lnum - 1),
- (linenr_T)(lnum + 1)) == FAIL) {
- new_line = NULL; // flag out-of-memory
- break;
- }
- }
-
- // len is actual number of white characters used
- len = num_spaces + num_tabs;
- old_len = (long)strlen(ptr);
- const long new_len = old_len - col + start_col + len + 1;
- if (new_len <= 0 || new_len >= MAXCOL) {
- emsg(_(e_resulting_text_too_long));
- break;
- }
- new_line = xmalloc((size_t)new_len);
-
- if (start_col > 0) {
- memmove(new_line, ptr, (size_t)start_col);
- }
- memmove(new_line + start_col + len,
- ptr + col, (size_t)(old_len - col + 1));
- ptr = new_line + start_col;
- for (col = 0; col < len; col++) {
- ptr[col] = (col < num_tabs) ? '\t' : ' ';
- }
- if (ml_replace(lnum, new_line, false) == OK) {
- // "new_line" may have been copied
- new_line = curbuf->b_ml.ml_line_ptr;
- extmark_splice_cols(curbuf, lnum - 1, 0, (colnr_T)old_len,
- (colnr_T)new_len - 1, kExtmarkUndo);
- }
- if (first_line == 0) {
- first_line = lnum;
- }
- last_line = lnum;
- ptr = new_line;
- col = start_col + len;
- }
- }
- got_tab = false;
- num_spaces = 0;
- }
- if (ptr[col] == NUL) {
- break;
- }
- vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
- if (vcol >= MAXCOL) {
- emsg(_(e_resulting_text_too_long));
- break;
- }
- col += utfc_ptr2len(ptr + col);
- }
- if (new_line == NULL) { // out of memory
- break;
- }
- line_breakcheck();
- }
- if (got_int) {
- emsg(_(e_interr));
- }
-
- // If a single value was given then it can be considered equal to
- // either the value of 'tabstop' or the value of 'vartabstop'.
- if (tabstop_count(curbuf->b_p_vts_array) == 0
- && tabstop_count(new_vts_array) == 1
- && curbuf->b_p_ts == tabstop_first(new_vts_array)) {
- // not changed
- } else if (tabstop_count(curbuf->b_p_vts_array) > 0
- && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
- // not changed
- } else {
- redraw_curbuf_later(UPD_NOT_VALID);
- }
- if (first_line != 0) {
- changed_lines(first_line, 0, last_line + 1, 0L, true);
- }
-
- curwin->w_p_list = save_list; // restore 'list'
-
- if (new_ts_str != NULL) { // set the new tabstop
- // If 'vartabstop' is in use or if the value given to retab has more
- // than one tabstop then update 'vartabstop'.
- long *old_vts_ary = curbuf->b_p_vts_array;
-
- if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) {
- set_string_option_direct("vts", -1, new_ts_str, OPT_FREE | OPT_LOCAL, 0);
- curbuf->b_p_vts_array = new_vts_array;
- xfree(old_vts_ary);
- } else {
- // 'vartabstop' wasn't in use and a single value was given to
- // retab then update 'tabstop'.
- curbuf->b_p_ts = tabstop_first(new_vts_array);
- xfree(new_vts_array);
- }
- xfree(new_ts_str);
- }
- coladvance(curwin->w_curswant);
-
- u_clearline();
-}
-
/// :move command - move lines line1-line2 to line dest
///
/// @return FAIL for failure, OK otherwise
diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua
index ac60b9f8e9..3a022d45c8 100644
--- a/src/nvim/generators/gen_ex_cmds.lua
+++ b/src/nvim/generators/gen_ex_cmds.lua
@@ -66,6 +66,7 @@ defsfile:write(string.format([[
#include "nvim/ex_session.h"
#include "nvim/hardcopy.h"
#include "nvim/help.h"
+#include "nvim/indent.h"
#include "nvim/locale.h"
#include "nvim/lua/executor.h"
#include "nvim/mapping.h"
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 1905128c3c..0ffa152af7 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -13,6 +13,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval_defs.h"
@@ -27,6 +28,8 @@
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
@@ -915,6 +918,197 @@ bool may_do_si(void)
return curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL && !p_paste;
}
+/// Give a "resulting text too long" error and maybe set got_int.
+static void emsg_text_too_long(void)
+{
+ emsg(_(e_resulting_text_too_long));
+ // when not inside a try/catch set got_int to break out of any loop
+ if (trylevel == 0) {
+ got_int = true;
+ }
+}
+
+/// ":retab".
+void ex_retab(exarg_T *eap)
+{
+ linenr_T lnum;
+ bool got_tab = false;
+ long num_spaces = 0;
+ long num_tabs;
+ long len;
+ long col;
+ long vcol;
+ long start_col = 0; // For start of white-space string
+ long start_vcol = 0; // For start of white-space string
+ long old_len;
+ char *ptr;
+ char *new_line = (char *)1; // init to non-NULL
+ bool did_undo; // called u_save for current line
+ long *new_vts_array = NULL;
+ char *new_ts_str; // string value of tab argument
+
+ int save_list;
+ linenr_T first_line = 0; // first changed line
+ linenr_T last_line = 0; // last changed line
+
+ save_list = curwin->w_p_list;
+ curwin->w_p_list = 0; // don't want list mode here
+
+ new_ts_str = eap->arg;
+ if (!tabstop_set(eap->arg, &new_vts_array)) {
+ return;
+ }
+ while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
+ (eap->arg)++;
+ }
+
+ // This ensures that either new_vts_array and new_ts_str are freshly
+ // allocated, or new_vts_array points to an existing array and new_ts_str
+ // is null.
+ if (new_vts_array == NULL) {
+ new_vts_array = curbuf->b_p_vts_array;
+ new_ts_str = NULL;
+ } else {
+ new_ts_str = xstrnsave(new_ts_str, (size_t)(eap->arg - new_ts_str));
+ }
+ for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
+ ptr = ml_get(lnum);
+ col = 0;
+ vcol = 0;
+ did_undo = false;
+ for (;;) {
+ if (ascii_iswhite(ptr[col])) {
+ if (!got_tab && num_spaces == 0) {
+ // First consecutive white-space
+ start_vcol = vcol;
+ start_col = col;
+ }
+ if (ptr[col] == ' ') {
+ num_spaces++;
+ } else {
+ got_tab = true;
+ }
+ } else {
+ if (got_tab || (eap->forceit && num_spaces > 1)) {
+ // Retabulate this string of white-space
+
+ // len is virtual length of white string
+ len = num_spaces = vcol - start_vcol;
+ num_tabs = 0;
+ if (!curbuf->b_p_et) {
+ int t, s;
+
+ tabstop_fromto((colnr_T)start_vcol, (colnr_T)vcol,
+ curbuf->b_p_ts, new_vts_array, &t, &s);
+ num_tabs = t;
+ num_spaces = s;
+ }
+ if (curbuf->b_p_et || got_tab
+ || (num_spaces + num_tabs < len)) {
+ if (did_undo == false) {
+ did_undo = true;
+ if (u_save((linenr_T)(lnum - 1),
+ (linenr_T)(lnum + 1)) == FAIL) {
+ new_line = NULL; // flag out-of-memory
+ break;
+ }
+ }
+
+ // len is actual number of white characters used
+ len = num_spaces + num_tabs;
+ old_len = (long)strlen(ptr);
+ const long new_len = old_len - col + start_col + len + 1;
+ if (new_len <= 0 || new_len >= MAXCOL) {
+ emsg_text_too_long();
+ break;
+ }
+ new_line = xmalloc((size_t)new_len);
+
+ if (start_col > 0) {
+ memmove(new_line, ptr, (size_t)start_col);
+ }
+ memmove(new_line + start_col + len,
+ ptr + col, (size_t)(old_len - col + 1));
+ ptr = new_line + start_col;
+ for (col = 0; col < len; col++) {
+ ptr[col] = (col < num_tabs) ? '\t' : ' ';
+ }
+ if (ml_replace(lnum, new_line, false) == OK) {
+ // "new_line" may have been copied
+ new_line = curbuf->b_ml.ml_line_ptr;
+ extmark_splice_cols(curbuf, lnum - 1, 0, (colnr_T)old_len,
+ (colnr_T)new_len - 1, kExtmarkUndo);
+ }
+ if (first_line == 0) {
+ first_line = lnum;
+ }
+ last_line = lnum;
+ ptr = new_line;
+ col = start_col + len;
+ }
+ }
+ got_tab = false;
+ num_spaces = 0;
+ }
+ if (ptr[col] == NUL) {
+ break;
+ }
+ vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
+ if (vcol >= MAXCOL) {
+ emsg_text_too_long();
+ break;
+ }
+ col += utfc_ptr2len(ptr + col);
+ }
+ if (new_line == NULL) { // out of memory
+ break;
+ }
+ line_breakcheck();
+ }
+ if (got_int) {
+ emsg(_(e_interr));
+ }
+
+ // If a single value was given then it can be considered equal to
+ // either the value of 'tabstop' or the value of 'vartabstop'.
+ if (tabstop_count(curbuf->b_p_vts_array) == 0
+ && tabstop_count(new_vts_array) == 1
+ && curbuf->b_p_ts == tabstop_first(new_vts_array)) {
+ // not changed
+ } else if (tabstop_count(curbuf->b_p_vts_array) > 0
+ && tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
+ // not changed
+ } else {
+ redraw_curbuf_later(UPD_NOT_VALID);
+ }
+ if (first_line != 0) {
+ changed_lines(first_line, 0, last_line + 1, 0L, true);
+ }
+
+ curwin->w_p_list = save_list; // restore 'list'
+
+ if (new_ts_str != NULL) { // set the new tabstop
+ // If 'vartabstop' is in use or if the value given to retab has more
+ // than one tabstop then update 'vartabstop'.
+ long *old_vts_ary = curbuf->b_p_vts_array;
+
+ if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1) {
+ set_string_option_direct("vts", -1, new_ts_str, OPT_FREE | OPT_LOCAL, 0);
+ curbuf->b_p_vts_array = new_vts_array;
+ xfree(old_vts_ary);
+ } else {
+ // 'vartabstop' wasn't in use and a single value was given to
+ // retab then update 'tabstop'.
+ curbuf->b_p_ts = tabstop_first(new_vts_array);
+ xfree(new_vts_array);
+ }
+ xfree(new_ts_str);
+ }
+ coladvance(curwin->w_curswant);
+
+ u_clearline();
+}
+
/// Get indent level from 'indentexpr'.
int get_expr_indent(void)
{
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index a1b04a4601..15f66fc1ad 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -171,6 +171,7 @@ func RunTheTest(test)
endtry
endif
+ au VimLeavePre * call EarlyExit(g:testfunc)
if a:test =~ 'Test_nocatch_'
" Function handles errors itself. This avoids skipping commands after the
" error.
@@ -182,10 +183,7 @@ func RunTheTest(test)
endif
else
try
- let s:test = a:test
- au VimLeavePre * call EarlyExit(s:test)
exe 'call ' . a:test
- au! VimLeavePre
catch /^\cskipped/
call add(s:messages, ' Skipped')
call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', ''))
@@ -193,6 +191,7 @@ func RunTheTest(test)
call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint)
endtry
endif
+ au! VimLeavePre
" In case 'insertmode' was set and something went wrong, make sure it is
" reset to avoid trouble with anything else.
@@ -255,11 +254,11 @@ func AfterTheTest(func_name)
if len(v:errors) > 0
if match(s:may_fail_list, '^' .. a:func_name) >= 0
let s:fail_expected += 1
- call add(s:errors_expected, 'Found errors in ' . s:test . ':')
+ call add(s:errors_expected, 'Found errors in ' . g:testfunc . ':')
call extend(s:errors_expected, v:errors)
else
let s:fail += 1
- call add(s:errors, 'Found errors in ' . s:test . ':')
+ call add(s:errors, 'Found errors in ' . g:testfunc . ':')
call extend(s:errors, v:errors)
endif
let v:errors = []
@@ -420,12 +419,12 @@ endif
let s:may_fail_list = []
if $TEST_MAY_FAIL != ''
- " Split the list at commas and add () to make it match s:test.
+ " Split the list at commas and add () to make it match g:testfunc.
let s:may_fail_list = split($TEST_MAY_FAIL, ',')->map({i, v -> v .. '()'})
endif
" Execute the tests in alphabetical order.
-for s:test in sort(s:tests)
+for g:testfunc in sort(s:tests)
" Silence, please!
set belloff=all
let prev_error = ''
@@ -435,7 +434,7 @@ for s:test in sort(s:tests)
" A test can set g:test_is_flaky to retry running the test.
let g:test_is_flaky = 0
- call RunTheTest(s:test)
+ call RunTheTest(g:testfunc)
" Repeat a flaky test. Give up when:
" - $TEST_NO_RETRY is not empty
@@ -443,10 +442,10 @@ for s:test in sort(s:tests)
" - it fails five times (with a different message)
if len(v:errors) > 0
\ && $TEST_NO_RETRY == ''
- \ && (index(s:flaky_tests, s:test) >= 0
+ \ && (index(s:flaky_tests, g:testfunc) >= 0
\ || g:test_is_flaky)
while 1
- call add(s:messages, 'Found errors in ' . s:test . ':')
+ call add(s:messages, 'Found errors in ' . g:testfunc . ':')
call extend(s:messages, v:errors)
call add(total_errors, 'Run ' . g:run_nr . ':')
@@ -469,7 +468,7 @@ for s:test in sort(s:tests)
let v:errors = []
let g:run_nr += 1
- call RunTheTest(s:test)
+ call RunTheTest(g:testfunc)
if len(v:errors) == 0
" Test passed on rerun.
@@ -478,7 +477,7 @@ for s:test in sort(s:tests)
endwhile
endif
- call AfterTheTest(s:test)
+ call AfterTheTest(g:testfunc)
endfor
call FinishTesting()
diff --git a/src/nvim/testdir/test_escaped_glob.vim b/src/nvim/testdir/test_escaped_glob.vim
index 1a4fd8bdab..9f53c76a2c 100644
--- a/src/nvim/testdir/test_escaped_glob.vim
+++ b/src/nvim/testdir/test_escaped_glob.vim
@@ -1,7 +1,7 @@
" Test whether glob()/globpath() return correct results with certain escaped
" characters.
-function SetUp()
+func SetUp()
" consistent sorting of file names
set nofileignorecase
endfunction
diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim
index a75583cd2c..073f8303dc 100644
--- a/src/nvim/testdir/test_getcwd.vim
+++ b/src/nvim/testdir/test_getcwd.vim
@@ -24,7 +24,7 @@ endfunc
" Do all test in a separate window to avoid E211 when we recursively
" delete the Xtopdir directory during cleanup
-function SetUp()
+func SetUp()
set visualbell
set nocp viminfo+=nviminfo
diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim
index 41b1a4ad7c..b3ce395523 100644
--- a/src/nvim/testdir/test_hide.vim
+++ b/src/nvim/testdir/test_hide.vim
@@ -1,6 +1,6 @@
" Tests for :hide command/modifier and 'hidden' option
-function SetUp()
+func SetUp()
let s:save_hidden = &hidden
let s:save_bufhidden = &bufhidden
let s:save_autowrite = &autowrite
diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim
index 1650a03876..a4f95053c0 100644
--- a/src/nvim/testdir/test_retab.vim
+++ b/src/nvim/testdir/test_retab.vim
@@ -1,4 +1,7 @@
" Test :retab
+
+source check.vim
+
func SetUp()
new
call setline(1, "\ta \t b c ")
@@ -81,19 +84,32 @@ func Test_retab_error()
call assert_fails('ret 80000000000000000000', 'E475:')
endfunc
+func RetabLoop()
+ while 1
+ set ts=4000
+ retab 4
+ endwhile
+endfunc
+
func Test_retab_endless()
- new
+ " inside try/catch we can catch the error message
call setline(1, "\t0\t")
let caught = 'no'
try
- while 1
- set ts=4000
- retab 4
- endwhile
- catch /E1240/
- let caught = 'yes'
+ call RetabLoop()
+ catch /E1240:/
+ let caught = v:exception
endtry
- bwipe!
+ call assert_match('E1240:', caught)
+
+ set tabstop&
+endfunc
+
+func Test_nocatch_retab_endless()
+ " when not inside try/catch an interrupt is generated to get out of loops
+ call setline(1, "\t0\t")
+ call assert_fails('call RetabLoop()', ['E1240:', 'Interrupted'])
+
set tabstop&
endfunc
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 3617670da9..8c54d9ad32 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -606,6 +606,7 @@ theend:
suppress_errthrow = false;
in_assert_fails = false;
did_emsg = false;
+ got_int = false;
msg_col = 0;
no_wait_return--;
need_wait_return = false;