From e3f615b205c8675b209e609d6426e2b138ceab7a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Dec 2021 07:41:23 +0800 Subject: vim-patch:8.1.1434: test 3 is old style Problem: Test 3 is old style. Solution: Turn into a new style test. (Yegappan Lakshmanan, closes vim/vim#4460) https://github.com/vim/vim/commit/1ab74a5af36933f1c3023a910af20280bb79bd6c --- src/nvim/testdir/test_cindent.vim | 5125 ++++++++++++++++++++++++++++++++++++- 1 file changed, 5124 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 562867f548..8a575c7cd8 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -1,6 +1,5 @@ " Test for cinoptions and cindent " -" TODO: rewrite test3.in into this new style test func Test_cino_hash() " Test that curbuf->b_ind_hash_comment is correctly reset @@ -128,6 +127,5130 @@ func Test_cindent_func() bwipe! endfunc +func Test_cindent_1() + new + setl cindent ts=4 sw=4 + setl cino& sts& + + let code =<< trim [CODE] + /* start of AUTO matically checked vim: set ts=4 : */ + { + if (test) + cmd1; + cmd2; + } + + { + if (test) + cmd1; + else + cmd2; + } + + { + if (test) + { + cmd1; + cmd2; + } + } + + { + if (test) + { + cmd1; + else + } + } + + { + while (this) + if (test) + cmd1; + cmd2; + } + + { + while (this) + if (test) + cmd1; + else + cmd2; + } + + { + if (test) + { + cmd; + } + + if (test) + cmd; + } + + { + if (test) { + cmd; + } + + if (test) cmd; + } + + { + cmd1; + for (blah) + while (this) + if (test) + cmd2; + cmd3; + } + + { + cmd1; + for (blah) + while (this) + if (test) + cmd2; + cmd3; + + if (test) + { + cmd1; + cmd2; + cmd3; + } + } + + + /* Test for 'cindent' do/while mixed with if/else: */ + + { + do + if (asdf) + asdfasd; + while (cond); + + do + if (asdf) + while (asdf) + asdf; + while (asdf); + } + + /* Test for 'cindent' with two ) on a continuation line */ + { + if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d + aal;sdkjf ( ;asldfkja;sldfk + al;sdjfka ;slkdf ) sa;ldkjfsa dlk;) + line up here; + } + + + /* C++ tests: */ + + // foo() these three lines should remain in column 0 + // { + // } + + /* Test for continuation and unterminated lines: */ + { + i = 99 + 14325 + + 21345 + + 21345 + + 21345 + ( 21345 + + 21345) + + 2345 + + 1234; + c = 1; + } + + /* + testje for indent with empty line + + here */ + + { + if (testing && + not a joke || + line up here) + hay; + if (testing && + (not a joke || testing + )line up here) + hay; + if (testing && + (not a joke || testing + line up here)) + hay; + } + + + { + switch (c) + { + case xx: + do + if (asdf) + do + asdfasdf; + while (asdf); + else + asdfasdf; + while (cond); + case yy: + case xx: + case zz: + testing; + } + } + + { + if (cond) { + foo; + } + else + { + bar; + } + } + + { + if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf + alsdkfj (asldk;fj + awith cino=(0 ;lf this one goes to below the paren with == + ;laksjfd ;lsakdjf ;alskdf asd) + asdfasdf;))) + asdfasdf; + } + + int + func(a, b) + int a; + int c; + { + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3) + ) + } + + { + while (asd) + { + if (asdf) + if (test) + if (that) + { + if (asdf) + do + cdasd; + while (as + df); + } + else + if (asdf) + asdf; + else + asdf; + asdf; + } + } + + { + s = "/*"; b = ';' + s = "/*"; b = ';'; + a = b; + } + + { + switch (a) + { + case a: + switch (t) + { + case 1: + cmd; + break; + case 2: + cmd; + break; + } + cmd; + break; + case b: + { + int i; + cmd; + } + break; + case c: { + int i; + cmd; + } + case d: if (cond && + test) { /* this line doesn't work right */ + int i; + cmd; + } + break; + } + } + + { + if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) && + (bp_to->b_p_initialized || + (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) + return; + label : + asdf = asdf ? + asdf : asdf; + asdf = asdf ? + asdf: asdf; + } + + /* Special Comments : This function has the added complexity (compared */ + /* : to addtolist) of having to check for a detail */ + /* : texture and add that to the list first. */ + + char *(array[100]) = { + "testje", + "foo", + "bar", + } + + enum soppie + { + yes = 0, + no, + maybe + }; + + typedef enum soppie + { + yes = 0, + no, + maybe + }; + + static enum + { + yes = 0, + no, + maybe + } soppie; + + public static enum + { + yes = 0, + no, + maybe + } soppie; + + static private enum + { + yes = 0, + no, + maybe + } soppie; + + { + int a, + b; + } + + { + struct Type + { + int i; + char *str; + } var[] = + { + 0, "zero", + 1, "one", + 2, "two", + 3, "three" + }; + + float matrix[3][3] = + { + { + 0, + 1, + 2 + }, + { + 3, + 4, + 5 + }, + { + 6, + 7, + 8 + } + }; + } + + { + /* blah ( blah */ + /* where does this go? */ + + /* blah ( blah */ + cmd; + + func(arg1, + /* comment */ + arg2); + a; + { + b; + { + c; /* Hey, NOW it indents?! */ + } + } + + { + func(arg1, + arg2, + arg3); + /* Hey, what am I doing here? Is this coz of the ","? */ + } + } + + main () + { + if (cond) + { + a = b; + } + if (cond) { + a = c; + } + if (cond) + a = d; + return; + } + + { + case 2: if (asdf && + asdfasdf) + aasdf; + a = 9; + case 3: if (asdf) + aasdf; + a = 9; + case 4: x = 1; + y = 2; + + label: if (asdf) + here; + + label: if (asdf && + asdfasdf) + { + } + + label: if (asdf && + asdfasdf) { + there; + } + + label: if (asdf && + asdfasdf) + there; + } + + { + /* + hello with ":set comments= cino=c5" + */ + + /* + hello with ":set comments= cino=" + */ + } + + + { + if (a < b) { + a = a + 1; + } else + a = a + 2; + + if (a) + do { + testing; + } while (asdfasdf); + a = b + 1; + asdfasdf + } + + { + for ( int i = 0; + i < 10; i++ ) + { + } + i = 0; + } + + class bob + { + int foo() {return 1;} + int bar; + } + + main() + { + while(1) + if (foo) + { + bar; + } + else { + asdf; + } + misplacedline; + } + + { + if (clipboard.state == SELECT_DONE + && ((row == clipboard.start.lnum + && col >= clipboard.start.col) + || row > clipboard.start.lnum)) + } + + { + if (1) {i += 4;} + where_am_i; + return 0; + } + + { + { + } // sdf(asdf + if (asdf) + asd; + } + + { + label1: + label2: + } + + { + int fooRet = foo(pBar1, false /*fKB*/, + true /*fPTB*/, 3 /*nT*/, false /*fDF*/); + f() { + for ( i = 0; + i < m; + /* c */ i++ ) { + a = b; + } + } + } + + { + f1(/*comment*/); + f2(); + } + + { + do { + if (foo) { + } else + ; + } while (foo); + foo(); // was wrong + } + + int x; // no extra indent because of the ; + void func() + { + } + + char *tab[] = {"aaa", + "};", /* }; */ NULL} + int indented; + {} + + char *a[] = {"aaa", "bbb", + "ccc", NULL}; + // here + + char *tab[] = {"aaa", + "xx", /* xx */}; /* asdf */ + int not_indented; + + { + do { + switch (bla) + { + case 1: if (foo) + bar; + } + } while (boo); + wrong; + } + + int foo, + bar; + int foo; + + #if defined(foo) \ + && defined(bar) + char * xx = "asdf\ + foo\ + bor"; + int x; + + char *foo = "asdf\ + asdf\ + asdf", + *bar; + + void f() + { + #if defined(foo) \ + && defined(bar) + char *foo = "asdf\ + asdf\ + asdf", + *bar; + { + int i; + char *foo = "asdf\ + asdf\ + asdf", + *bar; + } + #endif + } + #endif + + int y; // comment + // comment + + // comment + + { + Constructor(int a, + int b ) : BaseClass(a) + { + } + } + + void foo() + { + char one, + two; + struct bla piet, + jan; + enum foo kees, + jannie; + static unsigned sdf, + krap; + unsigned int piet, + jan; + int + kees, + jan; + } + + { + t(int f, + int d); // ) + d(); + } + + Constructor::Constructor(int a, + int b + ) : + BaseClass(a, + b, + c), + mMember(b), + { + } + + Constructor::Constructor(int a, + int b ) : + BaseClass(a) + { + } + + Constructor::Constructor(int a, + int b ) /*x*/ : /*x*/ BaseClass(a), + member(b) + { + } + + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + + class CAbc : + public BaseClass1, + protected BaseClass2 + { + int Test() { return FALSE; } + int Test1() { return TRUE; } + + CAbc(int a, int b ) : + BaseClass(a) + { + switch(xxx) + { + case abc: + asdf(); + break; + + case 999: + baer(); + break; + } + } + + public: // <-- this was incoreectly indented before!! + void testfall(); + protected: + void testfall(); + }; + + class CAbc : public BaseClass1, + protected BaseClass2 + { + }; + + static struct + { + int a; + int b; + } variable[COUNT] = + { + { + 123, + 456 + }, + { + 123, + 456 + } + }; + + static struct + { + int a; + int b; + } variable[COUNT] = + { + { 123, 456 }, + { 123, 456 } + }; + + void asdf() /* ind_maxparen may cause trouble here */ + { + if ((0 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1)) break; + } + + foo() + { + a = cond ? foo() : asdf + + asdf; + + a = cond ? + foo() : asdf + + asdf; + } + + int main(void) + { + if (a) + if (b) + 2; + else 3; + next_line_of_code(); + } + + barry() + { + Foo::Foo (int one, + int two) + : something(4) + {} + } + + barry() + { + Foo::Foo (int one, int two) + : something(4) + {} + } + + Constructor::Constructor(int a, + int b + ) : + BaseClass(a, + b, + c), + mMember(b) + { + } + int main () + { + if (lala) + do + ++(*lolo); + while (lili + && lele); + lulu; + } + + int main () + { + switch (c) + { + case 'c': if (cond) + { + } + } + } + + main() + { + (void) MyFancyFuasdfadsfnction( + argument); + } + + main() + { + char foo[] = "/*"; + /* as + df */ + hello + } + + /* valid namespaces with normal indent */ + namespace + { + { + 111111111111; + } + } + namespace /* test */ + { + 11111111111111111; + } + namespace // test + { + 111111111111111111; + } + namespace + { + 111111111111111111; + } + namespace test + { + 111111111111111111; + } + namespace{ + 111111111111111111; + } + namespace test{ + 111111111111111111; + } + namespace { + 111111111111111111; + } + namespace test { + 111111111111111111; + namespace test2 { + 22222222222222222; + } + } + + /* invalid namespaces use block indent */ + namespace test test2 { + 111111111111111111111; + } + namespace11111111111 { + 111111111111; + } + namespace() { + 1111111111111; + } + namespace() + { + 111111111111111111; + } + namespace test test2 + { + 1111111111111111111; + } + namespace111111111 + { + 111111111111111111; + } + + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } + + /* end of AUTO */ + [CODE] + + call append(0, code) + normal gg + call search('start of AUTO') + exe "normal =/end of AUTO\" + + let expected =<< trim [CODE] + /* start of AUTO matically checked vim: set ts=4 : */ + { + if (test) + cmd1; + cmd2; + } + + { + if (test) + cmd1; + else + cmd2; + } + + { + if (test) + { + cmd1; + cmd2; + } + } + + { + if (test) + { + cmd1; + else + } + } + + { + while (this) + if (test) + cmd1; + cmd2; + } + + { + while (this) + if (test) + cmd1; + else + cmd2; + } + + { + if (test) + { + cmd; + } + + if (test) + cmd; + } + + { + if (test) { + cmd; + } + + if (test) cmd; + } + + { + cmd1; + for (blah) + while (this) + if (test) + cmd2; + cmd3; + } + + { + cmd1; + for (blah) + while (this) + if (test) + cmd2; + cmd3; + + if (test) + { + cmd1; + cmd2; + cmd3; + } + } + + + /* Test for 'cindent' do/while mixed with if/else: */ + + { + do + if (asdf) + asdfasd; + while (cond); + + do + if (asdf) + while (asdf) + asdf; + while (asdf); + } + + /* Test for 'cindent' with two ) on a continuation line */ + { + if (asdfasdf;asldkfj asdlkfj as;ldkfj sal;d + aal;sdkjf ( ;asldfkja;sldfk + al;sdjfka ;slkdf ) sa;ldkjfsa dlk;) + line up here; + } + + + /* C++ tests: */ + + // foo() these three lines should remain in column 0 + // { + // } + + /* Test for continuation and unterminated lines: */ + { + i = 99 + 14325 + + 21345 + + 21345 + + 21345 + ( 21345 + + 21345) + + 2345 + + 1234; + c = 1; + } + + /* + testje for indent with empty line + + here */ + + { + if (testing && + not a joke || + line up here) + hay; + if (testing && + (not a joke || testing + )line up here) + hay; + if (testing && + (not a joke || testing + line up here)) + hay; + } + + + { + switch (c) + { + case xx: + do + if (asdf) + do + asdfasdf; + while (asdf); + else + asdfasdf; + while (cond); + case yy: + case xx: + case zz: + testing; + } + } + + { + if (cond) { + foo; + } + else + { + bar; + } + } + + { + if (alskdfj ;alsdkfjal;skdjf (;sadlkfsa ;dlkf j;alksdfj ;alskdjf + alsdkfj (asldk;fj + awith cino=(0 ;lf this one goes to below the paren with == + ;laksjfd ;lsakdjf ;alskdf asd) + asdfasdf;))) + asdfasdf; + } + + int + func(a, b) + int a; + int c; + { + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3) + ) + } + + { + while (asd) + { + if (asdf) + if (test) + if (that) + { + if (asdf) + do + cdasd; + while (as + df); + } + else + if (asdf) + asdf; + else + asdf; + asdf; + } + } + + { + s = "/*"; b = ';' + s = "/*"; b = ';'; + a = b; + } + + { + switch (a) + { + case a: + switch (t) + { + case 1: + cmd; + break; + case 2: + cmd; + break; + } + cmd; + break; + case b: + { + int i; + cmd; + } + break; + case c: { + int i; + cmd; + } + case d: if (cond && + test) { /* this line doesn't work right */ + int i; + cmd; + } + break; + } + } + + { + if (!(vim_strchr(p_cpo, CPO_BUFOPTGLOB) != NULL && entering) && + (bp_to->b_p_initialized || + (!entering && vim_strchr(p_cpo, CPO_BUFOPT) != NULL))) + return; + label : + asdf = asdf ? + asdf : asdf; + asdf = asdf ? + asdf: asdf; + } + + /* Special Comments : This function has the added complexity (compared */ + /* : to addtolist) of having to check for a detail */ + /* : texture and add that to the list first. */ + + char *(array[100]) = { + "testje", + "foo", + "bar", + } + + enum soppie + { + yes = 0, + no, + maybe + }; + + typedef enum soppie + { + yes = 0, + no, + maybe + }; + + static enum + { + yes = 0, + no, + maybe + } soppie; + + public static enum + { + yes = 0, + no, + maybe + } soppie; + + static private enum + { + yes = 0, + no, + maybe + } soppie; + + { + int a, + b; + } + + { + struct Type + { + int i; + char *str; + } var[] = + { + 0, "zero", + 1, "one", + 2, "two", + 3, "three" + }; + + float matrix[3][3] = + { + { + 0, + 1, + 2 + }, + { + 3, + 4, + 5 + }, + { + 6, + 7, + 8 + } + }; + } + + { + /* blah ( blah */ + /* where does this go? */ + + /* blah ( blah */ + cmd; + + func(arg1, + /* comment */ + arg2); + a; + { + b; + { + c; /* Hey, NOW it indents?! */ + } + } + + { + func(arg1, + arg2, + arg3); + /* Hey, what am I doing here? Is this coz of the ","? */ + } + } + + main () + { + if (cond) + { + a = b; + } + if (cond) { + a = c; + } + if (cond) + a = d; + return; + } + + { + case 2: if (asdf && + asdfasdf) + aasdf; + a = 9; + case 3: if (asdf) + aasdf; + a = 9; + case 4: x = 1; + y = 2; + + label: if (asdf) + here; + + label: if (asdf && + asdfasdf) + { + } + + label: if (asdf && + asdfasdf) { + there; + } + + label: if (asdf && + asdfasdf) + there; + } + + { + /* + hello with ":set comments= cino=c5" + */ + + /* + hello with ":set comments= cino=" + */ + } + + + { + if (a < b) { + a = a + 1; + } else + a = a + 2; + + if (a) + do { + testing; + } while (asdfasdf); + a = b + 1; + asdfasdf + } + + { + for ( int i = 0; + i < 10; i++ ) + { + } + i = 0; + } + + class bob + { + int foo() {return 1;} + int bar; + } + + main() + { + while(1) + if (foo) + { + bar; + } + else { + asdf; + } + misplacedline; + } + + { + if (clipboard.state == SELECT_DONE + && ((row == clipboard.start.lnum + && col >= clipboard.start.col) + || row > clipboard.start.lnum)) + } + + { + if (1) {i += 4;} + where_am_i; + return 0; + } + + { + { + } // sdf(asdf + if (asdf) + asd; + } + + { + label1: + label2: + } + + { + int fooRet = foo(pBar1, false /*fKB*/, + true /*fPTB*/, 3 /*nT*/, false /*fDF*/); + f() { + for ( i = 0; + i < m; + /* c */ i++ ) { + a = b; + } + } + } + + { + f1(/*comment*/); + f2(); + } + + { + do { + if (foo) { + } else + ; + } while (foo); + foo(); // was wrong + } + + int x; // no extra indent because of the ; + void func() + { + } + + char *tab[] = {"aaa", + "};", /* }; */ NULL} + int indented; + {} + + char *a[] = {"aaa", "bbb", + "ccc", NULL}; + // here + + char *tab[] = {"aaa", + "xx", /* xx */}; /* asdf */ + int not_indented; + + { + do { + switch (bla) + { + case 1: if (foo) + bar; + } + } while (boo); + wrong; + } + + int foo, + bar; + int foo; + + #if defined(foo) \ + && defined(bar) + char * xx = "asdf\ + foo\ + bor"; + int x; + + char *foo = "asdf\ + asdf\ + asdf", + *bar; + + void f() + { + #if defined(foo) \ + && defined(bar) + char *foo = "asdf\ + asdf\ + asdf", + *bar; + { + int i; + char *foo = "asdf\ + asdf\ + asdf", + *bar; + } + #endif + } + #endif + + int y; // comment + // comment + + // comment + + { + Constructor(int a, + int b ) : BaseClass(a) + { + } + } + + void foo() + { + char one, + two; + struct bla piet, + jan; + enum foo kees, + jannie; + static unsigned sdf, + krap; + unsigned int piet, + jan; + int + kees, + jan; + } + + { + t(int f, + int d); // ) + d(); + } + + Constructor::Constructor(int a, + int b + ) : + BaseClass(a, + b, + c), + mMember(b), + { + } + + Constructor::Constructor(int a, + int b ) : + BaseClass(a) + { + } + + Constructor::Constructor(int a, + int b ) /*x*/ : /*x*/ BaseClass(a), + member(b) + { + } + + A::A(int a, int b) + : aa(a), + bb(b), + cc(c) + { + } + + class CAbc : + public BaseClass1, + protected BaseClass2 + { + int Test() { return FALSE; } + int Test1() { return TRUE; } + + CAbc(int a, int b ) : + BaseClass(a) + { + switch(xxx) + { + case abc: + asdf(); + break; + + case 999: + baer(); + break; + } + } + + public: // <-- this was incoreectly indented before!! + void testfall(); + protected: + void testfall(); + }; + + class CAbc : public BaseClass1, + protected BaseClass2 + { + }; + + static struct + { + int a; + int b; + } variable[COUNT] = + { + { + 123, + 456 + }, + { + 123, + 456 + } + }; + + static struct + { + int a; + int b; + } variable[COUNT] = + { + { 123, 456 }, + { 123, 456 } + }; + + void asdf() /* ind_maxparen may cause trouble here */ + { + if ((0 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1 + && 1)) break; + } + + foo() + { + a = cond ? foo() : asdf + + asdf; + + a = cond ? + foo() : asdf + + asdf; + } + + int main(void) + { + if (a) + if (b) + 2; + else 3; + next_line_of_code(); + } + + barry() + { + Foo::Foo (int one, + int two) + : something(4) + {} + } + + barry() + { + Foo::Foo (int one, int two) + : something(4) + {} + } + + Constructor::Constructor(int a, + int b + ) : + BaseClass(a, + b, + c), + mMember(b) + { + } + int main () + { + if (lala) + do + ++(*lolo); + while (lili + && lele); + lulu; + } + + int main () + { + switch (c) + { + case 'c': if (cond) + { + } + } + } + + main() + { + (void) MyFancyFuasdfadsfnction( + argument); + } + + main() + { + char foo[] = "/*"; + /* as + df */ + hello + } + + /* valid namespaces with normal indent */ + namespace + { + { + 111111111111; + } + } + namespace /* test */ + { + 11111111111111111; + } + namespace // test + { + 111111111111111111; + } + namespace + { + 111111111111111111; + } + namespace test + { + 111111111111111111; + } + namespace{ + 111111111111111111; + } + namespace test{ + 111111111111111111; + } + namespace { + 111111111111111111; + } + namespace test { + 111111111111111111; + namespace test2 { + 22222222222222222; + } + } + + /* invalid namespaces use block indent */ + namespace test test2 { + 111111111111111111111; + } + namespace11111111111 { + 111111111111; + } + namespace() { + 1111111111111; + } + namespace() + { + 111111111111111111; + } + namespace test test2 + { + 1111111111111111111; + } + namespace111111111 + { + 111111111111111111; + } + + void getstring() { + /* Raw strings */ + const char* s = R"( + test { + # comment + field: 123 + } + )"; + } + + void getstring() { + const char* s = R"foo( + test { + # comment + field: 123 + } + )foo"; + } + + { + int a[4] = { + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 3, + }; + } + + { + a = b[2] + + 3; + } + + { + if (1) + /* aaaaa + * bbbbb + */ + a = 1; + } + + void func() + { + switch (foo) + { + case (bar): + if (baz()) + quux(); + break; + case (shmoo): + if (!bar) + { + } + case (foo1): + switch (bar) + { + case baz: + baz_f(); + break; + } + break; + default: + baz(); + baz(); + break; + } + } + + /* end of AUTO */ + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_2() + new + setl cindent ts=4 sw=4 + setl tw=0 noai fo=croq + let &wm = &columns - 20 + + let code =<< trim [CODE] + { + + /* this is + * a real serious important big + * comment + */ + /* insert " about life, the universe, and the rest" after "serious" */ + } + [CODE] + + call append(0, code) + normal gg + call search('serious', 'e') + normal a about life, the universe, and the rest + + let expected =<< trim [CODE] + { + + /* this is + * a real serious + * about life, the + * universe, and the + * rest important big + * comment + */ + /* insert " about life, the universe, and the rest" after "serious" */ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + set wm& + enew! | close +endfunc + +func Test_cindent_3() + new + setl nocindent ts=4 sw=4 + + let code =<< trim [CODE] + { + /* + * Testing for comments, without 'cin' set + */ + + /* + * what happens here? + */ + + /* + the end of the comment, try inserting a line below */ + + /* how about + this one */ + } + [CODE] + + call append(0, code) + normal gg + call search('comments') + normal joabout life + call search('happens') + normal jothere + call search('below') + normal oline + call search('this') + normal Ohello + + let expected =<< trim [CODE] + { + /* + * Testing for comments, without 'cin' set + */ + about life + + /* + * what happens here? + */ + there + + /* + the end of the comment, try inserting a line below */ + line + + /* how about + hello + this one */ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_4() + new + setl cindent ts=4 sw=4 + + let code =<< trim [CODE] + { + var = this + that + vec[0] * vec[0] + + vec[1] * vec[1] + + vec2[2] * vec[2]; + } + [CODE] + + call append(0, code) + normal gg + call search('vec2') + normal == + + let expected =<< trim [CODE] + { + var = this + that + vec[0] * vec[0] + + vec[1] * vec[1] + + vec2[2] * vec[2]; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_5() + new + setl cindent ts=4 sw=4 + setl cino=}4 + + let code =<< trim [CODE] + { + asdf asdflkajds f; + if (tes & ting) { + asdf asdf asdf ; + asdfa sdf asdf; + } + testing1; + if (tes & ting) + { + asdf asdf asdf ; + asdfa sdf asdf; + } + testing2; + } + [CODE] + + call append(0, code) + normal gg + call search('testing1') + exe "normal k2==/testing2\" + normal k2== + + let expected =<< trim [CODE] + { + asdf asdflkajds f; + if (tes & ting) { + asdf asdf asdf ; + asdfa sdf asdf; + } + testing1; + if (tes & ting) + { + asdf asdf asdf ; + asdfa sdf asdf; + } + testing2; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_6() + new + setl cindent ts=4 sw=4 + setl cino=(0,)20 + + let code =<< trim [CODE] + main ( int first_par, /* + * Comment for + * first par + */ + int second_par /* + * Comment for + * second par + */ + ) + { + func( first_par, /* + * Comment for + * first par + */ + second_par /* + * Comment for + * second par + */ + ); + + } + [CODE] + + call append(0, code) + normal gg + call search('main') + normal =][ + + let expected =<< trim [CODE] + main ( int first_par, /* + * Comment for + * first par + */ + int second_par /* + * Comment for + * second par + */ + ) + { + func( first_par, /* + * Comment for + * first par + */ + second_par /* + * Comment for + * second par + */ + ); + + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_7() + new + setl cindent ts=4 sw=4 + setl cino=es,n0s + + let code =<< trim [CODE] + main(void) + { + /* Make sure that cino=X0s is not parsed like cino=Xs. */ + if (cond) + foo(); + else + { + bar(); + } + } + [CODE] + + call append(0, code) + normal gg + call search('main') + normal =][ + + let expected =<< trim [CODE] + main(void) + { + /* Make sure that cino=X0s is not parsed like cino=Xs. */ + if (cond) + foo(); + else + { + bar(); + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_8() + new + setl cindent ts=4 sw=4 + setl cino= + + let code =<< trim [CODE] + + { + do + { + if () + { + if () + asdf; + else + asdf; + } + } while (); + cmd; /* this should go under the } */ + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + + { + do + { + if () + { + if () + asdf; + else + asdf; + } + } while (); + cmd; /* this should go under the } */ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_9() + new + setl cindent ts=4 sw=4 + + let code =<< trim [CODE] + + void f() + { + if ( k() ) { + l(); + + } else { /* Start (two words) end */ + m(); + } + + n(); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + + void f() + { + if ( k() ) { + l(); + + } else { /* Start (two words) end */ + m(); + } + + n(); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_10() + new + setl cindent ts=4 sw=4 + setl cino={s,e-s + + let code =<< trim [CODE] + + void f() + { + if ( k() ) + { + l(); + } else { /* Start (two words) end */ + m(); + } + n(); /* should be under the if () */ + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + + void f() + { + if ( k() ) + { + l(); + } else { /* Start (two words) end */ + m(); + } + n(); /* should be under the if () */ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_11() + new + setl cindent ts=4 sw=4 + setl cino={s,fs + + let code =<< trim [CODE] + void bar(void) + { + static array[2][2] = + { + { 1, 2 }, + { 3, 4 }, + } + + while (a) + { + foo(&a); + } + + { + int a; + { + a = a + 1; + } + } + b = a; + } + + void func(void) + { + a = 1; + { + b = 2; + } + c = 3; + d = 4; + } + /* foo */ + [CODE] + + call append(0, code) + normal gg + exe "normal ]]=/ foo\" + + let expected =<< trim [CODE] + void bar(void) + { + static array[2][2] = + { + { 1, 2 }, + { 3, 4 }, + } + + while (a) + { + foo(&a); + } + + { + int a; + { + a = a + 1; + } + } + b = a; + } + + void func(void) + { + a = 1; + { + b = 2; + } + c = 3; + d = 4; + } + /* foo */ + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_12() + new + setl cindent ts=4 sw=4 + setl cino= + + let code =<< trim [CODE] + a() + { + do { + a = a + + a; + } while ( a ); /* add text under this line */ + if ( a ) + a; + } + [CODE] + + call append(0, code) + normal gg + call search('while') + normal ohere + + let expected =<< trim [CODE] + a() + { + do { + a = a + + a; + } while ( a ); /* add text under this line */ + here + if ( a ) + a; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_13() + new + setl cindent ts=4 sw=4 + setl cino= com= + + let code =<< trim [CODE] + a() + { + label1: + /* hmm */ + // comment + } + [CODE] + + call append(0, code) + normal gg + call search('comment') + exe "normal olabel2: b();\rlabel3 /* post */:\r/* pre */ label4:\r" . + \ "f(/*com*/);\rif (/*com*/)\rcmd();" + + let expected =<< trim [CODE] + a() + { + label1: + /* hmm */ + // comment + label2: b(); + label3 /* post */: + /* pre */ label4: + f(/*com*/); + if (/*com*/) + cmd(); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_14() + new + setl cindent ts=4 sw=4 + setl comments& comments^=s:/*,m:**,ex:*/ + + let code =<< trim [CODE] + /* + * A simple comment + */ + + /* + ** A different comment + */ + [CODE] + + call append(0, code) + normal gg + call search('simple') + normal =5j + + let expected =<< trim [CODE] + /* + * A simple comment + */ + + /* + ** A different comment + */ + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_15() + new + setl cindent ts=4 sw=4 + setl cino=c0 + setl comments& comments-=s1:/* comments^=s0:/* + + let code =<< trim [CODE] + void f() + { + + /********* + A comment. + *********/ + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + + /********* + A comment. + *********/ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_16() + new + setl cindent ts=4 sw=4 + setl cino=c0,C1 + setl comments& comments-=s1:/* comments^=s0:/* + + let code =<< trim [CODE] + void f() + { + + /********* + A comment. + *********/ + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + + /********* + A comment. + *********/ + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_17() + new + setl cindent ts=4 sw=4 + setl cino= + + let code =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_18() + new + setl cindent ts=4 sw=4 + setl cino=(s + + let code =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_19() + new + setl cindent ts=4 sw=4 + set cino=(s,U1 + + let code =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + c = c1 && + ( + c2 || + c3 + ) && c4; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_20() + new + setl cindent ts=4 sw=4 + setl cino=(0 + + let code =<< trim [CODE] + void f() + { + if ( c1 + && ( c2 + || c3)) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + if ( c1 + && ( c2 + || c3)) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_21() + new + setl cindent ts=4 sw=4 + setl cino=(0,w1 + + let code =<< trim [CODE] + void f() + { + if ( c1 + && ( c2 + || c3)) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + if ( c1 + && ( c2 + || c3)) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_22() + new + setl cindent ts=4 sw=4 + setl cino=(s + + let code =<< trim [CODE] + void f() + { + c = c1 && ( + c2 || + c3 + ) && c4; + if ( + c1 && c2 + ) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + c = c1 && ( + c2 || + c3 + ) && c4; + if ( + c1 && c2 + ) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_23() + new + setl cindent ts=4 sw=4 + setl cino=(s,m1 + + let code =<< trim [CODE] + void f() + { + c = c1 && ( + c2 || + c3 + ) && c4; + if ( + c1 && c2 + ) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + c = c1 && ( + c2 || + c3 + ) && c4; + if ( + c1 && c2 + ) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_24() + new + setl cindent ts=4 sw=4 + setl cino=b1 + + let code =<< trim [CODE] + void f() + { + switch (x) + { + case 1: + a = b; + break; + default: + a = 0; + break; + } + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + switch (x) + { + case 1: + a = b; + break; + default: + a = 0; + break; + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_25() + new + setl cindent ts=4 sw=4 + setl cino=(0,W5 + + let code =<< trim [CODE] + void f() + { + invokeme( + argu, + ment); + invokeme( + argu, + ment + ); + invokeme(argu, + ment + ); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + invokeme( + argu, + ment); + invokeme( + argu, + ment + ); + invokeme(argu, + ment + ); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_26() + new + setl cindent ts=4 sw=4 + setl cino=/6 + + let code =<< trim [CODE] + void f() + { + statement; + // comment 1 + // comment 2 + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void f() + { + statement; + // comment 1 + // comment 2 + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_27() + new + setl cindent ts=4 sw=4 + setl cino= + + let code =<< trim [CODE] + void f() + { + statement; + // comment 1 + // comment 2 + } + [CODE] + + call append(0, code) + normal gg + exe "normal ]]/comment 1/+1\==" + + let expected =<< trim [CODE] + void f() + { + statement; + // comment 1 + // comment 2 + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_28() + new + setl cindent ts=4 sw=4 + setl cino=g0 + + let code =<< trim [CODE] + class CAbc + { + int Test() { return FALSE; } + + public: // comment + void testfall(); + protected: + void testfall(); + }; + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + class CAbc + { + int Test() { return FALSE; } + + public: // comment + void testfall(); + protected: + void testfall(); + }; + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_29() + new + setl cindent ts=4 sw=4 + setl cino=(0,gs,hs + + let code =<< trim [CODE] + class Foo : public Bar + { + public: + virtual void method1(void) = 0; + virtual void method2(int arg1, + int arg2, + int arg3) = 0; + }; + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + class Foo : public Bar + { + public: + virtual void method1(void) = 0; + virtual void method2(int arg1, + int arg2, + int arg3) = 0; + }; + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_30() + new + setl cindent ts=4 sw=4 + setl cino=+20 + + let code =<< trim [CODE] + void + foo() + { + if (a) + { + } else + asdf; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void + foo() + { + if (a) + { + } else + asdf; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_31() + new + setl cindent ts=4 sw=4 + setl cino=(0,W2s + + let code =<< trim [CODE] + + { + averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd( + asdasdf, + func(asdf, + asdfadsf), + asdfasdf + ); + + /* those are ugly, but consequent */ + + func()->asd(asdasdf, + averylongfunctionname( + abc, + dec)->averylongfunctionname( + asdfadsf, + asdfasdf, + asdfasdf, + ), + func(asdfadf, + asdfasdf + ), + asdasdf + ); + + averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf( + abc, + dec)->asdfasdfasdf( + asdfadsf, + asdfasdf, + asdfasdf, + ), + func(asdfadf, + asdfasdf), + asdasdf + ); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + + { + averylongfunctionnamelongfunctionnameaverylongfunctionname()->asd( + asdasdf, + func(asdf, + asdfadsf), + asdfasdf + ); + + /* those are ugly, but consequent */ + + func()->asd(asdasdf, + averylongfunctionname( + abc, + dec)->averylongfunctionname( + asdfadsf, + asdfasdf, + asdfasdf, + ), + func(asdfadf, + asdfasdf + ), + asdasdf + ); + + averylongfunctionnameaverylongfunctionnameavery()->asd(fasdf( + abc, + dec)->asdfasdfasdf( + asdfadsf, + asdfasdf, + asdfasdf, + ), + func(asdfadf, + asdfasdf), + asdasdf + ); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_32() + new + setl cindent ts=4 sw=4 + setl cino=M1 + + let code =<< trim [CODE] + int main () + { + if (cond1 && + cond2 + ) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + int main () + { + if (cond1 && + cond2 + ) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_33() + new + setl cindent ts=4 sw=4 + setl cino=(0,ts + + let code =<< trim [CODE] + void func(int a + #if defined(FOO) + , int b + , int c + #endif + ) + { + } + [CODE] + + call append(0, code) + normal gg + normal 2j=][ + + let expected =<< trim [CODE] + void func(int a + #if defined(FOO) + , int b + , int c + #endif + ) + { + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_34() + new + setl cindent ts=4 sw=4 + setl cino=(0 + + let code =<< trim [CODE] + + void + func(int a + #if defined(FOO) + , int b + , int c + #endif + ) + { + } + [CODE] + + call append(0, code) + normal gg + normal =][ + + let expected =<< trim [CODE] + + void + func(int a + #if defined(FOO) + , int b + , int c + #endif + ) + { + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_35() + new + setl cindent ts=4 sw=4 + setl cino& + + let code =<< trim [CODE] + void func(void) + { + if(x==y) + if(y==z) + foo=1; + else { bar=1; + baz=2; + } + printf("Foo!\n"); + } + + void func1(void) + { + char* tab[] = {"foo", "bar", + "baz", "quux", + "this line used", "to be indented incorrectly"}; + foo(); + } + + void func2(void) + { + int tab[] = + {1, 2, + 3, 4, + 5, 6}; + + printf("This line used to be indented incorrectly.\n"); + } + + int foo[] + #ifdef BAR + + = { 1, 2, 3, + 4, 5, 6 } + + #endif + ; + int baz; + + void func3(void) + { + int tab[] = { + 1, 2, + 3, 4, + 5, 6}; + + printf("Don't you dare indent this line incorrectly!\n"); + } + + void + func4(a, b, + c) + int a; + int b; + int c; + { + } + + void + func5( + int a, + int b) + { + } + + void + func6( + int a) + { + } + [CODE] + + call append(0, code) + normal gg + normal ]]=7][ + + let expected =<< trim [CODE] + void func(void) + { + if(x==y) + if(y==z) + foo=1; + else { bar=1; + baz=2; + } + printf("Foo!\n"); + } + + void func1(void) + { + char* tab[] = {"foo", "bar", + "baz", "quux", + "this line used", "to be indented incorrectly"}; + foo(); + } + + void func2(void) + { + int tab[] = + {1, 2, + 3, 4, + 5, 6}; + + printf("This line used to be indented incorrectly.\n"); + } + + int foo[] + #ifdef BAR + + = { 1, 2, 3, + 4, 5, 6 } + + #endif + ; + int baz; + + void func3(void) + { + int tab[] = { + 1, 2, + 3, 4, + 5, 6}; + + printf("Don't you dare indent this line incorrectly!\n"); + } + + void + func4(a, b, + c) + int a; + int b; + int c; + { + } + + void + func5( + int a, + int b) + { + } + + void + func6( + int a) + { + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_36() + new + setl cindent ts=4 sw=4 + setl cino& + setl cino+=l1 + + let code =<< trim [CODE] + void func(void) + { + int tab[] = + { + 1, 2, 3, + 4, 5, 6}; + + printf("Indent this line correctly!\n"); + + switch (foo) + { + case bar: + printf("bar"); + break; + case baz: { + printf("baz"); + break; + } + case quux: + printf("But don't break the indentation of this instruction\n"); + break; + } + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + int tab[] = + { + 1, 2, 3, + 4, 5, 6}; + + printf("Indent this line correctly!\n"); + + switch (foo) + { + case bar: + printf("bar"); + break; + case baz: { + printf("baz"); + break; + } + case quux: + printf("But don't break the indentation of this instruction\n"); + break; + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_37() + new + setl cindent ts=4 sw=4 + setl cino& + + let code =<< trim [CODE] + void func(void) + { + cout << "a" + << "b" + << ") :" + << "c"; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + cout << "a" + << "b" + << ") :" + << "c"; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_38() + new + setl cindent ts=4 sw=4 + setl com=s1:/*,m:*,ex:*/ + + let code =<< trim [CODE] + void func(void) + { + /* + * This is a comment. + */ + } + [CODE] + + call append(0, code) + normal gg + normal ]]3jofoo(); + + let expected =<< trim [CODE] + void func(void) + { + /* + * This is a comment. + */ + foo(); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_39() + new + setl cindent ts=4 sw=4 + setl cino& + + let code =<< trim [CODE] + void func(void) + { + for (int i = 0; i < 10; ++i) + if (i & 1) { + foo(1); + } else + foo(0); + baz(); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + for (int i = 0; i < 10; ++i) + if (i & 1) { + foo(1); + } else + foo(0); + baz(); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_40() + new + setl cindent ts=4 sw=4 + setl cino=k2s,(0 + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_41() + new + setl cindent ts=4 sw=4 + setl cino=k2s,(s + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_42() + new + setl cindent ts=4 sw=4 + setl cino=k2s,(s,U1 + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + c = c1 && + ( + c2 || + c3 + ) && c4; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + c = c1 && + ( + c2 || + c3 + ) && c4; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_43() + new + setl cindent ts=4 sw=4 + setl cino=k2s,(0,W4 + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + if ( c1 + && ( c2 + || c3)) + foo; + + a_long_line( + argument, + argument); + a_short_line(argument, + argument); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + if ( c1 + && ( c2 + || c3)) + foo; + + a_long_line( + argument, + argument); + a_short_line(argument, + argument); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_44() + new + setl cindent ts=4 sw=4 + setl cino=k2s,u2 + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_45() + new + setl cindent ts=4 sw=4 + setl cino=k2s,(0,w1 + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + if (c123456789 + && (c22345 + || c3)) + printf("foo\n"); + + if ( c1 + && ( c2 + || c3)) + foo; + func( c1 + && ( c2 + || c3)) + foo; + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_46() + new + setl cindent ts=4 sw=4 + setl cino=k2,(s + + let code =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + } + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + void func(void) + { + if (condition1 + && condition2) + action(); + function(argument1 + && argument2); + + if (c1 && (c2 || + c3)) + foo; + if (c1 && + (c2 || c3)) + { + } + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_47() + new + setl cindent ts=4 sw=4 + setl cino=N-s + + let code =<< trim [CODE] + NAMESPACESTART + /* valid namespaces with normal indent */ + namespace + { + { + 111111111111; + } + } + namespace /* test */ + { + 11111111111111111; + } + namespace // test + { + 111111111111111111; + } + namespace + { + 111111111111111111; + } + namespace test + { + 111111111111111111; + } + namespace test::cpp17 + { + 111111111111111111; + } + namespace ::incorrectcpp17 + { + 111111111111111111; + } + namespace test::incorrectcpp17:: + { + 111111111111111111; + } + namespace test:incorrectcpp17 + { + 111111111111111111; + } + namespace test:::incorrectcpp17 + { + 111111111111111111; + } + namespace{ + 111111111111111111; + } + namespace test{ + 111111111111111111; + } + namespace { + 111111111111111111; + } + namespace test { + 111111111111111111; + namespace test2 { + 22222222222222222; + } + } + + /* invalid namespaces use block indent */ + namespace test test2 { + 111111111111111111111; + } + namespace11111111111 { + 111111111111; + } + namespace() { + 1111111111111; + } + namespace() + { + 111111111111111111; + } + namespace test test2 + { + 1111111111111111111; + } + namespace111111111 + { + 111111111111111111; + } + NAMESPACEEND + [CODE] + + call append(0, code) + normal gg + call search('^NAMESPACESTART') + exe "normal =/^NAMESPACEEND\n" + + let expected =<< trim [CODE] + NAMESPACESTART + /* valid namespaces with normal indent */ + namespace + { + { + 111111111111; + } + } + namespace /* test */ + { + 11111111111111111; + } + namespace // test + { + 111111111111111111; + } + namespace + { + 111111111111111111; + } + namespace test + { + 111111111111111111; + } + namespace test::cpp17 + { + 111111111111111111; + } + namespace ::incorrectcpp17 + { + 111111111111111111; + } + namespace test::incorrectcpp17:: + { + 111111111111111111; + } + namespace test:incorrectcpp17 + { + 111111111111111111; + } + namespace test:::incorrectcpp17 + { + 111111111111111111; + } + namespace{ + 111111111111111111; + } + namespace test{ + 111111111111111111; + } + namespace { + 111111111111111111; + } + namespace test { + 111111111111111111; + namespace test2 { + 22222222222222222; + } + } + + /* invalid namespaces use block indent */ + namespace test test2 { + 111111111111111111111; + } + namespace11111111111 { + 111111111111; + } + namespace() { + 1111111111111; + } + namespace() + { + 111111111111111111; + } + namespace test test2 + { + 1111111111111111111; + } + namespace111111111 + { + 111111111111111111; + } + NAMESPACEEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_48() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + var bar = { + foo: { + that: this, + some: ok, + }, + "bar":{ + a : 2, + b: "123abc", + x: 4, + "y": 5 + } + } + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + var bar = { + foo: { + that: this, + some: ok, + }, + "bar":{ + a : 2, + b: "123abc", + x: 4, + "y": 5 + } + } + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_49() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + var foo = [ + 1, + 2, + 3 + ]; + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + var foo = [ + 1, + 2, + 3 + ]; + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_50() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + function bar() { + var foo = [ + 1, + 2, + 3 + ]; + } + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + function bar() { + var foo = [ + 1, + 2, + 3 + ]; + } + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_51() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + (function($){ + + if (cond && + cond) { + stmt; + } + window.something.left = + (width - 50 + offset) + "px"; + var class_name='myclass'; + + function private_method() { + } + + var public_method={ + method: function(options,args){ + private_method(); + } + } + + function init(options) { + + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + + $.fn[class_name]=function() { + + var _arguments=arguments; + return this.each(function(){ + + var options=$(this).data(class_name+'_public'); + if (!options) { + init.apply(this,_arguments); + + } else { + var method=public_method[_arguments[0]]; + + if (typeof(method)!='function') { + console.log(class_name+' has no method "'+_arguments[0]+'"'); + return false; + } + _arguments[0]=options; + method.apply(this,_arguments); + } + }); + } + + })(jQuery); + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + (function($){ + + if (cond && + cond) { + stmt; + } + window.something.left = + (width - 50 + offset) + "px"; + var class_name='myclass'; + + function private_method() { + } + + var public_method={ + method: function(options,args){ + private_method(); + } + } + + function init(options) { + + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + + $.fn[class_name]=function() { + + var _arguments=arguments; + return this.each(function(){ + + var options=$(this).data(class_name+'_public'); + if (!options) { + init.apply(this,_arguments); + + } else { + var method=public_method[_arguments[0]]; + + if (typeof(method)!='function') { + console.log(class_name+' has no method "'+_arguments[0]+'"'); + return false; + } + _arguments[0]=options; + method.apply(this,_arguments); + } + }); + } + + })(jQuery); + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_52() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + function init(options) { + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + function init(options) { + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_53() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1 + + let code =<< trim [CODE] + JSSTART + (function($){ + function init(options) { + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + })(jQuery); + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + (function($){ + function init(options) { + $(this).data(class_name+'_public',$.extend({},{ + foo: 'bar', + bar: 2, + foobar: [ + 1, + 2, + 3 + ], + callback: function(){ + return true; + } + }, options||{})); + } + })(jQuery); + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_54() + new + setl cindent ts=4 sw=4 + setl cino=j1,J1,+2 + + let code =<< trim [CODE] + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + [CODE] + + call append(0, code) + normal gg + call search('^JSSTART') + exe "normal =/^JSEND\n" + + let expected =<< trim [CODE] + JSSTART + // Results of JavaScript indent + // 1 + (function(){ + var a = [ + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 2 + (function(){ + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 3 + (function(){ + var a = [ + 0 + + // comment 1 + 5 * + /* comment 2 */ + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + }()) + + // 4 + { + var a = [ + 0, + 1 + ]; + var b; + var c; + } + + // 5 + { + var a = [ + [ + 0 + ], + 2, + 3 + ]; + } + + // 6 + { + var a = [ + [ + 0, + 1 + ], + 2, + 3 + ]; + } + + // 7 + { + var a = [ + // [ + 0, + // 1 + // ], + 2, + 3 + ]; + } + + // 8 + var x = [ + (function(){ + var a, + b, + c, + d, + e, + f, + g, + h, + i; + }) + ]; + + // 9 + var a = [ + 0 + + 5 * + 9 * + 'a', + 'b', + 0 + + 5 * + 9 * + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i' + ]; + + // 10 + var a, + b, + c, + d, + e, + f, + g, + h, + i; + JSEND + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_55() + new + setl cindent ts=4 sw=4 + setl cino& + + let code =<< trim [CODE] + /* start of define */ + { + } + #define AAA \ + BBB\ + CCC + + #define CNT \ + 1 + \ + 2 + \ + 4 + /* end of define */ + [CODE] + + call append(0, code) + normal gg + call search('start of define') + exe "normal =/end of define\n" + + let expected =<< trim [CODE] + /* start of define */ + { + } + #define AAA \ + BBB\ + CCC + + #define CNT \ + 1 + \ + 2 + \ + 4 + /* end of define */ + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + +func Test_cindent_56() + new + setl cindent ts=4 sw=4 + setl cino& + + let code =<< trim [CODE] + { + a = second/*bug*/*line; + } + [CODE] + + call append(0, code) + normal gg + call search('a = second') + normal ox + + let expected =<< trim [CODE] + { + a = second/*bug*/*line; + x + } + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + " this was going beyond the end of the line. func Test_cindent_case() new -- cgit From b8d6ab04a221b55ae240a85a0aba0b6a8b261fb4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Dec 2021 07:41:23 +0800 Subject: vim-patch:8.1.1585: :let-heredoc does not trim enough Problem: :let-heredoc does not trim enough. Solution: Trim indent from the contents based on the indent of the first line. Use let-heredoc in more tests. https://github.com/vim/vim/commit/e7eb92708ec2092a2fc11e78703b5dcf83844412 This is a missing part of Vim patch 8.1.1585 from #11211. --- src/nvim/testdir/test_cindent.vim | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 8a575c7cd8..bd556387c6 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -2069,14 +2069,14 @@ func Test_cindent_2() let &wm = &columns - 20 let code =<< trim [CODE] - { - - /* this is - * a real serious important big - * comment - */ - /* insert " about life, the universe, and the rest" after "serious" */ - } + { + + /* this is + * a real serious important big + * comment + */ + /* insert " about life, the universe, and the rest" after "serious" */ + } [CODE] call append(0, code) @@ -3243,32 +3243,32 @@ func Test_cindent_30() setl cindent ts=4 sw=4 setl cino=+20 - let code =<< trim [CODE] - void - foo() - { - if (a) - { - } else - asdf; - } - [CODE] + let code =<< [CODE] + void +foo() +{ + if (a) + { + } else + asdf; +} +[CODE] call append(0, code) normal gg normal ]]=][ - let expected =<< trim [CODE] - void - foo() - { - if (a) - { - } else - asdf; - } + let expected =<< [CODE] + void +foo() +{ + if (a) + { + } else + asdf; +} - [CODE] +[CODE] call assert_equal(expected, getline(1, '$')) enew! | close @@ -3461,7 +3461,7 @@ func Test_cindent_34() normal =][ let expected =<< trim [CODE] - + void func(int a #if defined(FOO) -- cgit From 6714ea35ac202eb7a3902eaff89840dfd8c80b8d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Dec 2021 07:41:23 +0800 Subject: vim-patch:8.2.3482: reading beyond end of line ending in quote and backslash Problem: Reading beyond end of line ending in quote and backslash. Solution: Check for non-NUL after backslash. (closes vim/vim#8964) https://github.com/vim/vim/commit/78e0fa4cf4fcd563c0bc8c87afa54d4f5dc22020 --- src/nvim/indent_c.c | 4 ++-- src/nvim/testdir/test_cindent.vim | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index faa9b38cf7..93bfe6d822 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -152,11 +152,11 @@ static char_u *skip_string(char_u *p) */ for (;; p++) { if (p[0] == '\'') { // 'c' or '\n' or '\000' - if (!p[1]) { // ' at end of line + if (p[1] == NUL) { // ' at end of line break; } i = 2; - if (p[1] == '\\') { // '\n' or '\000' + if (p[1] == '\\' && p[2] != NUL) { // '\n' or '\000' i++; while (ascii_isdigit(p[i - 1])) { // '\000' i++; diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index bd556387c6..055b578593 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -5296,4 +5296,11 @@ func Test_cindent_pragma() enew! | close endfunc +func Test_backslash_at_end_of_line() + new + exe "norm v>O'\\\-" + exe "norm \=" + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From de8a2f20e98be88b6568d5564e514f8d5fd43981 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Dec 2021 07:41:23 +0800 Subject: vim-patch:8.2.3625: illegal memory access when C-indenting Problem: Illegal memory access when C-indenting. Solution: Also set the cursor column. https://github.com/vim/vim/commit/2de9b7c7c8791da8853a9a7ca9c467867465b655 --- src/nvim/indent_c.c | 6 +++--- src/nvim/testdir/test_cindent.vim | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 93bfe6d822..8780d3253d 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -1412,8 +1412,8 @@ static int cin_skip2pos(pos_T *trypos) static pos_T *find_start_brace(void) // XXX { pos_T cursor_save; - pos_T *trypos; - pos_T *pos; + pos_T *trypos; + pos_T *pos; static pos_T pos_copy; cursor_save = curwin->w_cursor; @@ -1428,7 +1428,7 @@ static pos_T *find_start_brace(void) // XXX break; } if (pos != NULL) { - curwin->w_cursor.lnum = pos->lnum; + curwin->w_cursor = *pos; } } curwin->w_cursor = cursor_save; diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 055b578593..e8f448f96b 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -5303,4 +5303,16 @@ func Test_backslash_at_end_of_line() bwipe! endfunc +func Test_find_brace_backwards() + " this was looking beyond the end of the line + new + norm R/* + norm o0{ + norm o// + norm V{= + call assert_equal(['/*', ' 0{', '//'], getline(1, 3)) + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 67bf5c237f0bcb7323a550ff9791c96878d01816 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 23 Dec 2021 07:41:23 +0800 Subject: vim-patch:8.2.3876: 'cindent' does not recognize inline namespace Problem: 'cindent' does not recognize inline namespace. Solution: Skip over "inline" to find "namespace". (closes vim/vim#9383) https://github.com/vim/vim/commit/f2f0bddf303e37d4d532ca22e2d53179c20b1d19 --- src/nvim/indent_c.c | 5 ++++ src/nvim/testdir/test_cindent.vim | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 8780d3253d..eb1aa81e47 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -541,6 +541,11 @@ static bool cin_is_cpp_namespace(char_u *s) bool has_name_start = false; s = cin_skipcomment(s); + + if (STRNCMP(s, "inline", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) { + s = cin_skipcomment(skipwhite(s + 6)); + } + if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) { p = cin_skipcomment(skipwhite(s + 9)); while (*p != NUL) { diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index e8f448f96b..6554d034d3 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -997,6 +997,15 @@ func Test_cindent_1() 22222222222222222; } } + inline namespace { + 111111111111111111; + } + inline /* test */ namespace { + 111111111111111111; + } + inline/* test */namespace { + 111111111111111111; + } /* invalid namespaces use block indent */ namespace test test2 { @@ -1020,6 +1029,9 @@ func Test_cindent_1() { 111111111111111111; } + inlinenamespace { + 111111111111111111; + } void getstring() { /* Raw strings */ @@ -1962,6 +1974,15 @@ func Test_cindent_1() 22222222222222222; } } + inline namespace { + 111111111111111111; + } + inline /* test */ namespace { + 111111111111111111; + } + inline/* test */namespace { + 111111111111111111; + } /* invalid namespaces use block indent */ namespace test test2 { @@ -1985,6 +2006,9 @@ func Test_cindent_1() { 111111111111111111; } + inlinenamespace { + 111111111111111111; + } void getstring() { /* Raw strings */ @@ -4359,6 +4383,15 @@ func Test_cindent_47() 22222222222222222; } } + inline namespace { + 111111111111111111; + } + inline /* test */ namespace { + 111111111111111111; + } + inline/* test */namespace { + 111111111111111111; + } /* invalid namespaces use block indent */ namespace test test2 { @@ -4382,6 +4415,9 @@ func Test_cindent_47() { 111111111111111111; } + inlinenamespace { + 111111111111111111; + } NAMESPACEEND [CODE] @@ -4450,6 +4486,15 @@ func Test_cindent_47() 22222222222222222; } } + inline namespace { + 111111111111111111; + } + inline /* test */ namespace { + 111111111111111111; + } + inline/* test */namespace { + 111111111111111111; + } /* invalid namespaces use block indent */ namespace test test2 { @@ -4473,6 +4518,9 @@ func Test_cindent_47() { 111111111111111111; } + inlinenamespace { + 111111111111111111; + } NAMESPACEEND [CODE] -- cgit From 9d37b0998fae093b947d02d9543846a452a96070 Mon Sep 17 00:00:00 2001 From: kuuote Date: Thu, 30 Dec 2021 12:53:38 +0900 Subject: vim-patch:8.2.3571: some unicode control characters are considered printable Problem: Some unicode control characters are considered printable. Solution: Make 0x2060 - 0x2069 not printable. --- src/nvim/mbyte.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 5eb209a6f6..74a52e5614 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1067,7 +1067,7 @@ bool utf_printable(int c) static struct interval nonprint[] = { { 0x070f, 0x070f }, { 0x180b, 0x180e }, { 0x200b, 0x200f }, { 0x202a, 0x202e }, - { 0x206a, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb }, + { 0x2060, 0x206f }, { 0xd800, 0xdfff }, { 0xfeff, 0xfeff }, { 0xfff9, 0xfffb }, { 0xfffe, 0xffff } }; -- cgit From 4ddd6c53bd9ed33ba85d74f3774341705a0c1f4b Mon Sep 17 00:00:00 2001 From: Matt Wozniski Date: Tue, 4 Jan 2022 01:00:06 -0500 Subject: fix(screen): don't put empty sign text in line number column When `signcolumn=number` but no sign on a given line has any text, display the line's line number instead of the (empty) sign text in the line number column. --- src/nvim/screen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index b1ca8c5805..d4688f7782 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2792,7 +2792,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // in 'lnum', then display the sign instead of the line // number. if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' - && num_signs > 0) { + && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) { int count = win_signcol_count(wp); get_sign_display_info(true, wp, lnum, sattrs, row, startrow, filler_lines, filler_todo, count, -- cgit From 6e1a59da6c37e6785e9535af18de8846e55fcd81 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 5 Jan 2022 22:11:28 +0800 Subject: vim-patch:8.2.2887: crash when passing null string to fullcommand() Problem: Crash when passing null string to fullcommand(). Solution: Check for NULL pointer. (closes vim/vim#8256) https://github.com/vim/vim/commit/4c8e8c6e19b75d632b042aa0ba0a2ab769b2162e --- src/nvim/ex_docmd.c | 10 +++++++--- src/nvim/testdir/test_cmdline.vim | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 71c34f98ff..fc7e3e0c96 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2891,13 +2891,17 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) exarg_T ea; char_u *name = argvars[0].vval.v_string; - while (name[0] != NUL && name[0] == ':') { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (name == NULL) { + return; + } + + while (*name != NUL && *name == ':') { name++; } name = skip_range(name, NULL); - rettv->v_type = VAR_STRING; - ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; ea.cmdidx = (cmdidx_T)0; char_u *p = find_command(&ea, NULL); diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 1672b0e840..ef85c9e79a 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -500,6 +500,7 @@ func Test_fullcommand() for [in, want] in items(tests) call assert_equal(want, fullcommand(in)) endfor + call assert_equal('', fullcommand(v:_null_string)) call assert_equal('syntax', 'syn'->fullcommand()) endfunc -- cgit From bfe11dc8d08141fcb8b0fddda4526a995b3a5538 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 5 Jan 2022 22:11:28 +0800 Subject: vim-patch:8.2.3414: fullcommand() gives wrong name with buffer-local user command Problem: fullcommand() gives the wrong name if there is a buffer-local user command. (Naohiro Ono) Solution: Use a separate function to get the user command name. (closes vim/vim#8840) https://github.com/vim/vim/commit/80c88eac5a81dd9f1a96fc80cb8aab6c84fe7b86 --- src/nvim/ex_docmd.c | 24 +++++++++++++++++++++--- src/nvim/testdir/test_cmdline.vim | 7 +++++++ 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index fc7e3e0c96..157e82f585 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2910,7 +2910,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx) - ? get_user_commands(NULL, ea.useridx) + ? get_user_command_name(ea.useridx, ea.cmdidx) : cmdnames[ea.cmdidx].cmd_name); } @@ -5155,7 +5155,7 @@ static int check_more(int message, bool forceit) char_u *get_command_name(expand_T *xp, int idx) { if (idx >= CMD_SIZE) { - return get_user_command_name(idx); + return expand_user_command_name(idx); } return cmdnames[idx].cmd_name; } @@ -6270,7 +6270,7 @@ static void do_ucmd(exarg_T *eap) xfree(split_buf); } -static char_u *get_user_command_name(int idx) +static char_u *expand_user_command_name(int idx) { return get_user_commands(NULL, idx - CMD_SIZE); } @@ -6303,6 +6303,24 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) return NULL; } +// Get the name of user command "idx". "cmdidx" can be CMD_USER or +// CMD_USER_BUF. +// Returns NULL if the command is not found. +static char_u *get_user_command_name(int idx, int cmdidx) +{ + if (cmdidx == CMD_USER && idx < ucmds.ga_len) { + return USER_CMD(idx)->uc_name; + } + if (cmdidx == CMD_USER_BUF) { + // In cmdwin, the alternative buffer should be used. + buf_T *buf = (cmdwin_type != 0 && get_cmdline_type() == NUL) ? prevwin->w_buffer : curbuf; + if (idx < buf->b_ucmds.ga_len) { + return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; + } + } + return NULL; +} + /* * Function given to ExpandGeneric() to obtain the list of user command * attributes. diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index ef85c9e79a..ff4cbe544c 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -503,6 +503,13 @@ func Test_fullcommand() call assert_equal('', fullcommand(v:_null_string)) call assert_equal('syntax', 'syn'->fullcommand()) + + command -buffer BufferLocalCommand : + command GlobalCommand : + call assert_equal('GlobalCommand', fullcommand('GlobalCom')) + call assert_equal('BufferLocalCommand', fullcommand('BufferL')) + delcommand BufferLocalCommand + delcommand GlobalCommand endfunc func Test_shellcmd_completion() -- cgit From 30547c0d2b051dd4067f3197b968c28456fd23ff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 5 Jan 2022 22:11:28 +0800 Subject: vim-patch:8.2.3999: redundant check for NUL byte Problem: Redundant check for NUL byte. Solution: Remove the check for a NUL byte. (closes vim/vim#9471) https://github.com/vim/vim/commit/c024ed9233feac4c8da7394a62bb50474803514f --- src/nvim/ex_docmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 157e82f585..10ee0e10a8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2897,7 +2897,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - while (*name != NUL && *name == ':') { + while (*name == ':') { name++; } name = skip_range(name, NULL); -- cgit From aa5adef969f5f1ffcc3867200211fa236b569ae3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 6 Jan 2022 09:29:13 +0800 Subject: vim-patch:8.2.4007: session does not restore help buffer properly Problem: Session does not restore help buffer properly when "options' is missing from 'sessionoptions'. Solution: Use a ":help" command to create the help window. (closes vim/vim#9475, closes vim/vim#9458, closes vim/vim#9472) https://github.com/vim/vim/commit/8e7d9db32b53ca2b1cb7570d2042860bcd1e943f --- src/nvim/ex_session.c | 28 ++++++++++++++++++++-------- src/nvim/testdir/test_mksession.vim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index a37cad9f2d..a6d6bcbb8d 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -338,14 +338,26 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr // Edit the file. Skip this when ":next" already did it. if (add_edit && (!did_next || wp->w_arg_idx_invalid)) { - char *fname_esc = - ses_escape_fname(ses_get_fname(wp->w_buffer, flagp), flagp); - // - // Load the file. - // - if (wp->w_buffer->b_ffname != NULL - && (!bt_nofile(wp->w_buffer) - || wp->w_buffer->terminal)) { + char *fname_esc = ses_escape_fname(ses_get_fname(wp->w_buffer, flagp), flagp); + if (bt_help(wp->w_buffer)) { + char *curtag = ""; + + // A help buffer needs some options to be set. + // First, create a new empty buffer with "buftype=help". + // Then ":help" will re-use both the buffer and the window and set + // the options, even when "options" is not in 'sessionoptions'. + if (0 < wp->w_tagstackidx && wp->w_tagstackidx <= wp->w_tagstacklen) { + curtag = (char *)wp->w_tagstack[wp->w_tagstackidx - 1].tagname; + } + + if (put_line(fd, "enew | setl bt=help") == FAIL + || fprintf(fd, "help %s", curtag) < 0 || put_eol(fd) == FAIL) { + return FAIL; + } + } else if (wp->w_buffer->b_ffname != NULL + && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal)) { + // Load the file. + // Editing a file in this buffer: use ":edit file". // This may have side effects! (e.g., compressed or network file). // diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 057895047d..a87691abf9 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -696,6 +696,36 @@ func Test_mksession_foldopt() set sessionoptions& endfunc +" Test for mksession with "help" but not "options" in 'sessionoptions' +func Test_mksession_help_noopt() + set sessionoptions-=options + set sessionoptions+=help + help + let fname = expand('%') + mksession! Xtest_mks.out + bwipe + + source Xtest_mks.out + call assert_equal('help', &buftype) + call assert_equal('help', &filetype) + call assert_equal(fname, expand('%')) + call assert_false(&modifiable) + call assert_true(&readonly) + + helpclose + help index + let fname = expand('%') + mksession! Xtest_mks.out + bwipe + + source Xtest_mks.out + call assert_equal('help', &buftype) + call assert_equal(fname, expand('%')) + + call delete('Xtest_mks.out') + set sessionoptions& +endfunc + " Test for mksession with window position func Test_mksession_winpos() if !has('gui_running') -- cgit From 11142f6ffe46da1f20c570333a2c05b6e3015f56 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 8 Jan 2022 08:14:24 -0800 Subject: feat(extmarks): add strict option The strict option, when set to false, allows placing extmarks on invalid row and column values greater than the maximum buffer row or line column respectively. This allows for nvim_buf_set_extmark to be a drop-in replacement for nvim_buf_set_highlight. --- src/nvim/api/extmark.c | 13 ++++++++++--- src/nvim/api/keysets.lua | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 742b953c2a..04456eee01 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -404,6 +404,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// for left). Defaults to false. /// - priority: a priority value for the highlight group. For /// example treesitter highlighting uses a value of 100. +/// - strict: boolean that indicates extmark should be placed +/// even if the line or column value is past the end of the +/// buffer or end of the line respectively. Defaults to true. +/// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col, @@ -596,7 +600,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); - if (line < 0 || line > buf->b_ml.ml_line_count) { + bool strict = true; + OPTION_TO_BOOL(strict, strict, true); + + if (line < 0 || (line > buf->b_ml.ml_line_count && strict)) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; } else if (line < buf->b_ml.ml_line_count) { @@ -605,7 +612,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer if (col == -1) { col = (Integer)len; - } else if (col < -1 || col > (Integer)len) { + } else if (col < -1 || (col > (Integer)len && strict)) { api_set_error(err, kErrorTypeValidation, "col value outside range"); goto error; } @@ -620,7 +627,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // reuse len from before line2 = (int)line; } - if (col2 > (Integer)len) { + if (col2 > (Integer)len && strict) { api_set_error(err, kErrorTypeValidation, "end_col value outside range"); goto error; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 97ee885ff6..a385cfc64f 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -21,6 +21,7 @@ return { "virt_lines"; "virt_lines_above"; "virt_lines_leftcol"; + "strict"; }; keymap = { "noremap"; -- cgit From c7aa64631d721d140741206167d9a6ce766f1153 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 11 Jan 2022 10:45:46 +0800 Subject: feat(completion): support selecting item via API from Lua mapping --- src/nvim/edit.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index aa37d1b2dd..9efe5a27c4 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1082,6 +1082,8 @@ static int insert_handle_key(InsertState *s) map_execute_lua(); check_pum: + // nvim_select_popupmenu_item() can be called from the handling of + // K_EVENT, K_COMMAND, or K_LUA. // TODO(bfredl): Not entirely sure this indirection is necessary // but doing like this ensures using nvim_select_popupmenu_item is // equivalent to selecting the item with a typed key. @@ -4986,7 +4988,7 @@ void ins_compl_check_keys(int frequency, int in_compl_func) */ static int ins_compl_key2dir(int c) { - if (c == K_EVENT || c == K_COMMAND) { + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { return pum_want.item < pum_selected_item ? BACKWARD : FORWARD; } if (c == Ctrl_P || c == Ctrl_L @@ -5016,7 +5018,7 @@ static int ins_compl_key2count(int c) { int h; - if (c == K_EVENT || c == K_COMMAND) { + if (c == K_EVENT || c == K_COMMAND || c == K_LUA) { int offset = pum_want.item - pum_selected_item; return abs(offset); } @@ -5050,6 +5052,7 @@ static bool ins_compl_use_match(int c) return false; case K_EVENT: case K_COMMAND: + case K_LUA: return pum_want.active && pum_want.insert; } return true; -- cgit From d0d4fb792f2b3ac40a00b7929c8653968821e5ae Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Wed, 12 Jan 2022 12:01:29 -0800 Subject: Address 'review' --- src/nvim/api/extmark.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 04456eee01..ab97ff114c 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -404,8 +404,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// for left). Defaults to false. /// - priority: a priority value for the highlight group. For /// example treesitter highlighting uses a value of 100. -/// - strict: boolean that indicates extmark should be placed -/// even if the line or column value is past the end of the +/// - strict: boolean that indicates extmark should not be placed +/// if the line or column value is past the end of the /// buffer or end of the line respectively. Defaults to true. /// /// @param[out] err Error details, if any @@ -603,16 +603,31 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool strict = true; OPTION_TO_BOOL(strict, strict, true); - if (line < 0 || (line > buf->b_ml.ml_line_count && strict)) { + if (line < 0 ) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; + } + else if (line > buf->b_ml.ml_line_count) { + if (strict) { + api_set_error(err, kErrorTypeValidation, "line value outside range"); + goto error; + } else { + line = buf->b_ml.ml_line_count; + } } else if (line < buf->b_ml.ml_line_count) { len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line+1, false)); } if (col == -1) { col = (Integer)len; - } else if (col < -1 || (col > (Integer)len && strict)) { + } else if (col > (Integer)len) { + if (strict) { + api_set_error(err, kErrorTypeValidation, "col value outside range"); + goto error; + } else { + col = (Integer)len; + } + } else if (col < -1 ) { api_set_error(err, kErrorTypeValidation, "col value outside range"); goto error; } @@ -627,9 +642,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer // reuse len from before line2 = (int)line; } - if (col2 > (Integer)len && strict) { - api_set_error(err, kErrorTypeValidation, "end_col value outside range"); - goto error; + if (col2 > (Integer)len) { + if (strict) { + api_set_error(err, kErrorTypeValidation, "end_col value outside range"); + goto error; + } else { + col2 = (int)len; + } } } else if (line2 >= 0) { col2 = 0; -- cgit From 2fd8330628e007704c28043089a47ec75093bcb8 Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Wed, 12 Jan 2022 12:46:31 -0800 Subject: Address review r2 --- src/nvim/api/extmark.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index ab97ff114c..5624ae83dd 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -603,11 +603,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool strict = true; OPTION_TO_BOOL(strict, strict, true); - if (line < 0 ) { + if (line < 0) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; - } - else if (line > buf->b_ml.ml_line_count) { + } else if (line > buf->b_ml.ml_line_count) { if (strict) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; @@ -627,7 +626,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else { col = (Integer)len; } - } else if (col < -1 ) { + } else if (col < -1) { api_set_error(err, kErrorTypeValidation, "col value outside range"); goto error; } -- cgit From 3ee1ba35a79be452951ac35e4f45e779332657ed Mon Sep 17 00:00:00 2001 From: Daniel Steinberg Date: Thu, 13 Jan 2022 00:04:30 -0500 Subject: fix(keywordprg): retain terminal buffer after K (#17046) --- src/nvim/normal.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 60bf393085..2b5b47c0b3 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4437,11 +4437,7 @@ static void nv_ident(cmdarg_T *cap) // Start insert mode in terminal buffer restart_edit = 'i'; - add_map((char_u *)" call jobstop(&channel)", TERM_FOCUS, true); - do_cmdline_cmd("autocmd TermClose " - " if !v:event.status |" - " exec 'bdelete! ' .. expand('') |" - " endif"); + add_map((char_u *)" bdelete!", TERM_FOCUS, true); } } -- cgit From c8656e44d85502a1733df839b3cb3e8f239c5505 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 9 Jan 2022 09:02:02 +0800 Subject: feat(api, lua): more conversions between LuaRef and Vim Funcref --- src/nvim/api/private/converter.c | 20 ++++++++++++++++++++ src/nvim/lua/converter.c | 7 +++++++ 2 files changed, 27 insertions(+) (limited to 'src') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 36da6c13a9..e370c0d4d4 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -10,6 +10,9 @@ #include "nvim/api/private/helpers.h" #include "nvim/assert.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" +#include "nvim/lua/converter.h" +#include "nvim/lua/executor.h" /// Helper structure for vim_to_object typedef struct { @@ -228,6 +231,13 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { + if (obj->v_type == VAR_FUNC) { + ufunc_T *fp = find_func(obj->vval.v_string); + if (fp->uf_cb == nlua_CFunction_func_call) { + LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); + return LUAREF_OBJ(ref); + } + } EncodedData edata; kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, @@ -340,6 +350,16 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) tv->vval.v_dict = dict; break; } + + case kObjectTypeLuaRef: { + LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); + state->lua_callable.func_ref = api_new_luaref(obj.data.luaref); + char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state); + tv->v_type = VAR_FUNC; + tv->vval.v_string = vim_strsave(name); + break; + } + default: abort(); } diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 8a702ddd60..f9a2533d4e 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -617,6 +617,13 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; } + if (tv->v_type == VAR_FUNC) { + ufunc_T *fp = find_func(tv->vval.v_string); + if (fp->uf_cb == nlua_CFunction_func_call) { + nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); + return true; + } + } if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { return false; } -- cgit From 596c55756a6a25e8a6d587610292f3e2ca0940b7 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 13 Jan 2022 18:31:15 +0100 Subject: vim-patch:8.2.4077: not all Libsensors files are recognized (#17080) Problem: Not all Libsensors files are recognized. Solution: Add "sensors.d/*" pattern. (Doug Kearns) https://github.com/vim/vim/commit/8d9e470aa91a93da7d6bda62521aef69a79e956d --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 6e92baa939..f55b9cc1d2 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -446,7 +446,7 @@ let s:filename_checks = { \ 'sdc': ['file.sdc'], \ 'sdl': ['file.sdl', 'file.pr'], \ 'sed': ['file.sed'], - \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf', 'any/etc/sensors.conf', 'any/etc/sensors3.conf'], + \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf', '/etc/sensors.d/file', 'any/etc/sensors.conf', 'any/etc/sensors3.conf', 'any/etc/sensors.d/file'], \ 'services': ['/etc/services', 'any/etc/services'], \ 'setserial': ['/etc/serial.conf', 'any/etc/serial.conf'], \ 'sh': ['.bashrc', 'file.bash', '/usr/share/doc/bash-completion/filter.sh','/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'], -- cgit From e1b557d9130c94f72c2d836bf814a884c470809f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 13 Jan 2022 18:26:36 +0800 Subject: vim-patch:8.1.2375: no suffucient testing for registers Problem: No suffucient testing for registers. Solution: Add more test cases. (Yegappan Lakshmanan, closes vim/vim#5296) Fix that "p" on last virtual column of tab inserts spaces. https://github.com/vim/vim/commit/6f1f0ca3edf395102ff3109c998d81300c8be3c9 This patch doesn't actually change any behavior in Nvim, because Nvim always has vartabs feature. I modified a line in the test because of #6137. --- src/nvim/ops.c | 7 ++--- src/nvim/testdir/test_registers.vim | 51 +++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_virtualedit.vim | 42 +++++++++++++++++++++++++++++ src/nvim/testdir/test_visual.vim | 13 +++++++++ 4 files changed, 110 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 013a78bdac..1a12cb636a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3154,11 +3154,12 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) if (ve_flags == VE_ALL && y_type == kMTCharWise) { if (gchar_cursor() == TAB) { - /* Don't need to insert spaces when "p" on the last position of a - * tab or "P" on the first position. */ int viscol = getviscol(); + long ts = curbuf->b_p_ts; + // Don't need to insert spaces when "p" on the last position of a + // tab or "P" on the first position. if (dir == FORWARD - ? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_array) != 1 + ? tabstop_padding(viscol, ts, curbuf->b_p_vts_array) != 1 : curwin->w_cursor.coladd > 0) { coladvance_force(viscol); } else { diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 84a5aca3d5..4371828a72 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -13,6 +13,8 @@ func Test_aaa_empty_reg_test() call assert_fails('normal @!', 'E354:') call assert_fails('normal @:', 'E30:') call assert_fails('normal @.', 'E29:') + call assert_fails('put /', 'E35:') + call assert_fails('put .', 'E29:') endfunc func Test_yank_shows_register() @@ -141,6 +143,14 @@ func Test_last_used_exec_reg() normal @@ call assert_equal('EditEdit', a) + " Test for repeating the last command-line in visual mode + call append(0, 'register') + normal gg + let @r = '' + call feedkeys("v:yank R\", 'xt') + call feedkeys("v@:", 'xt') + call assert_equal("\nregister\nregister\n", @r) + enew! endfunc @@ -164,6 +174,28 @@ func Test_get_register() call assert_equal('', getregtype('!')) + " Test for clipboard registers (* and +) + if has("clipboard_working") + call append(0, "text for clipboard test") + normal gg"*yiw + call assert_equal('text', getreg('*')) + normal gg2w"+yiw + call assert_equal('clipboard', getreg('+')) + endif + + " Test for inserting an invalid register content + call assert_beeps('exe "normal i\!"') + + " Test for inserting a register with multiple lines + call deletebufline('', 1, '$') + call setreg('r', ['a', 'b']) + exe "normal i\r" + call assert_equal(['a', 'b', ''], getline(1, '$')) + + " Test for inserting a multi-line register in the command line + call feedkeys(":\r\", 'xt') + call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137 + enew! endfunc @@ -187,6 +219,25 @@ func Test_set_register() call setreg('=', 'b', 'a') call assert_equal('regwrite', getreg('=')) + " Test for settting a list of lines to special registers + call setreg('/', []) + call assert_equal('', @/) + call setreg('=', []) + call assert_equal('', @=) + call assert_fails("call setreg('/', ['a', 'b'])", 'E883:') + call assert_fails("call setreg('=', ['a', 'b'])", 'E883:') + call assert_equal(0, setreg('_', ['a', 'b'])) + + " Test for recording to a invalid register + call assert_beeps('normal q$') + + " Appending to a register when recording + call append(0, "text for clipboard test") + normal gg + call feedkeys('qrllq', 'xt') + call feedkeys('qRhhq', 'xt') + call assert_equal('llhh', getreg('r')) + enew! endfunc diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index 8f992f7501..0a218898ed 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -81,6 +81,48 @@ func Test_edit_change() normal Cx call assert_equal('x', getline(1)) bwipe! + set virtualedit= +endfunc + +" Test for pasting before and after a tab character +func Test_paste_in_tab() + new + let @" = 'xyz' + set virtualedit=all + call append(0, "a\tb") + call cursor(1, 2, 6) + normal p + call assert_equal("a\txyzb", getline(1)) + call setline(1, "a\tb") + call cursor(1, 2) + normal P + call assert_equal("axyz\tb", getline(1)) + + " Test for virtual block paste + call setreg('"', 'xyz', 'b') + call setline(1, "a\tb") + call cursor(1, 2, 6) + normal p + call assert_equal("a\txyzb", getline(1)) + call setline(1, "a\tb") + call cursor(1, 2, 6) + normal P + call assert_equal("a xyz b", getline(1)) + + " Test for virtual block paste with gp and gP + call setline(1, "a\tb") + call cursor(1, 2, 6) + normal gp + call assert_equal("a\txyzb", getline(1)) + call assert_equal([0, 1, 6, 0, 12], getcurpos()) + call setline(1, "a\tb") + call cursor(1, 2, 6) + normal gP + call assert_equal("a xyz b", getline(1)) + call assert_equal([0, 1, 12, 0 ,12], getcurpos()) + + bwipe! + set virtualedit= endfunc " Insert "keyword keyw", ESC, C CTRL-N, shows "keyword ykeyword". diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 8344598486..d58ca92a2f 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -433,6 +433,19 @@ func Test_Visual_Block() close! endfunc +" Test for 'p'ut in visual block mode +func Test_visual_block_put() + enew + + call append(0, ['One', 'Two', 'Three']) + normal gg + yank + call feedkeys("jl\ljp", 'xt') + call assert_equal(['One', 'T', 'Tee', 'One', ''], getline(1, '$')) + + enew! +endfunc + func Test_visual_put_in_block() new call setline(1, ['xxxx', 'y∞yy', 'zzzz']) -- cgit From 147f65373ecb5a5582d4cc0eb41ebc9a303181cc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jan 2022 07:47:13 +0800 Subject: vim-patch:8.2.4091: virtcol is recomputed for statusline unnecessarily Problem: Virtcol is recomputed for statusline unnecessarily. Solution: Just use "w_virtcol". (closes vim/vim#9523) https://github.com/vim/vim/commit/0f112052acaeffd75b7eb001eeb8a246ad12a276 --- src/nvim/buffer.c | 9 +-------- src/nvim/testdir/test_statusline.vim | 9 +++++++++ 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abd22fba26..4157651a7e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4001,14 +4001,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use case STL_VIRTCOL: case STL_VIRTCOL_ALT: { - // In list mode virtcol needs to be recomputed - colnr_T virtcol = wp->w_virtcol; - if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) { - wp->w_p_list = false; - getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); - wp->w_p_list = true; - } - virtcol++; + colnr_T virtcol = wp->w_virtcol + 1; // Don't display %V if it's the same as %c. if (opt == STL_VIRTCOL_ALT && (virtcol == (colnr_T)(!(State & INSERT) && empty_line diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index a3e4dcdd25..f40c9ae097 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -186,7 +186,16 @@ func Test_statusline() set virtualedit=all norm 10| call assert_match('^10,-10\s*$', s:get_statusline()) + set list + call assert_match('^10,-10\s*$', s:get_statusline()) set virtualedit& + exe "norm A\\a\" + " In list mode a is shown as "^I", which is 2-wide. + call assert_match('^9,-9\s*$', s:get_statusline()) + set list& + " Now the second ends at the 16th screen column. + call assert_match('^17,-17\s*$', s:get_statusline()) + undo " %w: Preview window flag, text is "[Preview]". " %W: Preview window flag, text is ",PRV". -- cgit From d391940b9a074bca7ee82460ccaaabf46b5f2ba9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jan 2022 19:21:17 +0800 Subject: vim-patch:8.2.3227: 'virtualedit' can only be set globally Problem: 'virtualedit' can only be set globally. Solution: Make 'virtualedit' global-local. (Gary Johnson, closes vim/vim#8638) https://github.com/vim/vim/commit/53ba05b09075f14227f9be831a22ed16f7cc26b2 I changed some macros to unsigned integer literals to avoid compiler warnings. --- src/nvim/buffer.c | 1 + src/nvim/buffer_defs.h | 2 + src/nvim/change.c | 2 +- src/nvim/cursor.c | 8 ++- src/nvim/edit.c | 7 +- src/nvim/normal.c | 6 +- src/nvim/ops.c | 27 ++++---- src/nvim/option.c | 46 ++++++++++--- src/nvim/option_defs.h | 13 ++-- src/nvim/options.lua | 2 +- src/nvim/screen.c | 8 +-- src/nvim/state.c | 9 ++- src/nvim/testdir/test_virtualedit.vim | 120 ++++++++++++++++++++++++++++++++++ 13 files changed, 208 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abd22fba26..3753d67d54 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1941,6 +1941,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_lw); clear_string_option(&buf->b_p_bkc); clear_string_option(&buf->b_p_menc); + clear_string_option(&buf->b_p_ve); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 63a550c017..a14dfff085 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -772,6 +772,8 @@ struct file_buffer { long b_p_ul; ///< 'undolevels' local value int b_p_udf; ///< 'undofile' char_u *b_p_lw; ///< 'lispwords' local value + char_u *b_p_ve; ///< 'virtualedit' local value + unsigned b_ve_flags; ///< flags for 'virtualedit' // end of buffer options diff --git a/src/nvim/change.c b/src/nvim/change.c index 1dbbfff024..0c16b204e3 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -789,7 +789,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) // fixpos is true, we don't want to end up positioned at the NUL, // unless "restart_edit" is set or 'virtualedit' contains "onemore". if (col > 0 && fixpos && restart_edit == 0 - && (ve_flags & VE_ONEMORE) == 0) { + && (get_ve_flags() & VE_ONEMORE) == 0) { curwin->w_cursor.col--; curwin->w_cursor.coladd = 0; curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col); diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 6e2c6232d7..55f55a46b2 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -15,6 +15,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/move.h" +#include "nvim/option.h" #include "nvim/plines.h" #include "nvim/screen.h" #include "nvim/state.h" @@ -110,7 +111,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a || (State & TERM_FOCUS) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') - || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); + || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL); line = ml_get_buf(curbuf, pos->lnum, false); if (wcol >= MAXCOL) { @@ -366,6 +367,7 @@ void check_cursor_col_win(win_T *win) colnr_T len; colnr_T oldcol = win->w_cursor.col; colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd; + unsigned int cur_ve_flags = get_ve_flags(); len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false)); if (len == 0) { @@ -377,7 +379,7 @@ void check_cursor_col_win(win_T *win) * - 'virtualedit' is set */ if ((State & INSERT) || restart_edit || (VIsual_active && *p_sel != 'o') - || (ve_flags & VE_ONEMORE) + || (cur_ve_flags & VE_ONEMORE) || virtual_active()) { win->w_cursor.col = len; } else { @@ -394,7 +396,7 @@ void check_cursor_col_win(win_T *win) // line. if (oldcol == MAXCOL) { win->w_cursor.coladd = 0; - } else if (ve_flags == VE_ALL) { + } else if (cur_ve_flags == VE_ALL) { if (oldcoladd > win->w_cursor.col) { win->w_cursor.coladd = oldcoladd - win->w_cursor.col; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 9efe5a27c4..abf704d554 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -909,7 +909,7 @@ static int insert_handle_key(InsertState *s) ins_ctrl_o(); // don't move the cursor left when 'virtualedit' has "onemore". - if (ve_flags & VE_ONEMORE) { + if (get_ve_flags() & VE_ONEMORE) { ins_at_eol = false; s->nomove = true; } @@ -6905,8 +6905,7 @@ int oneright(void) // move "l" bytes right, but don't end up on the NUL, unless 'virtualedit' // contains "onemore". - if (ptr[l] == NUL - && (ve_flags & VE_ONEMORE) == 0) { + if (ptr[l] == NUL && (get_ve_flags() & VE_ONEMORE) == 0) { return FAIL; } curwin->w_cursor.col += l; @@ -8028,7 +8027,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) && !VIsual_active )) && !revins_on) { - if (curwin->w_cursor.coladd > 0 || ve_flags == VE_ALL) { + if (curwin->w_cursor.coladd > 0 || get_ve_flags() == VE_ALL) { oneleft(); if (restart_edit != NUL) { curwin->w_cursor.coladd++; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2b5b47c0b3..98ed351bf1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6046,7 +6046,7 @@ static void n_start_visual_mode(int c) // Corner case: the 0 position in a tab may change when going into // virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting. // - if (c == Ctrl_V && (ve_flags & VE_BLOCK) && gchar_cursor() == TAB) { + if (c == Ctrl_V && (get_ve_flags() & VE_BLOCK) && gchar_cursor() == TAB) { validate_virtcol(); coladvance(curwin->w_virtcol); } @@ -6951,7 +6951,7 @@ static void adjust_cursor(oparg_T *oap) if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL && (!VIsual_active || *p_sel == 'o') && !virtual_active() - && (ve_flags & VE_ONEMORE) == 0) { + && (get_ve_flags() & VE_ONEMORE) == 0) { curwin->w_cursor.col--; // prevent cursor from moving on the trail byte mb_adjust_cursor(); @@ -7157,7 +7157,7 @@ static void nv_esc(cmdarg_T *cap) void set_cursor_for_append_to_line(void) { curwin->w_set_curswant = true; - if (ve_flags == VE_ALL) { + if (get_ve_flags() == VE_ALL) { const int save_State = State; // Pretend Insert mode here to allow the cursor on the diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1a12cb636a..74033df313 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2196,19 +2196,22 @@ void op_insert(oparg_T *oap, long count1) // doing block_prep(). When only "block" is used, virtual edit is // already disabled, but still need it when calling // coladvance_force(). + // coladvance_force() uses get_ve_flags() to get the 'virtualedit' + // state for the current buffer. To override that state, we need to + // set the buffer-local value of ve_flags rather than the global value. if (curwin->w_cursor.coladd > 0) { - unsigned old_ve_flags = ve_flags; + unsigned old_ve_flags = curbuf->b_ve_flags; - ve_flags = VE_ALL; if (u_save_cursor() == FAIL) { return; } + curbuf->b_ve_flags = VE_ALL; coladvance_force(oap->op_type == OP_APPEND ? oap->end_vcol + 1 : getviscol()); if (oap->op_type == OP_APPEND) { --curwin->w_cursor.col; } - ve_flags = old_ve_flags; + curbuf->b_ve_flags = old_ve_flags; } // Get the info about the block before entering the text block_prep(oap, &bd, oap->start.lnum, true); @@ -2906,6 +2909,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) char_u *insert_string = NULL; bool allocated = false; long cnt; + unsigned int cur_ve_flags = get_ve_flags(); if (flags & PUT_FIXINDENT) { orig_indent = get_indent(); @@ -2976,7 +2980,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL); } - bool ve_allows = (ve_flags == VE_ALL || ve_flags == VE_ONEMORE); + bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE); bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum && one_past_line; if (ve_allows || !(eol || eof)) { @@ -3152,7 +3156,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) yanklen = (int)STRLEN(y_array[0]); - if (ve_flags == VE_ALL && y_type == kMTCharWise) { + if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) { if (gchar_cursor() == TAB) { int viscol = getviscol(); long ts = curbuf->b_p_ts; @@ -3181,7 +3185,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) colnr_T endcol2 = 0; if (dir == FORWARD && c != NUL) { - if (ve_flags == VE_ALL) { + if (cur_ve_flags == VE_ALL) { getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2); } else { getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col); @@ -3195,9 +3199,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } col += curwin->w_cursor.coladd; - if (ve_flags == VE_ALL - && (curwin->w_cursor.coladd > 0 - || endcol2 == curwin->w_cursor.col)) { + if (cur_ve_flags == VE_ALL + && (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) { if (dir == FORWARD && c == NUL) { col++; } @@ -3629,14 +3632,16 @@ end: */ void adjust_cursor_eol(void) { + unsigned int cur_ve_flags = get_ve_flags(); + if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL - && (ve_flags & VE_ONEMORE) == 0 + && (cur_ve_flags & VE_ONEMORE) == 0 && !(restart_edit || (State & INSERT))) { // Put the cursor on the last character in the line. dec_cursor(); - if (ve_flags == VE_ALL) { + if (cur_ve_flags == VE_ALL) { colnr_T scol, ecol; // Coladd is set to the width of the last character. diff --git a/src/nvim/option.c b/src/nvim/option.c index 659965b64c..fd4cc9ebb2 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2065,6 +2065,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_menc); check_string_option(&buf->b_p_vsts); check_string_option(&buf->b_p_vts); + check_string_option(&buf->b_p_ve); } /// Free the string allocated for an option. @@ -3084,14 +3085,27 @@ ambw_end: if (foldmethodIsIndent(curwin)) { foldUpdateAll(curwin); } - } else if (varp == &p_ve) { // 'virtualedit' - if (opt_strings_flags(p_ve, p_ve_values, &ve_flags, true) != OK) { - errmsg = e_invarg; - } else if (STRCMP(p_ve, oldval) != 0) { - // Recompute cursor position in case the new 've' setting - // changes something. - validate_virtcol(); - coladvance(curwin->w_virtcol); + } else if (gvarp == &p_ve) { // 'virtualedit' + char_u *ve = p_ve; + unsigned int *flags = &ve_flags; + + if (opt_flags & OPT_LOCAL) { + ve = curbuf->b_p_ve; + flags = &curbuf->b_ve_flags; + } + + if ((opt_flags & OPT_LOCAL) && *ve == NUL) { + // make the local value empty: use the global value + *flags = 0; + } else { + if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) { + errmsg = e_invarg; + } else if (STRCMP(p_ve, oldval) != 0) { + // Recompute cursor position in case the new 've' setting + // changes something. + validate_virtcol(); + coladvance(curwin->w_virtcol); + } } } else if (varp == &p_csqf) { if (p_csqf != NULL) { @@ -5748,6 +5762,10 @@ void unset_global_local_option(char *name, void *from) set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true); redraw_later((win_T *)from, NOT_VALID); break; + case PV_VE: + clear_string_option(&buf->b_p_ve); + buf->b_ve_flags = 0; + break; } } @@ -5814,6 +5832,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) return (char_u *)&(curwin->w_p_fcs); case PV_LCS: return (char_u *)&(curwin->w_p_lcs); + case PV_VE: + return (char_u *)&(curbuf->b_p_ve); } return NULL; // "cannot happen" } @@ -6106,6 +6126,8 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curbuf->b_p_vsts); case PV_VTS: return (char_u *)&(curbuf->b_p_vts); + case PV_VE: + return *curbuf->b_p_ve != NUL ? (char_u *)&curbuf->b_p_ve : p->var; case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); case PV_SCL: @@ -6438,6 +6460,8 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_udf = p_udf; buf->b_p_lw = empty_option; buf->b_p_menc = empty_option; + buf->b_p_ve = empty_option; + buf->b_ve_flags = 0; /* * Don't copy the options set by ex_help(), use the saved values, @@ -7815,6 +7839,12 @@ unsigned int get_bkc_value(buf_T *buf) return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags; } +/// Get the local or global value of the 'virtualedit' flags. +unsigned int get_ve_flags(void) +{ + return (curbuf->b_ve_flags ? curbuf->b_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); +} + /// Get the local or global value of 'showbreak'. /// /// @param win If not NULL, the window to get the local option from; global diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 09c3bf3800..aed2868a09 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -705,12 +705,14 @@ EXTERN int p_vb; ///< 'visualbell' EXTERN char_u *p_ve; ///< 'virtualedit' EXTERN unsigned ve_flags; #ifdef IN_OPTION_C -static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", NULL }; +static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL }; #endif -#define VE_BLOCK 5 // includes "all" -#define VE_INSERT 6 // includes "all" -#define VE_ALL 4 -#define VE_ONEMORE 8 +#define VE_BLOCK 5U // includes "all" +#define VE_INSERT 6U // includes "all" +#define VE_ALL 4U +#define VE_ONEMORE 8U +#define VE_NONE 16U +#define VE_NONEU 32U // Upper-case NONE EXTERN long p_verbose; // 'verbose' #ifdef IN_OPTION_C char_u *p_vfile = (char_u *)""; // used before options are initialized @@ -839,6 +841,7 @@ enum { BV_WM, BV_VSTS, BV_VTS, + BV_VE, BV_COUNT, // must be the last one }; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 5133fe7ac8..0f46d2de21 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2736,7 +2736,7 @@ return { { full_name='virtualedit', abbreviation='ve', short_desc=N_("when to use virtual editing"), - type='string', list='onecomma', scope={'global'}, + type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, redraw={'curswant'}, varname='p_ve', diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 538604cf79..9aea42cda3 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1215,18 +1215,18 @@ static void win_update(win_T *wp, Providers *providers) */ if (VIsual_mode == Ctrl_V) { colnr_T fromc, toc; - int save_ve_flags = ve_flags; + unsigned int save_ve_flags = curbuf->b_ve_flags; if (curwin->w_p_lbr) { - ve_flags = VE_ALL; + curbuf->b_ve_flags = VE_ALL; } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); - ve_flags = save_ve_flags; + curbuf->b_ve_flags = save_ve_flags; toc++; // Highlight to the end of the line, unless 'virtualedit' has // "block". - if (curwin->w_curswant == MAXCOL && !(ve_flags & VE_BLOCK)) { + if (curwin->w_curswant == MAXCOL && !(get_ve_flags() & VE_BLOCK)) { toc = MAXCOL; } diff --git a/src/nvim/state.c b/src/nvim/state.c index 1fe8bb671d..9e4c9b2bad 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -12,6 +12,7 @@ #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/main.h" +#include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/os/input.h" #include "nvim/state.h" @@ -107,15 +108,17 @@ void state_handle_k_event(void) /// Return true if in the current mode we need to use virtual. bool virtual_active(void) { + unsigned int cur_ve_flags = get_ve_flags(); + // While an operator is being executed we return "virtual_op", because // VIsual_active has already been reset, thus we can't check for "block" // being used. if (virtual_op != kNone) { return virtual_op; } - return ve_flags == VE_ALL - || ((ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V) - || ((ve_flags & VE_INSERT) && (State & INSERT)); + return cur_ve_flags == VE_ALL + || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V) + || ((cur_ve_flags & VE_INSERT) && (State & INSERT)); } /// VISUAL, SELECTMODE and OP_PENDING State are never set, they are equal to diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index 0a218898ed..c19a9500bd 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -258,4 +258,124 @@ func Test_yank_paste_small_del_reg() set virtualedit= endfunc +" After calling s:TryVirtualeditReplace(), line 1 will contain one of these +" two strings, depending on whether virtual editing is on or off. +let s:result_ve_on = 'a x' +let s:result_ve_off = 'x' + +" Utility function for Test_global_local() +func s:TryVirtualeditReplace() + call setline(1, 'a') + normal gg7l + normal rx +endfunc + +" Test for :set and :setlocal +func Test_global_local() + new + + " Verify that 'virtualedit' is initialized to empty, can be set globally to + " all and to empty, and can be set locally to all and to empty. + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + set ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + set ve= + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + setlocal ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve= + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + + " Verify that :set affects multiple buffers + new + set ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + wincmd p + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + set ve= + wincmd p + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + bwipe! + + " Verify that :setlocal affects only the current buffer + setlocal ve=all + new + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + setlocal ve=all + wincmd p + setlocal ve= + wincmd p + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + bwipe! + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + + " Verify that the buffer 'virtualedit' state follows the global value only + " when empty and that "none" works as expected. + " + " 'virtualedit' State + " +--------+--------------------------+ + " | Local | Global | + " | | | + " +--------+--------+--------+--------+ + " | | "" | "all" | "none" | + " +--------+--------+--------+--------+ + " | "" | off | on | off | + " | "all" | on | on | on | + " | "none" | off | off | off | + " +--------+--------+--------+--------+ + new + + setglobal ve= + setlocal ve= + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + setlocal ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve=none + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + + setglobal ve=all + setlocal ve= + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve=none + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + setlocal ve=NONE + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + + setglobal ve=none + setlocal ve= + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + setlocal ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve=none + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + + bwipe! + + setlocal virtualedit& + set virtualedit& +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 87e54f123aa1c9c769d3ff35bdd1b5a980ba701c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jan 2022 19:21:17 +0800 Subject: vim-patch:8.2.3280: 'virtualedit' local to buffer is not the best solution Problem: 'virtualedit' local to buffer is not the best solution. Solution: Make it window-local. (Gary Johnson, closes vim/vim#8685) https://github.com/vim/vim/commit/51ad850f5fbafa7aa3f60affa74ec9c9f992c6cc --- src/nvim/buffer.c | 1 - src/nvim/buffer_defs.h | 6 ++++-- src/nvim/ops.c | 10 +++++----- src/nvim/option.c | 24 ++++++++++++----------- src/nvim/option_defs.h | 4 ++-- src/nvim/screen.c | 6 +++--- src/nvim/testdir/test_virtualedit.vim | 37 ++++++++++++++++++++++++----------- 7 files changed, 53 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3753d67d54..abd22fba26 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1941,7 +1941,6 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_lw); clear_string_option(&buf->b_p_bkc); clear_string_option(&buf->b_p_menc); - clear_string_option(&buf->b_p_ve); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index a14dfff085..2856a7390d 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -204,6 +204,10 @@ typedef struct { #define w_p_nu w_onebuf_opt.wo_nu // 'number' int wo_rnu; #define w_p_rnu w_onebuf_opt.wo_rnu // 'relativenumber' + char_u *wo_ve; +#define w_p_ve w_onebuf_opt.wo_ve // 'virtualedit' + unsigned wo_ve_flags; +#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit' long wo_nuw; #define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth' int wo_wfh; @@ -772,8 +776,6 @@ struct file_buffer { long b_p_ul; ///< 'undolevels' local value int b_p_udf; ///< 'undofile' char_u *b_p_lw; ///< 'lispwords' local value - char_u *b_p_ve; ///< 'virtualedit' local value - unsigned b_ve_flags; ///< flags for 'virtualedit' // end of buffer options diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 74033df313..c845bd3717 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2197,21 +2197,21 @@ void op_insert(oparg_T *oap, long count1) // already disabled, but still need it when calling // coladvance_force(). // coladvance_force() uses get_ve_flags() to get the 'virtualedit' - // state for the current buffer. To override that state, we need to - // set the buffer-local value of ve_flags rather than the global value. + // state for the current window. To override that state, we need to + // set the window-local value of ve_flags rather than the global value. if (curwin->w_cursor.coladd > 0) { - unsigned old_ve_flags = curbuf->b_ve_flags; + unsigned old_ve_flags = curwin->w_ve_flags; if (u_save_cursor() == FAIL) { return; } - curbuf->b_ve_flags = VE_ALL; + curwin->w_ve_flags = VE_ALL; coladvance_force(oap->op_type == OP_APPEND ? oap->end_vcol + 1 : getviscol()); if (oap->op_type == OP_APPEND) { --curwin->w_cursor.col; } - curbuf->b_ve_flags = old_ve_flags; + curwin->w_ve_flags = old_ve_flags; } // Get the info about the block before entering the text block_prep(oap, &bd, oap->start.lnum, true); diff --git a/src/nvim/option.c b/src/nvim/option.c index fd4cc9ebb2..a79523f800 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2065,7 +2065,6 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_menc); check_string_option(&buf->b_p_vsts); check_string_option(&buf->b_p_vts); - check_string_option(&buf->b_p_ve); } /// Free the string allocated for an option. @@ -3090,8 +3089,8 @@ ambw_end: unsigned int *flags = &ve_flags; if (opt_flags & OPT_LOCAL) { - ve = curbuf->b_p_ve; - flags = &curbuf->b_ve_flags; + ve = curwin->w_p_ve; + flags = &curwin->w_ve_flags; } if ((opt_flags & OPT_LOCAL) && *ve == NUL) { @@ -5763,8 +5762,8 @@ void unset_global_local_option(char *name, void *from) redraw_later((win_T *)from, NOT_VALID); break; case PV_VE: - clear_string_option(&buf->b_p_ve); - buf->b_ve_flags = 0; + clear_string_option(&((win_T *)from)->w_p_ve); + ((win_T *)from)->w_ve_flags = 0; break; } } @@ -5833,7 +5832,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_LCS: return (char_u *)&(curwin->w_p_lcs); case PV_VE: - return (char_u *)&(curbuf->b_p_ve); + return (char_u *)&(curwin->w_p_ve); } return NULL; // "cannot happen" } @@ -5928,6 +5927,9 @@ static char_u *get_varp(vimoption_T *p) case PV_LCS: return *curwin->w_p_lcs != NUL ? (char_u *)&(curwin->w_p_lcs) : p->var; + case PV_VE: + return *curwin->w_p_ve != NUL + ? (char_u *)&curwin->w_p_ve : p->var; case PV_ARAB: return (char_u *)&(curwin->w_p_arab); @@ -6126,8 +6128,6 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curbuf->b_p_vsts); case PV_VTS: return (char_u *)&(curbuf->b_p_vts); - case PV_VE: - return *curbuf->b_p_ve != NUL ? (char_u *)&curbuf->b_p_ve : p->var; case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); case PV_SCL: @@ -6170,6 +6170,8 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_list = from->wo_list; to->wo_nu = from->wo_nu; to->wo_rnu = from->wo_rnu; + to->wo_ve = vim_strsave(from->wo_ve); + to->wo_ve_flags = from->wo_ve_flags; to->wo_nuw = from->wo_nuw; to->wo_rl = from->wo_rl; to->wo_rlc = vim_strsave(from->wo_rlc); @@ -6246,6 +6248,7 @@ static void check_winopt(winopt_T *wop) check_string_option(&wop->wo_winhl); check_string_option(&wop->wo_fcs); check_string_option(&wop->wo_lcs); + check_string_option(&wop->wo_ve); } /// Free the allocated memory inside a winopt_T. @@ -6270,6 +6273,7 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_winhl); clear_string_option(&wop->wo_fcs); clear_string_option(&wop->wo_lcs); + clear_string_option(&wop->wo_ve); } void didset_window_options(win_T *wp) @@ -6460,8 +6464,6 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_udf = p_udf; buf->b_p_lw = empty_option; buf->b_p_menc = empty_option; - buf->b_p_ve = empty_option; - buf->b_ve_flags = 0; /* * Don't copy the options set by ex_help(), use the saved values, @@ -7842,7 +7844,7 @@ unsigned int get_bkc_value(buf_T *buf) /// Get the local or global value of the 'virtualedit' flags. unsigned int get_ve_flags(void) { - return (curbuf->b_ve_flags ? curbuf->b_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); + return (curwin->w_ve_flags ? curwin->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU); } /// Get the local or global value of 'showbreak'. diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index aed2868a09..db8bc83395 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -711,8 +711,8 @@ static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "N #define VE_INSERT 6U // includes "all" #define VE_ALL 4U #define VE_ONEMORE 8U -#define VE_NONE 16U -#define VE_NONEU 32U // Upper-case NONE +#define VE_NONE 16U // "none" +#define VE_NONEU 32U // "NONE" EXTERN long p_verbose; // 'verbose' #ifdef IN_OPTION_C char_u *p_vfile = (char_u *)""; // used before options are initialized diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 9aea42cda3..9b253fad01 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1215,14 +1215,14 @@ static void win_update(win_T *wp, Providers *providers) */ if (VIsual_mode == Ctrl_V) { colnr_T fromc, toc; - unsigned int save_ve_flags = curbuf->b_ve_flags; + unsigned int save_ve_flags = curwin->w_ve_flags; if (curwin->w_p_lbr) { - curbuf->b_ve_flags = VE_ALL; + curwin->w_ve_flags = VE_ALL; } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); - curbuf->b_ve_flags = save_ve_flags; + curwin->w_ve_flags = save_ve_flags; toc++; // Highlight to the end of the line, unless 'virtualedit' has // "block". diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index c19a9500bd..d2a5258bd3 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -263,7 +263,7 @@ endfunc let s:result_ve_on = 'a x' let s:result_ve_off = 'x' -" Utility function for Test_global_local() +" Utility function for Test_global_local_virtualedit() func s:TryVirtualeditReplace() call setline(1, 'a') normal gg7l @@ -271,7 +271,7 @@ func s:TryVirtualeditReplace() endfunc " Test for :set and :setlocal -func Test_global_local() +func Test_global_local_virtualedit() new " Verify that 'virtualedit' is initialized to empty, can be set globally to @@ -291,8 +291,8 @@ func Test_global_local() call s:TryVirtualeditReplace() call assert_equal(s:result_ve_off, getline(1)) - " Verify that :set affects multiple buffers - new + " Verify that :set affects multiple windows. + split set ve=all call s:TryVirtualeditReplace() call assert_equal(s:result_ve_on, getline(1)) @@ -305,17 +305,15 @@ func Test_global_local() call assert_equal(s:result_ve_off, getline(1)) bwipe! - " Verify that :setlocal affects only the current buffer - setlocal ve=all + " Verify that :setlocal affects only the current window. new - call s:TryVirtualeditReplace() - call assert_equal(s:result_ve_off, getline(1)) + split setlocal ve=all - wincmd p - setlocal ve= - wincmd p call s:TryVirtualeditReplace() call assert_equal(s:result_ve_on, getline(1)) + wincmd p + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) bwipe! call s:TryVirtualeditReplace() call assert_equal(s:result_ve_off, getline(1)) @@ -374,6 +372,23 @@ func Test_global_local() bwipe! + " Verify that the 'virtualedit' state is copied to new windows. + new + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + split + setlocal ve=all + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + split + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_on, getline(1)) + setlocal ve= + split + call s:TryVirtualeditReplace() + call assert_equal(s:result_ve_off, getline(1)) + bwipe! + setlocal virtualedit& set virtualedit& endfunc -- cgit From 6c22e5fd1a4e6d945bb76a4a8a558613f99ec793 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jan 2022 19:21:17 +0800 Subject: vim-patch:8.2.4094: 'virtualedit' is window-local but using buffer-local enum Problem: 'virtualedit' is window-local but using buffer-local enum. Solution: Use window-local enum. (closes vim/vim#9529) https://github.com/vim/vim/commit/e1833bfd01c100896d2a01f281762c285192d84b --- src/nvim/option_defs.h | 2 +- src/nvim/options.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index db8bc83395..5d6aca9574 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -841,7 +841,6 @@ enum { BV_WM, BV_VSTS, BV_VTS, - BV_VE, BV_COUNT, // must be the last one }; @@ -872,6 +871,7 @@ enum { WV_LBR, WV_NU, WV_RNU, + WV_VE, WV_NUW, WV_PVW, WV_RL, diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 0f46d2de21..aea2179a61 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2736,7 +2736,7 @@ return { { full_name='virtualedit', abbreviation='ve', short_desc=N_("when to use virtual editing"), - type='string', list='onecomma', scope={'global', 'buffer'}, + type='string', list='onecomma', scope={'global', 'window'}, deny_duplicates=true, redraw={'curswant'}, varname='p_ve', -- cgit From c09147aad99a88dc39c47c276b431ade4c83ac9d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 15 Jan 2022 14:47:25 +0100 Subject: vim-patch:8.2.4095: sed script not recognized by the first line (#17101) Problem: Sed script not recognized by the first line. Solution: Recognize a sed script starting with "#n". (Doug Kearns) https://github.com/vim/vim/commit/e3ce17a3ca838954728df21ccb6c2a724490203d --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index f55b9cc1d2..7db05e34d5 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -653,7 +653,7 @@ let s:script_checks = { \ ['#!/path/nodejs'], \ ['#!/path/rhino']], \ 'bc': [['#!/path/bc']], - \ 'sed': [['#!/path/sed']], + \ 'sed': [['#!/path/sed'], ['#n'], ['#n comment']], \ 'ocaml': [['#!/path/ocaml']], \ 'awk': [['#!/path/awk'], \ ['#!/path/gawk']], -- cgit From 8e945c2524f26e019959e82072e783cdaa694818 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 15 Jan 2022 22:33:16 +0800 Subject: vim-patch:8.2.1762: when a timer uses :stopinsert completion isn't stopped Problem: When a timer uses :stopinsert Insert mode completion isn't stopped. (Stanley Chan) Solution: Call ins_compl_prep(ESC). https://github.com/vim/vim/commit/d0e1b7103c14eb0d175c6b245b4b6ed93a204da9 --- src/nvim/edit.c | 4 ++++ src/nvim/testdir/test_ins_complete.vim | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 9efe5a27c4..23e555c6de 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -663,8 +663,12 @@ static int insert_execute(VimState *state, int key) InsertState *const s = (InsertState *)state; if (stop_insert_mode) { // Insert mode ended, possibly from a callback. + if (key != K_IGNORE && key != K_NOP) { + vungetc(key); + } s->count = 0; s->nomove = true; + ins_compl_prep(ESC); return 0; } diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index ce75799551..6803271c03 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -445,6 +445,28 @@ func Test_issue_7021() set completeslash= endfunc +func Test_pum_stopped_by_timer() + CheckScreendump + + let lines =<< trim END + call setline(1, ['hello', 'hullo', 'heeee', '']) + func StartCompl() + call timer_start(100, { -> execute('stopinsert') }) + call feedkeys("Gah\") + endfunc + END + + call writefile(lines, 'Xpumscript') + let buf = RunVimInTerminal('-S Xpumscript', #{rows: 12}) + call term_sendkeys(buf, ":call StartCompl()\") + call TermWait(buf, 200) + call term_sendkeys(buf, "k") + call VerifyScreenDump(buf, 'Test_pum_stopped_by_timer', {}) + + call StopVimInTerminal(buf) + call delete('Xpumscript') +endfunc + func Test_pum_with_folds_two_tabs() CheckScreendump -- cgit From facd07bcf76b3c0b41eb046fce6b9d5bad62b89e Mon Sep 17 00:00:00 2001 From: Michael Lingelbach Date: Sat, 15 Jan 2022 08:37:44 -0800 Subject: Address review r3 --- src/nvim/api/extmark.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 5624ae83dd..58be8f2807 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -445,9 +445,18 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer opts->end_row = opts->end_line; } +#define OPTION_TO_BOOL(target, name, val) \ + target = api_object_to_bool(opts->name, #name, val, err); \ + if (ERROR_SET(err)) { \ + goto error; \ + } + + bool strict = true; + OPTION_TO_BOOL(strict, strict, true); + if (opts->end_row.type == kObjectTypeInteger) { Integer val = opts->end_row.data.integer; - if (val < 0 || val > buf->b_ml.ml_line_count) { + if (val < 0 || (val > buf->b_ml.ml_line_count && strict)) { api_set_error(err, kErrorTypeValidation, "end_row value outside range"); goto error; } else { @@ -516,12 +525,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } -#define OPTION_TO_BOOL(target, name, val) \ - target = api_object_to_bool(opts->name, #name, val, err); \ - if (ERROR_SET(err)) { \ - goto error; \ - } - OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); OPTION_TO_BOOL(decor.hl_eol, hl_eol, false); @@ -600,9 +603,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer bool ephemeral = false; OPTION_TO_BOOL(ephemeral, ephemeral, false); - bool strict = true; - OPTION_TO_BOOL(strict, strict, true); - if (line < 0) { api_set_error(err, kErrorTypeValidation, "line value outside range"); goto error; -- cgit From 95ab979fde66d8f9f97fceb943bfe9422739a0f8 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 25 Oct 2021 21:51:29 +0200 Subject: refactor(extmarks): use a more efficient representation marktree.c was originally constructed as a "generic" datatype, to make the prototyping of its internal logic as simple as possible and also as the usecases for various kinds of extmarks/decorations was not yet decided. As a consequence of this, various extra indirections and allocations was needed to use marktree to implement extmarks (ns/id pairs) and decorations of different kinds (some which is just a single highlight id, other an allocated list of virtual text/lines) This change removes a lot of indirection, by making Marktree specialized for the usecase. In particular, the namespace id and mark id is stored directly, instead of the 64-bit global id particular to the Marktree struct. This removes the two maps needed to convert between global and per-ns ids. Also, "small" decorations are stored inline, i.e. those who doesn't refer to external heap memory anyway. That is highlights (with priority+flags) are stored inline, while virtual text, which anyway occurs a lot of heap allocations, do not. (previously a hack was used to elide heap allocations for highlights with standard prio+flags) TODO(bfredl): the functionaltest-lua CI version of gcc is having severe issues with uint16_t bitfields, so splitting up compound assignments and redundant casts are needed. Clean this up once we switch to a working compiler version. --- src/nvim/api/deprecated.c | 11 +- src/nvim/api/extmark.c | 95 ++++++-------- src/nvim/api/private/helpers.c | 2 +- src/nvim/buffer_defs.h | 3 +- src/nvim/decoration.c | 161 ++++++++++------------- src/nvim/decoration.h | 3 +- src/nvim/extmark.c | 286 +++++++++++++++++++---------------------- src/nvim/extmark.h | 3 +- src/nvim/extmark_defs.h | 12 -- src/nvim/map.c | 5 +- src/nvim/map.h | 10 +- src/nvim/marktree.c | 196 +++++++++++++++------------- src/nvim/marktree.h | 89 +++++++++---- src/nvim/types.h | 2 + 14 files changed, 433 insertions(+), 445 deletions(-) (limited to 'src') diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 76b699800e..18243fec2b 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -130,7 +130,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A return 0; } - uint64_t ns_id = src2ns(&src_id); + uint32_t ns_id = src2ns(&src_id); int width; VirtText virt_text = parse_virt_text(chunks, err, &width); @@ -148,11 +148,12 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A return src_id; } - Decoration *decor = xcalloc(1, sizeof(*decor)); - decor->virt_text = virt_text; - decor->virt_text_width = width; + Decoration decor = DECORATION_INIT; + decor.virt_text = virt_text; + decor.virt_text_width = width; + decor.priority = 0; - extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, true, + extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, &decor, true, false, kExtmarkNoUndo); return src_id; } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 742b953c2a..a37bbae668 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -85,12 +85,12 @@ const char *describe_ns(NS ns_id) } // Is the Namespace in use? -static bool ns_initialized(uint64_t ns) +static bool ns_initialized(uint32_t ns) { if (ns < 1) { return false; } - return ns < (uint64_t)next_namespace_id; + return ns < (uint32_t)next_namespace_id; } @@ -111,27 +111,27 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) PUT(dict, "end_col", INTEGER_OBJ(extmark.end_col)); } - if (extmark.decor) { - Decoration *decor = extmark.decor; - if (decor->hl_id) { - String name = cstr_to_string((const char *)syn_id2name(decor->hl_id)); - PUT(dict, "hl_group", STRING_OBJ(name)); - } - if (kv_size(decor->virt_text)) { - Array chunks = ARRAY_DICT_INIT; - for (size_t i = 0; i < decor->virt_text.size; i++) { - Array chunk = ARRAY_DICT_INIT; - VirtTextChunk *vtc = &decor->virt_text.items[i]; - ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); - if (vtc->hl_id > 0) { - ADD(chunk, - STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id)))); - } - ADD(chunks, ARRAY_OBJ(chunk)); + Decoration *decor = &extmark.decor; + if (decor->hl_id) { + String name = cstr_to_string((const char *)syn_id2name(decor->hl_id)); + PUT(dict, "hl_group", STRING_OBJ(name)); + } + if (kv_size(decor->virt_text)) { + Array chunks = ARRAY_DICT_INIT; + for (size_t i = 0; i < decor->virt_text.size; i++) { + Array chunk = ARRAY_DICT_INIT; + VirtTextChunk *vtc = &decor->virt_text.items[i]; + ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); + if (vtc->hl_id > 0) { + ADD(chunk, + STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id)))); } - PUT(dict, "virt_text", ARRAY_OBJ(chunks)); + ADD(chunks, ARRAY_OBJ(chunk)); } + PUT(dict, "virt_text", ARRAY_OBJ(chunks)); + } + if (decor->hl_id || kv_size(decor->virt_text)) { PUT(dict, "priority", INTEGER_OBJ(decor->priority)); } @@ -166,7 +166,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, return rv; } - if (!ns_initialized((uint64_t)ns_id)) { + if (!ns_initialized((uint32_t)ns_id)) { api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); return rv; } @@ -191,7 +191,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, } - ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id); + ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); if (extmark.row < 0) { return rv; } @@ -252,7 +252,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e return rv; } - if (!ns_initialized((uint64_t)ns_id)) { + if (!ns_initialized((uint32_t)ns_id)) { api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); return rv; } @@ -310,7 +310,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e } - ExtmarkInfoArray marks = extmark_get(buf, (uint64_t)ns_id, l_row, l_col, + ExtmarkInfoArray marks = extmark_get(buf, (uint32_t)ns_id, l_row, l_col, u_row, u_col, (int64_t)limit, reverse); for (size_t i = 0; i < kv_size(marks); i++) { @@ -417,14 +417,14 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - if (!ns_initialized((uint64_t)ns_id)) { + if (!ns_initialized((uint32_t)ns_id)) { api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); goto error; } - uint64_t id = 0; + uint32_t id = 0; if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) { - id = (uint64_t)opts->id.data.integer; + id = (uint32_t)opts->id.data.integer; } else if (HAS_KEY(opts->id)) { api_set_error(err, kErrorTypeValidation, "id is not a positive integer"); goto error; @@ -628,20 +628,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = 0; } - Decoration *d = NULL; - - if (ephemeral) { - d = &decor; - } else if (kv_size(decor.virt_text) || kv_size(decor.virt_lines) - || decor.priority != DECOR_PRIORITY_BASE - || decor.hl_eol) { - // TODO(bfredl): this is a bit sketchy. eventually we should - // have predefined decorations for both marks/ephemerals - d = xcalloc(1, sizeof(*d)); - *d = decor; - } else if (decor.hl_id) { - d = decor_hl(decor.hl_id); - } // TODO(bfredl): synergize these two branches even more if (ephemeral && decor_state.buf == buf) { @@ -652,12 +638,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, - d, right_gravity, end_right_gravity, kExtmarkNoUndo); - - if (kv_size(decor.virt_lines)) { - redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(decor.virt_lines_above?0:1))); - } + extmark_set(buf, (uint32_t)ns_id, &id, (int)line, (colnr_T)col, line2, col2, + &decor, right_gravity, end_right_gravity, kExtmarkNoUndo); } return (Integer)id; @@ -682,23 +664,23 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er if (!buf) { return false; } - if (!ns_initialized((uint64_t)ns_id)) { + if (!ns_initialized((uint32_t)ns_id)) { api_set_error(err, kErrorTypeValidation, "Invalid ns_id"); return false; } - return extmark_del(buf, (uint64_t)ns_id, (uint64_t)id); + return extmark_del(buf, (uint32_t)ns_id, (uint32_t)id); } -uint64_t src2ns(Integer *src_id) +uint32_t src2ns(Integer *src_id) { if (*src_id == 0) { *src_id = nvim_create_namespace((String)STRING_INIT); } if (*src_id < 0) { - return UINT64_MAX; + return (((uint32_t)1) << 31) - 1; } else { - return (uint64_t)(*src_id); + return (uint32_t)(*src_id); } } @@ -753,7 +735,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In col_end = MAXCOL; } - uint64_t ns = src2ns(&ns_id); + uint32_t ns = src2ns(&ns_id); if (!(line < buf->b_ml.ml_line_count)) { // safety check, we can't add marks outside the range @@ -773,10 +755,13 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In end_line++; } + Decoration decor = DECORATION_INIT; + decor.hl_id = hl_id; + extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, - decor_hl(hl_id), true, false, kExtmarkNoUndo); + &decor, true, false, kExtmarkNoUndo); return ns_id; } @@ -808,7 +793,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, if (line_end < 0 || line_end > MAXLNUM) { line_end = MAXLNUM; } - extmark_clear(buf, (ns_id < 0 ? 0 : (uint64_t)ns_id), + extmark_clear(buf, (ns_id < 0 ? 0 : (uint32_t)ns_id), (int)line_start, 0, (int)line_end-1, MAXCOL); } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f9603acbda..f777fa1d27 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1141,7 +1141,7 @@ bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, int return false; } - ExtmarkInfo extmark = extmark_from_id(buf, (uint64_t)ns_id, (uint64_t)id); + ExtmarkInfo extmark = extmark_from_id(buf, (uint32_t)ns_id, (uint32_t)id); if (extmark.row >= 0) { *row = extmark.row; *col = extmark.col; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 63a550c017..bba53b415a 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -866,8 +866,7 @@ struct file_buffer { int b_mapped_ctrl_c; // modes where CTRL-C is mapped MarkTree b_marktree[1]; - Map(uint64_t, ExtmarkItem) b_extmark_index[1]; - Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces + Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces size_t b_virt_line_blocks; // number of virt_line blocks // array of channel_id:s which have asked to receive updates for this diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index c0f3c32f93..935b233752 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -13,8 +13,6 @@ # include "decoration.c.generated.h" #endif -static PMap(uint64_t) hl_decors; - /// Add highlighting to a buffer, bounded by two cursor positions, /// with an offset. /// @@ -33,9 +31,9 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start { colnr_T hl_start = 0; colnr_T hl_end = 0; - Decoration *decor = decor_hl(hl_id); + Decoration decor = DECORATION_INIT; + decor.hl_id = hl_id; - decor->priority = DECOR_PRIORITY_BASE; // TODO(bfredl): if decoration had blocky mode, we could avoid this loop for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum++) { int end_off = 0; @@ -59,40 +57,23 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start hl_start = pos_start.col + offset; hl_end = pos_end.col + offset; } - (void)extmark_set(buf, (uint64_t)src_id, NULL, - (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, - decor, true, false, kExtmarkNoUndo); - } -} - -Decoration *decor_hl(int hl_id) -{ - assert(hl_id > 0); - Decoration **dp = (Decoration **)pmap_ref(uint64_t)(&hl_decors, - (uint64_t)hl_id, true); - if (*dp) { - return *dp; + extmark_set(buf, (uint32_t)src_id, NULL, + (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, + &decor, true, false, kExtmarkNoUndo); } - - Decoration *decor = xcalloc(1, sizeof(*decor)); - decor->hl_id = hl_id; - decor->shared = true; - decor->priority = DECOR_PRIORITY_BASE; - *dp = decor; - return decor; } void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { - if (decor->hl_id && row2 >= row1) { + if ((!decor || decor->hl_id) && row2 >= row1) { redraw_buf_range_later(buf, row1+1, row2+1); } - if (kv_size(decor->virt_text)) { + if (decor && kv_size(decor->virt_text)) { redraw_buf_line_later(buf, row1+1); } - if (kv_size(decor->virt_lines)) { + if (decor && kv_size(decor->virt_lines)) { redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, row1+1+(decor->virt_lines_above?0:1))); } @@ -100,17 +81,17 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { - if (kv_size(decor->virt_lines)) { + decor_redraw(buf, row, row2, decor); + if (decor && kv_size(decor->virt_lines)) { assert(buf->b_virt_line_blocks > 0); buf->b_virt_line_blocks--; } - decor_redraw(buf, row, row2, decor); decor_free(decor); } void decor_free(Decoration *decor) { - if (decor && !decor->shared) { + if (decor) { clear_virttext(&decor->virt_text); for (size_t i = 0; i < kv_size(decor->virt_lines); i++) { clear_virttext(&kv_A(decor->virt_lines, i).line); @@ -134,17 +115,16 @@ Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id) MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, row, 0, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 || mark.row > row) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > row) { break; - } else if (marktree_decor_level(mark.id) < kDecorLevelVisible) { + } else if (marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - if (item && (ns_id == 0 || ns_id == item->ns_id) - && item->decor && kv_size(item->decor->virt_text)) { - return item->decor; + Decoration *decor = mark.decor_full; + if ((ns_id == 0 || ns_id == mark.ns) + && decor && kv_size(decor->virt_text)) { + return decor; } next_mark: marktree_itr_next(buf->b_marktree, itr); @@ -163,7 +143,20 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) } } kv_size(state->active) = 0; - return map_size(buf->b_extmark_index); + return buf->b_marktree->n_keys; +} + +Decoration get_decor(mtkey_t mark) +{ + if (mark.decor_full) { + return *mark.decor_full; + } else { + Decoration fake = DECORATION_INIT; + fake.hl_id = mark.hl_id; + fake.priority = mark.priority; + fake.hl_eol = (mark.flags & MT_FLAG_HL_EOL); + return fake; + } } @@ -176,42 +169,35 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) } marktree_itr_rewind(buf->b_marktree, state->itr); while (true) { - mtmark_t mark = marktree_itr_current(state->itr); - if (mark.row < 0) { // || mark.row > end_row + mtkey_t mark = marktree_itr_current(state->itr); + if (mark.pos.row < 0) { // || mark.row > end_row break; } - if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG) - || marktree_decor_level(mark.id) < kDecorLevelVisible) { + if ((mark.pos.row < top_row && mt_end(mark)) + || marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - start_id, false); - if (!item || !item->decor) { - goto next_mark; - } - Decoration *decor = item->decor; + Decoration decor = get_decor(mark); - mtpos_t altpos = marktree_lookup(buf->b_marktree, - mark.id^MARKTREE_END_FLAG, NULL); + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row - && !kv_size(decor->virt_text)) - || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { + if ((!mt_end(mark) && altpos.row < top_row + && !kv_size(decor.virt_text)) + || (mt_end(mark) && altpos.row >= top_row)) { goto next_mark; } - if (mark.id&MARKTREE_END_FLAG) { - decor_add(state, altpos.row, altpos.col, mark.row, mark.col, - decor, false); + if (mt_end(mark)) { + decor_add(state, altpos.row, altpos.col, mark.pos.row, mark.pos.col, + &decor, false); } else { if (altpos.row == -1) { - altpos.row = mark.row; - altpos.col = mark.col; + altpos.row = mark.pos.row; + altpos.col = mark.pos.col; } - decor_add(state, mark.row, mark.col, altpos.row, altpos.col, - decor, false); + decor_add(state, mark.pos.row, mark.pos.col, altpos.row, altpos.col, + &decor, false); } next_mark: @@ -266,43 +252,36 @@ int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState * while (true) { // TODO(bfredl): check duplicate entry in "intersection" // branch - mtmark_t mark = marktree_itr_current(state->itr); - if (mark.row < 0 || mark.row > state->row) { + mtkey_t mark = marktree_itr_current(state->itr); + if (mark.pos.row < 0 || mark.pos.row > state->row) { break; - } else if (mark.row == state->row && mark.col > col) { - state->col_until = mark.col-1; + } else if (mark.pos.row == state->row && mark.pos.col > col) { + state->col_until = mark.pos.col-1; break; } - if ((mark.id&MARKTREE_END_FLAG) - || marktree_decor_level(mark.id) < kDecorLevelVisible) { + if (mt_end(mark) + || marktree_decor_level(mark) < kDecorLevelVisible) { goto next_mark; } - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - if (!item || !item->decor) { - goto next_mark; - } - Decoration *decor = item->decor; + Decoration decor = get_decor(mark); - mtpos_t endpos = marktree_lookup(buf->b_marktree, - mark.id|MARKTREE_END_FLAG, NULL); + mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); if (endpos.row == -1) { - endpos.row = mark.row; - endpos.col = mark.col; + endpos = mark.pos; } - if (endpos.row < mark.row - || (endpos.row == mark.row && endpos.col <= mark.col)) { - if (!kv_size(decor->virt_text)) { + if (endpos.row < mark.pos.row + || (endpos.row == mark.pos.row && endpos.col <= mark.pos.col)) { + if (!kv_size(decor.virt_text)) { goto next_mark; } } - decor_add(state, mark.row, mark.col, endpos.row, endpos.col, - decor, false); + decor_add(state, mark.pos.row, mark.pos.col, endpos.row, endpos.col, + &decor, false); next_mark: marktree_itr_next(buf->b_marktree, state->itr); @@ -452,18 +431,18 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, row, 0, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 || mark.row >= end_row) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row >= end_row) { break; - } else if (marktree_decor_level(mark.id) < kDecorLevelVirtLine) { + } else if (marktree_decor_level(mark) < kDecorLevelVirtLine) { goto next_mark; } - bool above = mark.row > (int)(lnum - 2); - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark.id, false); - if (item && item->decor && item->decor->virt_lines_above == above) { - virt_lines += (int)kv_size(item->decor->virt_lines); + bool above = mark.pos.row > (int)(lnum - 2); + Decoration *decor = mark.decor_full; + if (decor && decor->virt_lines_above == above) { + virt_lines += (int)kv_size(decor->virt_lines); if (lines) { - kv_splice(*lines, item->decor->virt_lines); + kv_splice(*lines, decor->virt_lines); } } next_mark: diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 611b4223da..209e2176f2 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -42,7 +42,6 @@ struct Decoration { // TODO(bfredl): at some point turn this into FLAGS bool virt_text_hide; bool hl_eol; - bool shared; // shared decoration, don't free bool virt_lines_above; // TODO(bfredl): style, signs, etc DecorPriority priority; @@ -50,7 +49,7 @@ struct Decoration { int virt_text_width; // width of virt_text }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, false, DECOR_PRIORITY_BASE, 0, 0 } + false, false, false, DECOR_PRIORITY_BASE, 0, 0 } typedef struct { int start_row; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index c4d8f75a21..cee657c8c9 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -48,28 +48,29 @@ # include "extmark.c.generated.h" #endif -static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) +static uint32_t *buf_ns_ref(buf_T *buf, uint32_t ns_id, bool put) { - return map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); + return map_ref(uint32_t, uint32_t)(buf->b_extmark_ns, ns_id, put); } /// Create or update an extmark /// /// must not be used during iteration! -/// @returns the internal mark id -uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T col, int end_row, - colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity, - ExtmarkOp op) +void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col, int end_row, + colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity, + ExtmarkOp op) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - assert(ns != NULL); - mtpos_t old_pos; - uint64_t mark = 0; - uint64_t id = idp ? *idp : 0; + uint32_t *ns = buf_ns_ref(buf, ns_id, true); + uint32_t id = idp ? *idp : 0; + bool decor_full = false; uint8_t decor_level = kDecorLevelNone; // no decor if (decor) { + if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) { + decor_full = true; + decor = xmemdup(decor, sizeof *decor); + } decor_level = kDecorLevelVisible; // decor affects redraw if (kv_size(decor->virt_lines)) { decor_level = kDecorLevelVirtLine; // decor affects horizontal size @@ -77,50 +78,64 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T } if (id == 0) { - id = ns->free_id++; + id = ++*ns; } else { - uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (old_mark) { - if (old_mark & MARKTREE_PAIRED_FLAG || end_row > -1) { + MarkTreeIter itr[1] = { 0 }; + mtkey_t old_mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); + if (old_mark.id) { + if (mt_paired(old_mark) || end_row > -1) { extmark_del(buf, ns_id, id); } else { - MarkTreeIter itr[1] = { 0 }; - old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); + // TODO(bfredl): we need to do more if "revising" a decoration mark. assert(itr->node); - if (old_pos.row == row && old_pos.col == col) { - ExtmarkItem it = map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, - old_mark); - if (it.decor) { - decor_remove(buf, row, row, it.decor); + if (old_mark.pos.row == row && old_mark.pos.col == col) { + if (marktree_decor_level(old_mark) > kDecorLevelNone) { + decor_remove(buf, row, row, old_mark.decor_full); + old_mark.decor_full = NULL; + } + old_mark.flags = 0; + if (decor_full) { + old_mark.decor_full = decor; + } else if (decor) { + old_mark.hl_id = decor->hl_id; + // Workaround: the gcc compiler of functionaltest-lua build + // apparently incapable of handling basic integer constants. + // This can be underanged as soon as we bump minimal gcc version. + old_mark.flags = (uint16_t)(old_mark.flags + | (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0)); + old_mark.priority = decor->priority; } - mark = marktree_revise(buf->b_marktree, itr, decor_level); + marktree_revise(buf->b_marktree, itr, decor_level, old_mark); goto revised; } marktree_del_itr(buf->b_marktree, itr, false); } } else { - ns->free_id = MAX(ns->free_id, id+1); + *ns = MAX(*ns, id); } } - if (end_row > -1) { - mark = marktree_put_pair(buf->b_marktree, - row, col, right_gravity, - end_row, end_col, end_right_gravity, decor_level); - } else { - mark = marktree_put(buf->b_marktree, row, col, right_gravity, decor_level); + mtkey_t mark = { { row, col }, ns_id, id, 0, + mt_flags(right_gravity, decor_level), 0, NULL }; + if (decor_full) { + mark.decor_full = decor; + } else if (decor) { + mark.hl_id = decor->hl_id; + // workaround: see above + mark.flags = (uint16_t)(mark.flags | (decor->hl_eol ? (uint16_t)MT_FLAG_HL_EOL : (uint16_t)0)); + mark.priority = decor->priority; } -revised: - map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, - (ExtmarkItem){ ns_id, id, decor }); - map_put(uint64_t, uint64_t)(ns->map, id, mark); + marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity); +revised: if (op != kExtmarkNoUndo) { // TODO(bfredl): this doesn't cover all the cases and probably shouldn't // be done "prematurely". Any movement in undo history might necessitate - // adding new marks to old undo headers. - u_extmark_set(buf, mark, row, col); + // adding new marks to old undo headers. add a test case for this (doesn't + // fail extmark_spec.lua, and it should) + uint64_t mark_id = mt_lookup_id(ns_id, id, false); + u_extmark_set(buf, mark_id, row, col); } if (decor) { @@ -133,18 +148,17 @@ revised: if (idp) { *idp = id; } - return mark; } static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) { MarkTreeIter itr[1] = { 0 }; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - if (pos.row == -1) { + mtkey_t key = marktree_lookup(buf->b_marktree, mark, itr); + if (key.pos.row == -1) { return false; } - if (pos.row == row && pos.col == col) { + if (key.pos.row == row && key.pos.col == col) { return true; } @@ -154,45 +168,35 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) // Remove an extmark // Returns 0 on missing id -bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) +bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - if (!ns) { - return false; - } - - uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (!mark) { + MarkTreeIter itr[1] = { 0 }; + mtkey_t key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr); + if (!key.id) { return false; } - - MarkTreeIter itr[1] = { 0 }; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - assert(pos.row >= 0); + assert(key.pos.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - mtpos_t pos2 = pos; - if (mark & MARKTREE_PAIRED_FLAG) { - pos2 = marktree_lookup(buf->b_marktree, mark|MARKTREE_END_FLAG, itr); - assert(pos2.row >= 0); + mtkey_t key2 = key; + + if (mt_paired(key)) { + key2 = marktree_lookup_ns(buf->b_marktree, ns_id, id, true, itr); + assert(key2.pos.row >= 0); marktree_del_itr(buf->b_marktree, itr, false); } - if (item.decor) { - decor_remove(buf, pos.row, pos2.row, item.decor); + if (marktree_decor_level(key) > kDecorLevelNone) { + decor_remove(buf, key.pos.row, key2.pos.row, key.decor_full); } - map_del(uint64_t, uint64_t)(ns->map, id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - // TODO(bfredl): delete it from current undo header, opportunistically? return true; } // Free extmarks in a ns between lines // if ns = 0, it means clear all namespaces -bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) +bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) { if (!map_size(buf->b_extmark_ns)) { return false; @@ -201,68 +205,58 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r bool marks_cleared = false; bool all_ns = (ns_id == 0); - ExtmarkNs *ns = NULL; + uint32_t *ns = NULL; if (!all_ns) { ns = buf_ns_ref(buf, ns_id, false); if (!ns) { // nothing to do return false; } - - // TODO(bfredl): if map_size(ns->map) << buf->b_marktree.n_nodes - // it could be faster to iterate over the map instead } // the value is either zero or the lnum (row+1) if highlight was present. static Map(uint64_t, ssize_t) delete_set = MAP_INIT; - typedef struct { Decoration *decor; int row1; } DecorItem; + typedef struct { int row1; } DecorItem; static kvec_t(DecorItem) decors; MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 - || mark.row > u_row - || (mark.row == u_row && mark.col > u_col)) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 + || mark.pos.row > u_row + || (mark.pos.row == u_row && mark.pos.col > u_col)) { break; } - ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mark.id, + ssize_t *del_status = map_ref(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark), false); if (del_status) { marktree_del_itr(buf->b_marktree, itr, false); if (*del_status >= 0) { // we had a decor_id DecorItem it = kv_A(decors, *del_status); - decor_remove(buf, it.row1, mark.row, it.decor); + decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } - map_del(uint64_t, ssize_t)(&delete_set, mark.id); + map_del(uint64_t, ssize_t)(&delete_set, mt_lookup_key(mark)); continue; } - uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - start_id); - - assert(item.ns_id > 0 && item.mark_id > 0); - if (item.mark_id > 0 && (item.ns_id == ns_id || all_ns)) { + assert(mark.ns > 0 && mark.id > 0); + if (mark.ns == ns_id || all_ns) { marks_cleared = true; - if (mark.id & MARKTREE_PAIRED_FLAG) { - uint64_t other = mark.id ^ MARKTREE_END_FLAG; + if (mt_paired(mark)) { + uint64_t other = mt_lookup_id(mark.ns, mark.id, !mt_end(mark)); ssize_t decor_id = -1; - if (item.decor) { + if (marktree_decor_level(mark) > kDecorLevelNone) { // Save the decoration and the first pos. Clear the decoration // later when we know the full range. decor_id = (ssize_t)kv_size(decors); kv_push(decors, - ((DecorItem) { .decor = item.decor, .row1 = mark.row })); + ((DecorItem) { .row1 = mark.pos.row })); } map_put(uint64_t, ssize_t)(&delete_set, other, decor_id); - } else if (item.decor) { - decor_remove(buf, mark.row, mark.row, item.decor); + } else if (mark.decor_full) { + decor_remove(buf, mark.pos.row, mark.pos.row, mark.decor_full); } - ExtmarkNs *my_ns = all_ns ? buf_ns_ref(buf, item.ns_id, false) : ns; - map_del(uint64_t, uint64_t)(my_ns->map, item.mark_id); - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); marktree_del_itr(buf->b_marktree, itr, false); } else { marktree_itr_next(buf->b_marktree, itr); @@ -271,12 +265,12 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r uint64_t id; ssize_t decor_id; map_foreach(&delete_set, id, decor_id, { - mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); + mtkey_t mark = marktree_lookup(buf->b_marktree, id, itr); assert(itr->node); marktree_del_itr(buf->b_marktree, itr, false); if (decor_id >= 0) { DecorItem it = kv_A(decors, decor_id); - decor_remove(buf, it.row1, pos.row, it.decor); + decor_remove(buf, it.row1, mark.pos.row, mark.decor_full); } }); map_clear(uint64_t, ssize_t)(&delete_set); @@ -290,7 +284,7 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r // will be searched to the start, or end // dir can be set to control the order of the array // amount = amount of marks to find or -1 for all -ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_row, +ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col, int64_t amount, bool reverse) { ExtmarkInfoArray array = KV_INITIAL_VALUE; @@ -300,30 +294,24 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_co itr, reverse, false, NULL); int order = reverse ? -1 : 1; while ((int64_t)kv_size(array) < amount) { - mtmark_t mark = marktree_itr_current(itr); - mtpos_t endpos = { -1, -1 }; - if (mark.row < 0 - || (mark.row - u_row) * order > 0 - || (mark.row == u_row && (mark.col - u_col) * order > 0)) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 + || (mark.pos.row - u_row) * order > 0 + || (mark.pos.row == u_row && (mark.pos.col - u_col) * order > 0)) { break; } - if (mark.id & MARKTREE_END_FLAG) { + if (mt_end(mark)) { goto next_mark; - } else if (mark.id & MARKTREE_PAIRED_FLAG) { - endpos = marktree_lookup(buf->b_marktree, mark.id | MARKTREE_END_FLAG, - NULL); } - - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id); - if (item.ns_id == ns_id) { - kv_push(array, ((ExtmarkInfo) { .ns_id = item.ns_id, - .mark_id = item.mark_id, - .row = mark.row, .col = mark.col, + if (mark.ns == ns_id) { + mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + kv_push(array, ((ExtmarkInfo) { .ns_id = mark.ns, + .mark_id = mark.id, + .row = mark.pos.row, .col = mark.pos.col, .end_row = endpos.row, .end_col = endpos.col, - .decor = item.decor })); + .decor = get_decor(mark) })); } next_mark: if (reverse) { @@ -336,36 +324,23 @@ next_mark: } // Lookup an extmark by id -ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) +ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, NULL }; - if (!ns) { + ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, DECORATION_INIT }; + mtkey_t mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL); + if (!mark.id) { return ret; } - - uint64_t mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (!mark) { - return ret; - } - - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, NULL); - mtpos_t endpos = { -1, -1 }; - if (mark & MARKTREE_PAIRED_FLAG) { - endpos = marktree_lookup(buf->b_marktree, mark | MARKTREE_END_FLAG, NULL); - } - assert(pos.row >= 0); - - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark); + assert(mark.pos.row >= 0); + mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); ret.ns_id = ns_id; ret.mark_id = id; - ret.row = pos.row; - ret.col = pos.col; + ret.row = mark.pos.row; + ret.col = mark.pos.col; ret.end_row = endpos.row; ret.end_col = endpos.col; - ret.decor = item.decor; + ret.decor = get_decor(mark); return ret; } @@ -378,25 +353,26 @@ void extmark_free_all(buf_T *buf) return; } - uint64_t id; - ExtmarkNs ns; - ExtmarkItem item; + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, 0, 0, itr); + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0) { + break; + } - marktree_clear(buf->b_marktree); + // don't free mark.decor_full twice for a paired mark. + if (!(mt_paired(mark) && mt_end(mark))) { + decor_free(mark.decor_full); + } - map_foreach(buf->b_extmark_ns, id, ns, { - (void)id; - map_destroy(uint64_t, uint64_t)(ns.map); - }); - map_destroy(uint64_t, ExtmarkNs)(buf->b_extmark_ns); - map_init(uint64_t, ExtmarkNs, buf->b_extmark_ns); + marktree_itr_next(buf->b_marktree, itr); + } - map_foreach(buf->b_extmark_index, id, item, { - (void)id; - decor_free(item.decor); - }); - map_destroy(uint64_t, ExtmarkItem)(buf->b_extmark_index); - map_init(uint64_t, ExtmarkItem, buf->b_extmark_index); + marktree_clear(buf->b_marktree); + + map_destroy(uint32_t, uint32_t)(buf->b_extmark_ns); + map_init(uint32_t, uint32_t, buf->b_extmark_ns); } @@ -437,16 +413,16 @@ void u_extmark_copy(buf_T *buf, int l_row, colnr_T l_col, int u_row, colnr_T u_c MarkTreeIter itr[1] = { 0 }; marktree_itr_get(buf->b_marktree, l_row, l_col, itr); while (true) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 - || mark.row > u_row - || (mark.row == u_row && mark.col > u_col)) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 + || mark.pos.row > u_row + || (mark.pos.row == u_row && mark.pos.col > u_col)) { break; } ExtmarkSavePos pos; - pos.mark = mark.id; - pos.old_row = mark.row; - pos.old_col = mark.col; + pos.mark = mt_lookup_key(mark); + pos.old_row = mark.pos.row; + pos.old_col = mark.pos.col; pos.row = -1; pos.col = -1; diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h index c70db9f7aa..c6ec1d0aa8 100644 --- a/src/nvim/extmark.h +++ b/src/nvim/extmark.h @@ -3,6 +3,7 @@ #include "nvim/buffer_defs.h" #include "nvim/extmark_defs.h" +#include "nvim/decoration.h" #include "nvim/marktree.h" #include "nvim/pos.h" @@ -15,7 +16,7 @@ typedef struct { colnr_T col; int end_row; colnr_T end_col; - Decoration *decor; + Decoration decor; // TODO(bfredl): CHONKY } ExtmarkInfo; typedef kvec_t(ExtmarkInfo) ExtmarkInfoArray; diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index bbe8504ebf..5570b5c71e 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -4,23 +4,11 @@ #include "nvim/lib/kvec.h" #include "nvim/types.h" -typedef struct Decoration Decoration; - typedef struct { char *text; int hl_id; } VirtTextChunk; - -typedef struct { - uint64_t ns_id; - uint64_t mark_id; - // TODO(bfredl): a lot of small allocations. Should probably use - // kvec_t(Decoration) as an arena. Alternatively, store ns_id/mark_id - // _inline_ in MarkTree and use the map only for decorations. - Decoration *decor; -} ExtmarkItem; - typedef struct undo_object ExtmarkUndoObject; typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t; diff --git a/src/nvim/map.c b/src/nvim/map.c index c77433df71..77ebc2a387 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -171,10 +171,7 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ssize_t, SSIZE_INITIALIZER) MAP_IMPL(uint64_t, uint64_t, DEFAULT_INITIALIZER) -#define EXTMARK_NS_INITIALIZER { { MAP_INIT }, 1 } -MAP_IMPL(uint64_t, ExtmarkNs, EXTMARK_NS_INITIALIZER) -#define EXTMARK_ITEM_INITIALIZER { 0, 0, NULL } -MAP_IMPL(uint64_t, ExtmarkItem, EXTMARK_ITEM_INITIALIZER) +MAP_IMPL(uint32_t, uint32_t, DEFAULT_INITIALIZER) MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) #define MSGPACK_HANDLER_INITIALIZER { .fn = NULL, .fast = false } MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index dbd85a4e1f..5e56f4dd65 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -40,16 +40,8 @@ MAP_DECLS(ptr_t, ptr_t) MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(uint64_t, ssize_t) MAP_DECLS(uint64_t, uint64_t) +MAP_DECLS(uint32_t, uint32_t) -// NB: this is the only way to define a struct both containing and contained -// in a map... -typedef struct ExtmarkNs { // For namespacing extmarks - Map(uint64_t, uint64_t) map[1]; // For fast lookup - uint64_t free_id; // For automatically assigning id's -} ExtmarkNs; - -MAP_DECLS(uint64_t, ExtmarkNs) -MAP_DECLS(uint64_t, ExtmarkItem) MAP_DECLS(handle_T, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 38014ab375..8ae138b2eb 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -56,12 +56,6 @@ #define T MT_BRANCH_FACTOR #define ILEN (sizeof(mtnode_t)+(2 * T) * sizeof(void *)) -#define RIGHT_GRAVITY (((uint64_t)1) << 63) -#define ANTIGRAVITY(id) ((id)&(RIGHT_GRAVITY-1)) -#define IS_RIGHT(id) ((id)&RIGHT_GRAVITY) - -#define PAIRED MARKTREE_PAIRED_FLAG -#define END_FLAG MARKTREE_END_FLAG #define ID_INCR (((uint64_t)1) << 2) #define rawkey(itr) (itr->node->key[itr->i]) @@ -119,7 +113,7 @@ static int key_cmp(mtkey_t a, mtkey_t b) } // NB: keeping the events at the same pos sorted by id is actually not // necessary only make sure that START is before END etc. - return mt_generic_cmp(a.id, b.id); + return mt_generic_cmp(a.flags, b.flags); } static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r) @@ -148,7 +142,7 @@ static inline int marktree_getp_aux(const mtnode_t *x, mtkey_t k, int *r) static inline void refkey(MarkTree *b, mtnode_t *x, int i) { - pmap_put(uint64_t)(b->id2node, ANTIGRAVITY(x->key[i].id), x); + pmap_put(uint64_t)(b->id2node, mt_lookup_key(x->key[i]), x); } // put functions @@ -221,38 +215,28 @@ static inline void marktree_putp_aux(MarkTree *b, mtnode_t *x, mtkey_t k) } } -uint64_t marktree_put(MarkTree *b, int row, int col, bool right_gravity, uint8_t decor_level) +void marktree_put(MarkTree *b, mtkey_t key, int end_row, int end_col, bool end_right) { - uint64_t id = (b->next_id+=ID_INCR); - assert(decor_level < DECOR_LEVELS); - id = id | ((uint64_t)decor_level << DECOR_OFFSET); - uint64_t keyid = id; - if (right_gravity) { - // order all right gravity keys after the left ones, for effortless - // insertion (but not deletion!) - keyid |= RIGHT_GRAVITY; - } - marktree_put_key(b, row, col, keyid); - return id; -} + assert(!(key.flags & ~MT_FLAG_EXTERNAL_MASK)); + if (end_row >= 0) { + key.flags |= MT_FLAG_PAIRED; + } -uint64_t marktree_put_pair(MarkTree *b, int start_row, int start_col, bool start_right, int end_row, - int end_col, bool end_right, uint8_t decor_level) -{ - uint64_t id = (b->next_id+=ID_INCR)|PAIRED; - assert(decor_level < DECOR_LEVELS); - id = id | ((uint64_t)decor_level << DECOR_OFFSET); - uint64_t start_id = id|(start_right?RIGHT_GRAVITY:0); - uint64_t end_id = id|END_FLAG|(end_right?RIGHT_GRAVITY:0); - marktree_put_key(b, start_row, start_col, start_id); - marktree_put_key(b, end_row, end_col, end_id); - return id; + marktree_put_key(b, key); + + if (end_row >= 0) { + mtkey_t end_key = key; + end_key.flags = (uint16_t)((uint16_t)(key.flags & ~MT_FLAG_RIGHT_GRAVITY) + |(uint16_t)MT_FLAG_END + |(uint16_t)(end_right ? MT_FLAG_RIGHT_GRAVITY : 0)); + end_key.pos = (mtpos_t){ end_row, end_col }; + marktree_put_key(b, end_key); + } } -void marktree_put_key(MarkTree *b, int row, int col, uint64_t id) +void marktree_put_key(MarkTree *b, mtkey_t k) { - mtkey_t k = { .pos = { .row = row, .col = col }, .id = id }; - + k.flags |= MT_FLAG_REAL; // let's be real. if (!b->root) { b->root = (mtnode_t *)xcalloc(1, ILEN); b->n_nodes++; @@ -302,7 +286,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) mtnode_t *cur = itr->node; int curi = itr->i; - uint64_t id = cur->key[curi].id; + uint64_t id = mt_lookup_key(cur->key[curi]); // fprintf(stderr, "\nDELET %lu\n", id); if (itr->node->level) { @@ -364,7 +348,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev) } b->n_keys--; - pmap_del(uint64_t)(b->id2node, ANTIGRAVITY(id)); + pmap_del(uint64_t)(b->id2node, id); // 5. bool itr_dirty = false; @@ -570,23 +554,29 @@ void marktree_free_node(mtnode_t *x) } /// NB: caller must check not pair! -uint64_t marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level) +void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_t key) { - uint64_t old_id = rawkey(itr).id; - pmap_del(uint64_t)(b->id2node, ANTIGRAVITY(old_id)); - uint64_t new_id = (b->next_id += ID_INCR) + ((uint64_t)decor_level << DECOR_OFFSET); - rawkey(itr).id = new_id + (RIGHT_GRAVITY&old_id); - refkey(b, itr->node, itr->i); - return new_id; + // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms + // once we upgrade to a non-broken version of gcc in functionaltest-lua CI + rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); + rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags + | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET) + | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)); + rawkey(itr).decor_full = key.decor_full; + rawkey(itr).hl_id = key.hl_id; + rawkey(itr).priority = key.priority; } void marktree_move(MarkTree *b, MarkTreeIter *itr, int row, int col) { - uint64_t old_id = rawkey(itr).id; + mtkey_t key = rawkey(itr); // TODO(bfredl): optimize when moving a mark within a leaf without moving it // across neighbours! marktree_del_itr(b, itr, false); - marktree_put_key(b, row, col, old_id); + key.pos = (mtpos_t){ row, col }; + + + marktree_put_key(b, key); itr->node = NULL; // itr might become invalid by put } @@ -602,14 +592,15 @@ bool marktree_itr_get(MarkTree *b, int row, int col, MarkTreeIter *itr) bool marktree_itr_get_ext(MarkTree *b, mtpos_t p, MarkTreeIter *itr, bool last, bool gravity, mtpos_t *oldbase) { - mtkey_t k = { .pos = p, .id = gravity ? RIGHT_GRAVITY : 0 }; - if (last && !gravity) { - k.id = UINT64_MAX; - } if (b->n_keys == 0) { itr->node = NULL; return false; } + + mtkey_t k = { .pos = p, .flags = gravity ? MT_FLAG_RIGHT_GRAVITY : 0 }; + if (last && !gravity) { + k.flags = MT_FLAG_LAST; + } itr->pos = (mtpos_t){ 0, 0 }; itr->node = b->root; itr->lvl = 0; @@ -816,25 +807,29 @@ mtpos_t marktree_itr_pos(MarkTreeIter *itr) return pos; } -mtmark_t marktree_itr_current(MarkTreeIter *itr) +mtkey_t marktree_itr_current(MarkTreeIter *itr) { if (itr->node) { - uint64_t keyid = rawkey(itr).id; - mtpos_t pos = marktree_itr_pos(itr); - mtmark_t mark = { .row = pos.row, - .col = pos.col, - .id = ANTIGRAVITY(keyid), - .right_gravity = keyid & RIGHT_GRAVITY }; - return mark; - } - return (mtmark_t){ -1, -1, 0, false }; + mtkey_t key = rawkey(itr); + key.pos = marktree_itr_pos(itr); + return key; + } + return MT_INVALID_KEY; } -static void swap_id(uint64_t *id1, uint64_t *id2) +static bool itr_eq(MarkTreeIter *itr1, MarkTreeIter *itr2) { - uint64_t temp = *id1; - *id1 = *id2; - *id2 = temp; + return (&rawkey(itr1) == &rawkey(itr2)); +} + +static void itr_swap(MarkTreeIter *itr1, MarkTreeIter *itr2) +{ + mtkey_t key1 = rawkey(itr1); + mtkey_t key2 = rawkey(itr2); + rawkey(itr1) = key2; + rawkey(itr1).pos = key1.pos; + rawkey(itr2) = key1; + rawkey(itr2).pos = key2.pos; } bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_line, @@ -865,7 +860,7 @@ bool marktree_splice(MarkTree *b, int start_line, int start_col, int old_extent_ mtpos_t ipos = marktree_itr_pos(itr); if (!pos_leq(old_extent, ipos) || (old_extent.row == ipos.row && old_extent.col == ipos.col - && !IS_RIGHT(rawkey(itr).id))) { + && !mt_right(rawkey(itr)))) { marktree_itr_get_ext(b, old_extent, enditr, true, true, NULL); assert(enditr->node); // "assert" (itr <= enditr) @@ -895,13 +890,13 @@ continue_same_node: break; } - if (IS_RIGHT(rawkey(itr).id)) { - while (rawkey(itr).id != rawkey(enditr).id - && IS_RIGHT(rawkey(enditr).id)) { + if (mt_right(rawkey(itr))) { + while (!itr_eq(itr, enditr) + && mt_right(rawkey(enditr))) { marktree_itr_prev(b, enditr); } - if (!IS_RIGHT(rawkey(enditr).id)) { - swap_id(&rawkey(itr).id, &rawkey(enditr).id); + if (!mt_right(rawkey(enditr))) { + itr_swap(itr, enditr); refkey(b, itr->node, itr->i); refkey(b, enditr->node, enditr->i); } else { @@ -911,7 +906,7 @@ continue_same_node: } } - if (rawkey(itr).id == rawkey(enditr).id) { + if (itr_eq(itr, enditr)) { // actually, will be past_right after this key past_right = true; } @@ -1006,13 +1001,13 @@ void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int ext marktree_itr_get_ext(b, start, itr, false, true, NULL); kvec_t(mtkey_t) saved = KV_INITIAL_VALUE; while (itr->node) { - mtpos_t pos = marktree_itr_pos(itr); - if (!pos_leq(pos, end) || (pos.row == end.row && pos.col == end.col - && rawkey(itr).id & RIGHT_GRAVITY)) { + mtkey_t k = marktree_itr_current(itr); + if (!pos_leq(k.pos, end) || (k.pos.row == end.row && k.pos.col == end.col + && mt_right(k))) { break; } - relative(start, &pos); - kv_push(saved, ((mtkey_t){ .pos = pos, .id = rawkey(itr).id })); + relative(start, &k.pos); + kv_push(saved, k); marktree_del_itr(b, itr, false); } @@ -1024,30 +1019,36 @@ void marktree_move_region(MarkTree *b, int start_row, colnr_T start_col, int ext for (size_t i = 0; i < kv_size(saved); i++) { mtkey_t item = kv_A(saved, i); unrelative(new, &item.pos); - marktree_put_key(b, item.pos.row, item.pos.col, item.id); + marktree_put_key(b, item); } kv_destroy(saved); } /// @param itr OPTIONAL. set itr to pos. -mtpos_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr) +mtkey_t marktree_lookup_ns(MarkTree *b, uint32_t ns, uint32_t id, bool end, MarkTreeIter *itr) +{ + return marktree_lookup(b, mt_lookup_id(ns, id, end), itr); +} + +/// @param itr OPTIONAL. set itr to pos. +mtkey_t marktree_lookup(MarkTree *b, uint64_t id, MarkTreeIter *itr) { mtnode_t *n = pmap_get(uint64_t)(b->id2node, id); if (n == NULL) { if (itr) { itr->node = NULL; } - return (mtpos_t){ -1, -1 }; + return MT_INVALID_KEY; } int i = 0; for (i = 0; i < n->n; i++) { - if (ANTIGRAVITY(n->key[i].id) == id) { + if (mt_lookup_key(n->key[i]) == id) { goto found; } } abort(); found: {} - mtpos_t pos = n->key[i].pos; + mtkey_t key = n->key[i]; if (itr) { itr->i = i; itr->node = n; @@ -1066,14 +1067,23 @@ found_node: itr->s[b->root->level-p->level].i = i; } if (i > 0) { - unrelative(p->key[i-1].pos, &pos); + unrelative(p->key[i-1].pos, &key.pos); } n = p; } if (itr) { marktree_itr_fix_pos(b, itr); } - return pos; + return key; +} + +mtpos_t marktree_get_altpos(MarkTree *b, mtkey_t mark, MarkTreeIter *itr) +{ + mtkey_t end = MT_INVALID_KEY; + if (mt_paired(mark)) { + end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr); + } + return end.pos; } static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) @@ -1092,6 +1102,20 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) assert(x == itr->node); } +// for unit test +void marktree_put_test(MarkTree *b, uint32_t id, int row, int col, bool right_gravity) +{ + mtkey_t key = { { row, col }, UINT32_MAX, id, 0, + mt_flags(right_gravity, 0), 0, NULL }; + marktree_put(b, key, -1, -1, false); +} + +// for unit test +bool mt_right_test(mtkey_t key) +{ + return mt_right(key); +} + void marktree_check(MarkTree *b) { #ifndef NDEBUG @@ -1134,11 +1158,11 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig } assert(pos_leq(*last, x->key[i].pos)); if (last->row == x->key[i].pos.row && last->col == x->key[i].pos.col) { - assert(!*last_right || IS_RIGHT(x->key[i].id)); + assert(!*last_right || mt_right(x->key[i])); } - *last_right = IS_RIGHT(x->key[i].id); + *last_right = mt_right(x->key[i]); assert(x->key[i].pos.col >= 0); - assert(pmap_get(uint64_t)(b->id2node, ANTIGRAVITY(x->key[i].id)) == x); + assert(pmap_get(uint64_t)(b->id2node, mt_lookup_key(x->key[i])) == x); } if (x->level) { diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index a1dcdf5164..95d63dd14a 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -3,8 +3,10 @@ #include +#include "nvim/assert.h" #include "nvim/garray.h" #include "nvim/map.h" +#include "nvim/types.h" #include "nvim/pos.h" #define MT_MAX_DEPTH 20 @@ -15,13 +17,6 @@ typedef struct { int32_t col; } mtpos_t; -typedef struct { - int32_t row; - int32_t col; - uint64_t id; - bool right_gravity; -} mtmark_t; - typedef struct mtnode_s mtnode_t; typedef struct { int oldcol; @@ -39,12 +34,75 @@ typedef struct { // Internal storage // -// NB: actual marks have id > 0, so we can use (row,col,0) pseudo-key for +// NB: actual marks have flags > 0, so we can use (row,col,0) pseudo-key for // "space before (row,col)" typedef struct { mtpos_t pos; - uint64_t id; + uint32_t ns; + uint32_t id; + int32_t hl_id; + uint16_t flags; + uint16_t priority; + Decoration *decor_full; } mtkey_t; +#define MT_INVALID_KEY (mtkey_t) { { -1, -1 }, 0, 0, 0, 0, 0, NULL } + +#define MT_FLAG_REAL (((uint16_t)1) << 0) +#define MT_FLAG_END (((uint16_t)1) << 1) +#define MT_FLAG_PAIRED (((uint16_t)1) << 2) +#define MT_FLAG_HL_EOL (((uint16_t)1) << 3) + +#define DECOR_LEVELS 4 +#define MT_FLAG_DECOR_OFFSET 4 +#define MT_FLAG_DECOR_MASK (((uint16_t)(DECOR_LEVELS-1)) << MT_FLAG_DECOR_OFFSET) + +// next flag is (((uint16_t)1) << 6) + +// These _must_ be last to preserve ordering of marks +#define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14) +#define MT_FLAG_LAST (((uint16_t)1) << 15) + +#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_RIGHT_GRAVITY | MT_FLAG_HL_EOL) + +#define MARKTREE_END_FLAG (((uint64_t)1) << 63) +static inline uint64_t mt_lookup_id(uint32_t ns, uint32_t id, bool enda) +{ + return (uint64_t)ns << 32 | id | (enda?MARKTREE_END_FLAG:0); +} +#undef MARKTREE_END_FLAG + +static inline uint64_t mt_lookup_key(mtkey_t key) +{ + return mt_lookup_id(key.ns, key.id, key.flags & MT_FLAG_END); +} + +static inline bool mt_paired(mtkey_t key) +{ + return key.flags & MT_FLAG_PAIRED; +} + +static inline bool mt_end(mtkey_t key) +{ + return key.flags & MT_FLAG_END; +} + +static inline bool mt_right(mtkey_t key) +{ + return key.flags & MT_FLAG_RIGHT_GRAVITY; +} + +static inline uint8_t marktree_decor_level(mtkey_t key) +{ + return (uint8_t)((key.flags&MT_FLAG_DECOR_MASK) >> MT_FLAG_DECOR_OFFSET); +} + +static inline uint16_t mt_flags(bool right_gravity, uint8_t decor_level) +{ + assert(decor_level < DECOR_LEVELS); + return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0) + | (decor_level << MT_FLAG_DECOR_OFFSET)); +} + struct mtnode_s { int32_t n; @@ -61,7 +119,6 @@ struct mtnode_s { typedef struct { mtnode_t *root; size_t n_keys, n_nodes; - uint64_t next_id; // TODO(bfredl): the pointer to node could be part of the larger // Map(uint64_t, ExtmarkItem) essentially; PMap(uint64_t) id2node[1]; @@ -72,16 +129,4 @@ typedef struct { # include "marktree.h.generated.h" #endif -#define MARKTREE_PAIRED_FLAG (((uint64_t)1) << 1) -#define MARKTREE_END_FLAG (((uint64_t)1) << 0) - -#define DECOR_LEVELS 4 -#define DECOR_OFFSET 61 -#define DECOR_MASK (((uint64_t)(DECOR_LEVELS-1)) << DECOR_OFFSET) - -static inline uint8_t marktree_decor_level(uint64_t id) -{ - return (uint8_t)((id&DECOR_MASK) >> DECOR_OFFSET); -} - #endif // NVIM_MARKTREE_H diff --git a/src/nvim/types.h b/src/nvim/types.h index 604155c33e..73cd2204d6 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -32,4 +32,6 @@ typedef enum { kTrue = 1, } TriState; +typedef struct Decoration Decoration; + #endif // NVIM_TYPES_H -- cgit From 9afefd32d3938a50a023355af86ec3293a047130 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Jan 2022 17:37:06 +0800 Subject: vim-patch:8.2.3630: printf() with %S does not handle multi-byte correctly Problem: Printf() with %S does not handle multi-byte correctly. Solution: Count cells instead of bytes. (closes vim/vim#9169, closes vim/vim#7486) https://github.com/vim/vim/commit/d85fccdfed58108c4e0958d0b17c64690b5f073f --- src/nvim/strings.c | 13 ++++++++----- src/nvim/testdir/test_expr.vim | 5 +++++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e2a8108c45..efcd40bb75 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1001,10 +1001,9 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t - str_arg); } if (fmt_spec == 'S') { - if (min_field_width != 0) { - min_field_width += (strlen(str_arg) - - mb_string2cells((char_u *)str_arg)); - } + size_t base_width = min_field_width; + size_t pad_cell = 0; + if (precision) { char_u *p1; size_t i = 0; @@ -1016,7 +1015,11 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t break; } } - str_arg_l = (size_t)(p1 - (char_u *)str_arg); + pad_cell = min_field_width - precision; + base_width = str_arg_l = (size_t)(p1 - (char_u *)str_arg); + } + if (min_field_width != 0) { + min_field_width = base_width + pad_cell; } } break; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 1d7fd3e385..447a519f2c 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -283,6 +283,11 @@ function Test_printf_misc() call assert_equal('ðŸ', printf('%.2S', 'ðŸðŸ')) call assert_equal('', printf('%.1S', 'ðŸðŸ')) + call assert_equal('[ ã‚ã„ã†]', printf('[%10.6S]', 'ã‚ã„ã†ãˆãŠ')) + call assert_equal('[ ã‚ã„ã†ãˆ]', printf('[%10.8S]', 'ã‚ã„ã†ãˆãŠ')) + call assert_equal('[ã‚ã„ã†ãˆãŠ]', printf('[%10.10S]', 'ã‚ã„ã†ãˆãŠ')) + call assert_equal('[ã‚ã„ã†ãˆãŠ]', printf('[%10.12S]', 'ã‚ã„ã†ãˆãŠ')) + call assert_equal('1%', printf('%d%%', 1)) endfunc -- cgit From 658ef9c01ee2e89b5a29f8fb018ac959ae2f4a90 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Jan 2022 17:37:06 +0800 Subject: vim-patch:8.2.3663: using %S in printf() does not work correctly Problem: Using %S in printf() does not work correctly. Solution: Fix the problem and add more tests. (closes vim/vim#9208) https://github.com/vim/vim/commit/1f2453fec6f8f0f315f00ca7b562a02090cb1e37 --- src/nvim/strings.c | 27 ++++++++----------- src/nvim/testdir/test_expr.vim | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/strings.c b/src/nvim/strings.c index efcd40bb75..291138ef23 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1001,25 +1001,20 @@ int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, t - str_arg); } if (fmt_spec == 'S') { - size_t base_width = min_field_width; - size_t pad_cell = 0; - - if (precision) { - char_u *p1; - size_t i = 0; - - for (p1 = (char_u *)str_arg; *p1; - p1 += utfc_ptr2len(p1)) { - i += (size_t)utf_ptr2cells(p1); - if (i > precision) { - break; - } + char_u *p1; + size_t i; + + for (i = 0, p1 = (char_u *)str_arg; *p1; p1 += utfc_ptr2len(p1)) { + size_t cell = (size_t)utf_ptr2cells(p1); + if (precision_specified && i + cell > precision) { + break; } - pad_cell = min_field_width - precision; - base_width = str_arg_l = (size_t)(p1 - (char_u *)str_arg); + i += cell; } + + str_arg_l = (size_t)(p1 - (char_u *)str_arg); if (min_field_width != 0) { - min_field_width = base_width + pad_cell; + min_field_width += str_arg_l - i; } } break; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 447a519f2c..5b10e691e5 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -288,6 +288,66 @@ function Test_printf_misc() call assert_equal('[ã‚ã„ã†ãˆãŠ]', printf('[%10.10S]', 'ã‚ã„ã†ãˆãŠ')) call assert_equal('[ã‚ã„ã†ãˆãŠ]', printf('[%10.12S]', 'ã‚ã„ã†ãˆãŠ')) + call assert_equal('ã‚ã„ã†', printf('%S', 'ã‚ã„ã†')) + call assert_equal('ã‚ã„ã†', printf('%#S', 'ã‚ã„ã†')) + + call assert_equal('ã‚b', printf('%2S', 'ã‚b')) + call assert_equal('ã‚b', printf('%.4S', 'ã‚b')) + call assert_equal('ã‚', printf('%.2S', 'ã‚b')) + call assert_equal(' ã‚b', printf('%4S', 'ã‚b')) + call assert_equal('0ã‚b', printf('%04S', 'ã‚b')) + call assert_equal('ã‚b ', printf('%-4S', 'ã‚b')) + call assert_equal('ã‚ ', printf('%-4.2S', 'ã‚b')) + + call assert_equal('aã„', printf('%2S', 'aã„')) + call assert_equal('aã„', printf('%.4S', 'aã„')) + call assert_equal('a', printf('%.2S', 'aã„')) + call assert_equal(' aã„', printf('%4S', 'aã„')) + call assert_equal('0aã„', printf('%04S', 'aã„')) + call assert_equal('aã„ ', printf('%-4S', 'aã„')) + call assert_equal('a ', printf('%-4.2S', 'aã„')) + + call assert_equal('[ã‚ã„ã†]', printf('[%05S]', 'ã‚ã„ã†')) + call assert_equal('[ã‚ã„ã†]', printf('[%06S]', 'ã‚ã„ã†')) + call assert_equal('[0ã‚ã„ã†]', printf('[%07S]', 'ã‚ã„ã†')) + + call assert_equal('[ã‚iã†]', printf('[%05S]', 'ã‚iã†')) + call assert_equal('[0ã‚iã†]', printf('[%06S]', 'ã‚iã†')) + call assert_equal('[00ã‚iã†]', printf('[%07S]', 'ã‚iã†')) + + call assert_equal('[0ã‚ã„]', printf('[%05.4S]', 'ã‚ã„ã†')) + call assert_equal('[00ã‚ã„]', printf('[%06.4S]', 'ã‚ã„ã†')) + call assert_equal('[000ã‚ã„]', printf('[%07.4S]', 'ã‚ã„ã†')) + + call assert_equal('[00ã‚i]', printf('[%05.4S]', 'ã‚iã†')) + call assert_equal('[000ã‚i]', printf('[%06.4S]', 'ã‚iã†')) + call assert_equal('[0000ã‚i]', printf('[%07.4S]', 'ã‚iã†')) + + call assert_equal('[0ã‚ã„]', printf('[%05.5S]', 'ã‚ã„ã†')) + call assert_equal('[00ã‚ã„]', printf('[%06.5S]', 'ã‚ã„ã†')) + call assert_equal('[000ã‚ã„]', printf('[%07.5S]', 'ã‚ã„ã†')) + + call assert_equal('[ã‚iã†]', printf('[%05.5S]', 'ã‚iã†')) + call assert_equal('[0ã‚iã†]', printf('[%06.5S]', 'ã‚iã†')) + call assert_equal('[00ã‚iã†]', printf('[%07.5S]', 'ã‚iã†')) + + call assert_equal('[0000000000]', printf('[%010.0S]', 'ã‚ã„ã†')) + call assert_equal('[0000000000]', printf('[%010.1S]', 'ã‚ã„ã†')) + call assert_equal('[00000000ã‚]', printf('[%010.2S]', 'ã‚ã„ã†')) + call assert_equal('[00000000ã‚]', printf('[%010.3S]', 'ã‚ã„ã†')) + call assert_equal('[000000ã‚ã„]', printf('[%010.4S]', 'ã‚ã„ã†')) + call assert_equal('[000000ã‚ã„]', printf('[%010.5S]', 'ã‚ã„ã†')) + call assert_equal('[0000ã‚ã„ã†]', printf('[%010.6S]', 'ã‚ã„ã†')) + call assert_equal('[0000ã‚ã„ã†]', printf('[%010.7S]', 'ã‚ã„ã†')) + + call assert_equal('[0000000000]', printf('[%010.1S]', 'ã‚iã†')) + call assert_equal('[00000000ã‚]', printf('[%010.2S]', 'ã‚iã†')) + call assert_equal('[0000000ã‚i]', printf('[%010.3S]', 'ã‚iã†')) + call assert_equal('[0000000ã‚i]', printf('[%010.4S]', 'ã‚iã†')) + call assert_equal('[00000ã‚iã†]', printf('[%010.5S]', 'ã‚iã†')) + call assert_equal('[00000ã‚iã†]', printf('[%010.6S]', 'ã‚iã†')) + call assert_equal('[00000ã‚iã†]', printf('[%010.7S]', 'ã‚iã†')) + call assert_equal('1%', printf('%d%%', 1)) endfunc -- cgit From be15ac06badbea6b11390ad7d9c2ddd4aea73480 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Jan 2022 18:44:28 +0800 Subject: feat(statusline): support multibyte fillchar This includes a partial port of Vim patch 8.2.2569 and some changes to nvim_eval_statusline() to allow a multibyte fillchar. Literally every line of C code touched by that patch has been refactored in Nvim, and that patch contains some irrelevant foldcolumn tests I'm not sure how to port (as Nvim's foldcolumn behavior has diverged from Vim's). --- src/nvim/api/vim.c | 15 +++++++------- src/nvim/buffer.c | 40 +++++++++++++++++------------------- src/nvim/macros.h | 3 +++ src/nvim/screen.c | 2 +- src/nvim/testdir/test_statusline.vim | 15 ++++++++++++++ 5 files changed, 46 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59db12f2c0..7ef8faa67e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -19,6 +19,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/charset.h" #include "nvim/context.h" #include "nvim/decoration.h" #include "nvim/edit.h" @@ -2234,7 +2235,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * Dictionary result = ARRAY_DICT_INIT; int maxwidth; - char fillchar = 0; + int fillchar = 0; Window window = 0; bool use_tabline = false; bool highlights = false; @@ -2249,12 +2250,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * } if (HAS_KEY(opts->fillchar)) { - if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size > 1) { - api_set_error(err, kErrorTypeValidation, "fillchar must be an ASCII character"); + if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0 + || char2cells(fillchar = utf_ptr2char((char_u *)opts->fillchar.data.string.data)) != 1 + || (size_t)utf_char2len(fillchar) != opts->fillchar.data.string.size) { + api_set_error(err, kErrorTypeValidation, "fillchar must be a single-width character"); return result; } - - fillchar = opts->fillchar.data.string.data[0]; } if (HAS_KEY(opts->highlights)) { @@ -2285,7 +2286,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * if (fillchar == 0) { int attr; - fillchar = (char)fillchar_status(&attr, wp); + fillchar = fillchar_status(&attr, wp); } } @@ -2313,7 +2314,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * sizeof(buf), (char_u *)str.data, false, - (char_u)fillchar, + fillchar, maxwidth, hltab_ptr, NULL); diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abd22fba26..9a02dff23b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3418,7 +3418,7 @@ typedef enum { /// /// @return The final width of the statusline int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use_sandbox, - char_u fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab) + int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab) { static size_t stl_items_len = 20; // Initial value, grows as needed. static stl_item_t *stl_items = NULL; @@ -3461,9 +3461,6 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use if (fillchar == 0) { fillchar = ' '; - } else if (utf_char2len(fillchar) > 1) { - // Can't handle a multi-byte fill character yet. - fillchar = '-'; } // The cursor in windows other than the current one isn't always @@ -3661,7 +3658,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use out_p = out_p - n + 1; // Fill up space left over by half a double-wide char. while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) { - *out_p++ = fillchar; + MB_CHAR2BYTES(fillchar, out_p); } // } @@ -3684,14 +3681,14 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use if (min_group_width < 0) { min_group_width = 0 - min_group_width; while (group_len++ < min_group_width && out_p < out_end_p) { - *out_p++ = fillchar; + MB_CHAR2BYTES(fillchar, out_p); } // If the group is right-aligned, shift everything to the right and // prepend with filler characters. } else { // { Move the group to the right - memmove(t + min_group_width - group_len, t, (size_t)(out_p - t)); - group_len = min_group_width - group_len; + group_len = (min_group_width - group_len) * utf_char2len(fillchar); + memmove(t + group_len, t, (size_t)(out_p - t)); if (out_p + group_len >= (out_end_p + 1)) { group_len = (long)(out_end_p - out_p); } @@ -3705,7 +3702,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Prepend the fill characters for (; group_len > 0; group_len--) { - *t++ = fillchar; + MB_CHAR2BYTES(fillchar, t); } } } @@ -4237,7 +4234,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) { *out_p++ = ' '; } else { - *out_p++ = fillchar; + MB_CHAR2BYTES(fillchar, out_p); } } minwid = 0; @@ -4248,20 +4245,21 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use } // { Copy the string text into the output buffer - while (*t && out_p < out_end_p) { - *out_p++ = *t++; + for (; *t && out_p < out_end_p; t++) { // Change a space by fillchar, unless fillchar is '-' and a // digit follows. - if (fillable && out_p[-1] == ' ' - && (!ascii_isdigit(*t) || fillchar != '-')) { - out_p[-1] = fillchar; + if (fillable && *t == ' ' + && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) { + MB_CHAR2BYTES(fillchar, out_p); + } else { + *out_p++ = *t; } } // } // For left-aligned items, fill any remaining space with the fillchar for (; l < minwid && out_p < out_end_p; l++) { - *out_p++ = fillchar; + MB_CHAR2BYTES(fillchar, out_p); } // Otherwise if the item is a number, copy that to the output buffer. @@ -4454,7 +4452,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Fill up for half a double-wide character. while (++width < maxwidth) { - *trunc_p++ = fillchar; + MB_CHAR2BYTES(fillchar, trunc_p); *trunc_p = NUL; } // } @@ -4505,13 +4503,13 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use standard_spaces * (num_separators - 1); for (int i = 0; i < num_separators; i++) { - int dislocation = (i == (num_separators - 1)) - ? final_spaces : standard_spaces; + int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces; + dislocation *= utf_char2len(fillchar); char_u *start = stl_items[stl_separator_locations[i]].start; char_u *seploc = start + dislocation; STRMOVE(seploc, start); - for (char_u *s = start; s < seploc; s++) { - *s = fillchar; + for (char_u *s = start; s < seploc; ) { + MB_CHAR2BYTES(fillchar, s); } for (int item_idx = stl_separator_locations[i] + 1; diff --git a/src/nvim/macros.h b/src/nvim/macros.h index c2b2c89abf..d5611f587b 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -105,6 +105,9 @@ #define MB_PTR_BACK(s, p) \ (p -= utf_head_off((char_u *)s, (char_u *)p - 1) + 1) +// MB_CHAR2BYTES(): convert character to bytes and advance pointer to bytes +#define MB_CHAR2BYTES(c, b) ((b) += utf_char2bytes((c), (b))) + #define RESET_BINDING(wp) \ do { \ (wp)->w_p_scb = false; \ diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 538604cf79..0cc25552fe 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1960,7 +1960,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ level = foldinfo.fi_level; // If the column is too narrow, we start at the lowest level that - // fits and use numbers to indicated the depth. + // fits and use numbers to indicate the depth. first_level = level - fdc - closed + 1; if (first_level < 1) { first_level = 1; diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index a3e4dcdd25..9c9853674b 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -498,5 +498,20 @@ func Test_statusline_after_split_vsplit() set ls& stl& endfunc +" Test using a multibyte character for 'stl' and 'stlnc' items in 'fillchars' +" with a custom 'statusline' +func Test_statusline_mbyte_fillchar() + only + set laststatus=2 + set fillchars=vert:\|,fold:-,stl:â”,stlnc:â• + set statusline=a%=b + call assert_match('^a\+â”\+b$', s:get_statusline()) + vnew + call assert_match('^a\+â”\+bâ”a\+â•\+b$', s:get_statusline()) + wincmd w + call assert_match('^a\+â•\+bâ•a\+â”\+b$', s:get_statusline()) + set statusline& fillchars& laststatus& + %bw! +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 3906b2d4fc617c6b03f7c9a615d18b70d7250e80 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 17 Jan 2022 11:58:36 +0100 Subject: vim-patch:fd31be29b822 (#17114) Update runtime files https://github.com/vim/vim/commit/fd31be29b8220ee1cb0b3460c82f2634ae3cc370 --- src/nvim/po/ja.euc-jp.po | 9 --------- src/nvim/po/ja.po | 9 --------- 2 files changed, 18 deletions(-) (limited to 'src') diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po index 5dda7c59f5..9633bec9f2 100644 --- a/src/nvim/po/ja.euc-jp.po +++ b/src/nvim/po/ja.euc-jp.po @@ -1644,15 +1644,6 @@ msgstr " [w]" msgid " written" msgstr " ½ñ¹þ¤ß" -msgid "E205: Patchmode: can't save original file" -msgstr "E205: patchmode: ¸¶ËÜ¥Õ¥¡¥¤¥ë¤òÊݸ¤Ç¤­¤Þ¤»¤ó" - -msgid "E206: patchmode: can't touch empty original file" -msgstr "E206: patchmode: ¶õ¤Î¸¶ËÜ¥Õ¥¡¥¤¥ë¤òtouch¤Ç¤­¤Þ¤»¤ó" - -msgid "E207: Can't delete backup file" -msgstr "E207: ¥Ð¥Ã¥¯¥¢¥Ã¥×¥Õ¥¡¥¤¥ë¤ò¾Ã¤»¤Þ¤»¤ó" - msgid "" "\n" "WARNING: Original file may be lost or damaged\n" diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index a169bd3589..c363c00fa6 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -1644,15 +1644,6 @@ msgstr " [w]" msgid " written" msgstr " 書込ã¿" -msgid "E205: Patchmode: can't save original file" -msgstr "E205: patchmode: 原本ファイルをä¿å­˜ã§ãã¾ã›ã‚“" - -msgid "E206: patchmode: can't touch empty original file" -msgstr "E206: patchmode: 空ã®åŽŸæœ¬ãƒ•ã‚¡ã‚¤ãƒ«ã‚’touchã§ãã¾ã›ã‚“" - -msgid "E207: Can't delete backup file" -msgstr "E207: ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ファイルを消ã›ã¾ã›ã‚“" - msgid "" "\n" "WARNING: Original file may be lost or damaged\n" -- cgit From fcf5dd34fdfde3a6632b96a88f66c1053cba08d1 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Mon, 17 Jan 2022 14:11:59 -0700 Subject: refactor: enable filetype detection before user startup scripts (#17040) --- src/nvim/main.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index cbd1f53727..9d1c2ad834 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -341,9 +341,11 @@ int main(int argc, char **argv) init_default_autocmds(); TIME_MSG("init default autocommands"); + bool vimrc_none = params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE"); + // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. - if (params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE")) { + if (vimrc_none) { // When using --clean we still want to load plugins p_lpl = params.clean; } @@ -351,14 +353,20 @@ int main(int argc, char **argv) // Execute --cmd arguments. exe_pre_commands(¶ms); + if (!vimrc_none) { + // Does ":filetype plugin indent on". We do this *before* the user startup scripts to ensure + // ftplugins run before FileType autocommands defined in the init file (which allows those + // autocommands to overwrite settings from ftplugins). + filetype_maybe_enable(); + } + // Source startup scripts. source_startup_scripts(¶ms); // If using the runtime (-u is not NONE), enable syntax & filetype plugins. - if (params.use_vimrc == NULL || !strequal(params.use_vimrc, "NONE")) { - // Does ":filetype plugin indent on". - filetype_maybe_enable(); - // Sources syntax/syntax.vim, which calls `:filetype on`. + if (!vimrc_none) { + // Sources syntax/syntax.vim. We do this *after* the user startup scripts so that users can + // disable syntax highlighting with `:syntax off` if they wish. syn_maybe_enable(); } -- cgit From 176df92bd7f8a3c8755b2a16e021489ca03db747 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 27 Dec 2021 11:49:22 +0800 Subject: vim-patch:8.2.3580: gj does not move properly with a wide character Problem: gj does not move properly with a wide character. Solution: Move one to the right. (Christian Brabandt, closes vim/vim#8702) https://github.com/vim/vim/commit/aaec1d4fb12efb82b87ad322e95994de77b1a833 --- src/nvim/normal.c | 9 ++++++++- src/nvim/testdir/test_normal.vim | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2b5b47c0b3..ec01f4e92d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3281,7 +3281,7 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) int col_off1; // margin offset for first screen line int col_off2; // margin offset for wrapped screen line int width1; // text width for first screen line - int width2; // test width for wrapped screen line + int width2; // text width for wrapped screen line oap->motion_type = kMTCharWise; oap->inclusive = (curwin->w_curswant == MAXCOL); @@ -3405,6 +3405,13 @@ static bool nv_screengo(oparg_T *oap, int dir, long dist) virtcol -= vim_strsize(get_showbreak_value(curwin)); } + int c = utf_ptr2char(get_cursor_pos_ptr()); + if (dir == FORWARD && virtcol < curwin->w_curswant + && (curwin->w_curswant <= (colnr_T)width1) + && !vim_isprintc(c) && c > 255) { + oneright(); + } + if (virtcol > curwin->w_curswant && (curwin->w_curswant < (colnr_T)width1 ? (curwin->w_curswant > (colnr_T)width1 / 2) diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index aff22f5d01..cb1d66cb98 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2759,4 +2759,16 @@ func Test_normal_count_after_operator() bw! endfunc +func Test_normal_gj_on_extra_wide_char() + new | 25vsp + let text='1 foooooooo ar e insâ€zwe1 foooooooo insâ€zwei' . + \ ' i drei vier fünf sechs sieben acht un zehn elf zwöfl' . + \ ' dreizehn v ierzehn fünfzehn' + put =text + call cursor(2,1) + norm! gj + call assert_equal([0,2,25,0], getpos('.')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 0a65d821fcc3fe1cab52b4102f2a55b7aa89df03 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Jan 2022 10:05:31 +0800 Subject: vim-patch:8.2.3494: illegal memory access in utf_head_off Problem: Illegal memory access in utf_head_off. Solution: Check cursor position when reselecting the Visual area. (closes vim/vim#8963) https://github.com/vim/vim/commit/b07626d4afa73dd2af0f03c0d59eed25ee159ef9 Including the XTest_beval -> XTest_block from patch 8.2.3096. --- src/nvim/normal.c | 7 ++----- src/nvim/testdir/test_visual.vim | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2b5b47c0b3..418ba133bd 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5963,11 +5963,8 @@ static void nv_visual(cmdarg_T *cap) * was only one -- webb */ if (resel_VIsual_mode != 'v' || resel_VIsual_line_count > 1) { - curwin->w_cursor.lnum += - resel_VIsual_line_count * cap->count0 - 1; - if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; - } + curwin->w_cursor.lnum += resel_VIsual_line_count * cap->count0 - 1; + check_cursor(); } VIsual_mode = resel_VIsual_mode; if (VIsual_mode == 'v') { diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index d58ca92a2f..2e26da4947 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1120,7 +1120,27 @@ func Test_visual_block_with_virtualedit() " clean up call term_sendkeys(buf, "\") call StopVimInTerminal(buf) - call delete('XTest_beval') + call delete('XTest_block') +endfunc + +func Test_visual_reselect_with_count() + " this was causing an illegal memory access + let lines =<< trim END + + + + : + r + exe "%norm e3\kr\t" + : + + : + END + call writefile(lines, 'XvisualReselect') + source XvisualReselect + + bwipe! + call delete('XvisualReselect') endfunc -- cgit From ec39e1e421101d2573ca5f9003238adfcd45dcd1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Jan 2022 10:05:31 +0800 Subject: vim-patch:8.2.3611: crash when using CTRL-W f without finding a file name Problem: Crash when using CTRL-W f without finding a file name. Solution: Bail out when the file name length is zero. https://github.com/vim/vim/commit/615ddd5342b50a6878a907062aa471740bd9a847 --- src/nvim/file_search.c | 4 ++++ src/nvim/normal.c | 9 +++++++-- src/nvim/path.c | 4 ++++ src/nvim/testdir/test_visual.vim | 8 ++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b2cd5c510b..df48370ce1 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1433,6 +1433,10 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first rel_fname = NULL; } + if (len == 0) { + return NULL; + } + if (first == TRUE) { // copy file name into NameBuff, expanding environment variables save_char = ptr[len]; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 418ba133bd..1f528ce98a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4472,8 +4472,13 @@ bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp) *pp = ml_get_pos(&VIsual); *lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1; } - // Correct the length to include the whole last character. - *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1); + if (**pp == NUL) { + *lenp = 0; + } + if (*lenp > 0) { + // Correct the length to include all bytes of the last character. + *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1); + } } reset_VIsual_and_resel(); return true; diff --git a/src/nvim/path.c b/src/nvim/path.c index 674d67e21a..8b110d0ded 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1682,6 +1682,10 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count, char_u *file_name; char_u *tofree = NULL; + if (len == 0) { + return NULL; + } + if ((options & FNAME_INCL) && *curbuf->b_p_inex != NUL) { tofree = (char_u *)eval_includeexpr((char *)ptr, len); if (tofree != NULL) { diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 2e26da4947..e1e7765e06 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1123,6 +1123,14 @@ func Test_visual_block_with_virtualedit() call delete('XTest_block') endfunc +func Test_visual_block_ctrl_w_f() + " Emtpy block selected in new buffer should not result in an error. + au! BufNew foo sil norm f + edit foo + + au! BufNew +endfunc + func Test_visual_reselect_with_count() " this was causing an illegal memory access let lines =<< trim END -- cgit From dda1c8edda86a50d802f777e5ba2379f7ccd6ae8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 18 Jan 2022 10:05:31 +0800 Subject: vim-patch:8.2.3613: :find test fails Problem: :find test fails. Solution: Put length check inside if block. https://github.com/vim/vim/commit/e015d99abb4276f47ce97bad1ad5ff0c658b1c8a --- src/nvim/file_search.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index df48370ce1..b4becb3066 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1433,11 +1433,11 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first rel_fname = NULL; } - if (len == 0) { - return NULL; - } + if (first == true) { + if (len == 0) { + return NULL; + } - if (first == TRUE) { // copy file name into NameBuff, expanding environment variables save_char = ptr[len]; ptr[len] = NUL; -- cgit From 5e81687d9a4fb5d6fe734756b086aae47d64f5a4 Mon Sep 17 00:00:00 2001 From: f380cedric Date: Thu, 6 Jan 2022 10:54:33 +0100 Subject: vim-patch:8.2.3402: invalid memory access when using :retab with large value Problem: Invalid memory access when using :retab with large value. Solution: Check the number is positive. https://github.com/vim/vim/commit/b7081e135a16091c93f6f5f7525a5c58fb7ca9f9 --- src/nvim/option.c | 25 ++++++++++++++++--------- src/nvim/testdir/test_retab.vim | 3 +++ 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 659965b64c..9471974d2b 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1993,9 +1993,9 @@ static void didset_options2(void) // Parse default for 'wildmode'. check_opt_wim(); xfree(curbuf->b_p_vsts_array); - tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); + (void)tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); xfree(curbuf->b_p_vts_array); - tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); + (void)tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); } /// Check for string options that are NULL (normally only termcap options). @@ -6368,7 +6368,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_sts_nopaste = p_sts_nopaste; buf->b_p_vsts = vim_strsave(p_vsts); if (p_vsts && p_vsts != empty_option) { - tabstop_set(p_vsts, &buf->b_p_vsts_array); + (void)tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = 0; } @@ -6448,7 +6448,7 @@ void buf_copy_options(buf_T *buf, int flags) if (dont_do_help) { buf->b_p_isk = save_p_isk; if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) { - tabstop_set(p_vts, &buf->b_p_vts_array); + (void)tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -6458,7 +6458,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_ts = p_ts; buf->b_p_vts = vim_strsave(p_vts); if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) { - tabstop_set(p_vts, &buf->b_p_vts_array); + (void)tabstop_set(p_vts, &buf->b_p_vts_array); } else { buf->b_p_vts_array = NULL; } @@ -7151,7 +7151,7 @@ static void paste_option_changed(void) xfree(buf->b_p_vsts_array); } if (buf->b_p_vsts && buf->b_p_vsts != empty_option) { - tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); + (void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); } else { buf->b_p_vsts_array = 0; } @@ -7469,6 +7469,7 @@ int check_ff_value(char_u *p) // Set the integer values corresponding to the string setting of 'vartabstop'. // "array" will be set, caller must free it if needed. +// Return false for an error. bool tabstop_set(char_u *var, long **array) { long valcount = 1; @@ -7488,7 +7489,7 @@ bool tabstop_set(char_u *var, long **array) if (cp != end) { emsg(_(e_positive)); } else { - emsg(_(e_invarg)); + semsg(_(e_invarg2), cp); } return false; } @@ -7501,7 +7502,7 @@ bool tabstop_set(char_u *var, long **array) valcount++; continue; } - emsg(_(e_invarg)); + semsg(_(e_invarg2), var); return false; } @@ -7510,7 +7511,13 @@ bool tabstop_set(char_u *var, long **array) t = 1; for (cp = var; *cp != NUL;) { - (*array)[t++] = atoi((char *)cp); + int n = atoi((char *)cp); + + if (n < 0 || n > 9999) { + semsg(_(e_invarg2), cp); + return false; + } + (*array)[t++] = n; while (*cp != NUL && *cp != ',') { cp++; } diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim index f11a32bade..e7b8946ccf 100644 --- a/src/nvim/testdir/test_retab.vim +++ b/src/nvim/testdir/test_retab.vim @@ -74,4 +74,7 @@ endfunc func Test_retab_error() call assert_fails('retab -1', 'E487:') call assert_fails('retab! -1', 'E487:') + call assert_fails('ret -1000', 'E487:') + call assert_fails('ret 10000', 'E475:') + call assert_fails('ret 80000000000000000000', 'E475:') endfunc -- cgit From 1ae73e2d1c328a6181f4ebaebfc273e1d1c8d59d Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 18 Jan 2022 12:22:54 +0000 Subject: vim-patch:8.2.0175: crash when removing list element in map() Problem: Crash when removing list element in map(). Solution: Lock the list. (closes vim/vim#2652) https://github.com/vim/vim/commit/db661fb95dc41b7a9438cf3cd4e77f8410bc81c0 --- src/nvim/eval.c | 10 ++++++++++ src/nvim/testdir/test_filter_map.vim | 10 ++++++++++ 2 files changed, 20 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6dbdc09c3b..d25903c12a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6468,6 +6468,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_DICT) { vimvars[VV_KEY].vv_type = VAR_STRING; + const VarLockStatus prev_lock = d->dv_lock; + if (map && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } ht = &d->dv_hashtab; hash_lock(ht); todo = (int)ht->ht_used; @@ -6498,6 +6502,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } } hash_unlock(ht); + d->dv_lock = prev_lock; } else if (argvars[0].v_type == VAR_BLOB) { vimvars[VV_KEY].vv_type = VAR_NUMBER; @@ -6530,6 +6535,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) assert(argvars[0].v_type == VAR_LIST); vimvars[VV_KEY].vv_type = VAR_NUMBER; + const VarLockStatus prev_lock = tv_list_locked(l); + if (map && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, @@ -6548,6 +6557,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } idx++; } + tv_list_set_lock(l, prev_lock); } restore_vimvar(VV_KEY, &save_key); diff --git a/src/nvim/testdir/test_filter_map.vim b/src/nvim/testdir/test_filter_map.vim index a52a66ac2f..1cd3a2287b 100644 --- a/src/nvim/testdir/test_filter_map.vim +++ b/src/nvim/testdir/test_filter_map.vim @@ -88,4 +88,14 @@ func Test_map_filter_fails() call assert_fails("let l = filter('abc', '\"> \" . v:val')", 'E896:') endfunc +func Test_map_and_modify() + let l = ["abc"] + " cannot change the list halfway a map() + call assert_fails('call map(l, "remove(l, 0)[0]")', 'E741:') + + let d = #{a: 1, b: 2, c: 3} + call assert_fails('call map(d, "remove(d, v:key)[0]")', 'E741:') + call assert_fails('echo map(d, {k,v -> remove(d, k)})', 'E741:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From de6f9233eee937139f607d3a59fd5b1f479d8a13 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 18 Jan 2022 12:46:41 -0700 Subject: refactor: source ftplugin.vim separately from filetype.vim (#17129) This is a follow-on to #17040. The real benefit of #17040 was ensuring that the ftplugin FileType autocommand was defined first and thus always fired first. A side effect of the implementation in #17040 was that setting variables that modified the state of filetype detection (such as g:did_load_filetypes or g:do_filetype_lua) could no longer be set in the user's init file. Filetype detection can also no longer be prevented from loading by using `:filetype off`. This PR addresses both of those side effects by unconditionally sourcing ftplugin.vim and indent.vim before the user's init file (which ensures that these autocommands run first) and sourcing filetype.vim *after* the user's init file (thus allowing it to be blocked or modified). --- src/nvim/ex_docmd.c | 26 +++++++++++++++++--------- src/nvim/main.c | 7 +++++-- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3a285cdf90..b33a17c631 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9546,16 +9546,12 @@ static void ex_filetype(exarg_T *eap) } } -/// Set all :filetype options ON if user did not explicitly set any to OFF. -void filetype_maybe_enable(void) +/// Source ftplugin.vim and indent.vim to create the necessary FileType +/// autocommands. We do this separately from filetype.vim so that these +/// autocommands will always fire first (and thus can be overriden) while still +/// allowing general filetype detection to be disabled in the user's init file. +void filetype_plugin_enable(void) { - if (filetype_detect == kNone) { - // Normally .vim files are sourced before .lua files when both are - // supported, but we reverse the order here because we want the Lua - // autocommand to be defined first so that it runs first - source_runtime(FILETYPE_FILE, DIP_ALL); - filetype_detect = kTrue; - } if (filetype_plugin == kNone) { source_runtime(FTPLUGIN_FILE, DIP_ALL); filetype_plugin = kTrue; @@ -9566,6 +9562,18 @@ void filetype_maybe_enable(void) } } +/// Enable filetype detection if the user did not explicitly disable it. +void filetype_maybe_enable(void) +{ + if (filetype_detect == kNone) { + // Normally .vim files are sourced before .lua files when both are + // supported, but we reverse the order here because we want the Lua + // autocommand to be defined first so that it runs first + source_runtime(FILETYPE_FILE, DIP_ALL); + filetype_detect = kTrue; + } +} + /// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { diff --git a/src/nvim/main.c b/src/nvim/main.c index 9d1c2ad834..748f5098fd 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -354,10 +354,10 @@ int main(int argc, char **argv) exe_pre_commands(¶ms); if (!vimrc_none) { - // Does ":filetype plugin indent on". We do this *before* the user startup scripts to ensure + // Sources ftplugin.vim and indent.vim. We do this *before* the user startup scripts to ensure // ftplugins run before FileType autocommands defined in the init file (which allows those // autocommands to overwrite settings from ftplugins). - filetype_maybe_enable(); + filetype_plugin_enable(); } // Source startup scripts. @@ -365,6 +365,9 @@ int main(int argc, char **argv) // If using the runtime (-u is not NONE), enable syntax & filetype plugins. if (!vimrc_none) { + // Sources filetype.lua and filetype.vim unless the user explicitly disabled it with :filetype + // off. + filetype_maybe_enable(); // Sources syntax/syntax.vim. We do this *after* the user startup scripts so that users can // disable syntax highlighting with `:syntax off` if they wish. syn_maybe_enable(); -- cgit From 0c541ab1f661f17aef317232483d1464ad13ef36 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 19 Jan 2022 21:42:10 -0500 Subject: refactor(coverity/345582): assert fp is non-NULL Since we already have a typval, we know the lookup will succeed. --- src/nvim/api/private/converter.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index e370c0d4d4..3d4ff202fe 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -233,6 +233,7 @@ Object vim_to_object(typval_T *obj) { if (obj->v_type == VAR_FUNC) { ufunc_T *fp = find_func(obj->vval.v_string); + assert(fp != NULL); if (fp->uf_cb == nlua_CFunction_func_call) { LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); return LUAREF_OBJ(ref); -- cgit From 8f241e535fe845814a195bc5811dac4869a26998 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 19 Jan 2022 21:43:06 -0500 Subject: refactor(coverity/345583): assert fp is non-NULL Since we already have a typval, we know the lookup will succeed. --- src/nvim/lua/converter.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index f9a2533d4e..0fbd56ed53 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -619,6 +619,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) } if (tv->v_type == VAR_FUNC) { ufunc_T *fp = find_func(tv->vval.v_string); + assert(fp != NULL); if (fp->uf_cb == nlua_CFunction_func_call) { nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); return true; -- cgit From e850a929864508864ee52abcbac9579a6a2d2f28 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 19 Jan 2022 21:53:49 -0500 Subject: fix(coverity/340720): error if nvim_eval_statusline given invalid winid --- src/nvim/api/vim.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59db12f2c0..88a3577de3 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2281,6 +2281,11 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * fillchar = ' '; } else { wp = find_window_by_handle(window, err); + + if (wp == NULL) { + api_set_error(err, kErrorTypeException, "unknown winid %d", window); + return result; + } ewp = wp; if (fillchar == 0) { -- cgit From d224957d30654dfa7fac7732b81f6a1b495a418b Mon Sep 17 00:00:00 2001 From: James McCoy Date: Wed, 19 Jan 2022 22:07:37 -0500 Subject: fix(coverity/188749): nullify pointer to fix use-after-free --- src/nvim/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abd22fba26..0248d42f58 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4351,7 +4351,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use // Only free the string buffer if we allocated it. // Note: This is not needed if `str` is pointing at `tmp` if (opt == STL_VIM_EXPR) { - xfree(str); + XFREE_CLEAR(str); } if (num >= 0 || (!itemisflag && str && *str)) { -- cgit From 5971b863383160d9bf744a9789c1fe5ca62b55a4 Mon Sep 17 00:00:00 2001 From: notomo Date: Thu, 13 Jan 2022 23:26:57 +0900 Subject: feat(api): expose extmark more details --- src/nvim/api/extmark.c | 35 +++++++++++++++++++++++++++++++++++ src/nvim/decoration.h | 4 ++++ 2 files changed, 39 insertions(+) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 370f7fb47e..80bd88c4ee 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -115,7 +115,12 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) if (decor->hl_id) { String name = cstr_to_string((const char *)syn_id2name(decor->hl_id)); PUT(dict, "hl_group", STRING_OBJ(name)); + PUT(dict, "hl_eol", BOOLEAN_OBJ(decor->hl_eol)); } + if (decor->hl_mode) { + PUT(dict, "hl_mode", STRING_OBJ(cstr_to_string(hl_mode_str[decor->hl_mode]))); + } + if (kv_size(decor->virt_text)) { Array chunks = ARRAY_DICT_INIT; for (size_t i = 0; i < decor->virt_text.size; i++) { @@ -129,6 +134,36 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) ADD(chunks, ARRAY_OBJ(chunk)); } PUT(dict, "virt_text", ARRAY_OBJ(chunks)); + PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide)); + if (decor->virt_text_pos == kVTWinCol) { + PUT(dict, "virt_text_win_col", INTEGER_OBJ(decor->col)); + } + PUT(dict, "virt_text_pos", + STRING_OBJ(cstr_to_string(virt_text_pos_str[decor->virt_text_pos]))); + } + + if (kv_size(decor->virt_lines)) { + Array all_chunks = ARRAY_DICT_INIT; + bool virt_lines_leftcol = false; + for (size_t i = 0; i < decor->virt_lines.size; i++) { + Array chunks = ARRAY_DICT_INIT; + VirtText *vt = &decor->virt_lines.items[i].line; + virt_lines_leftcol = decor->virt_lines.items[i].left_col; + for (size_t j = 0; j < vt->size; j++) { + Array chunk = ARRAY_DICT_INIT; + VirtTextChunk *vtc = &vt->items[j]; + ADD(chunk, STRING_OBJ(cstr_to_string(vtc->text))); + if (vtc->hl_id > 0) { + ADD(chunk, + STRING_OBJ(cstr_to_string((const char *)syn_id2name(vtc->hl_id)))); + } + ADD(chunks, ARRAY_OBJ(chunk)); + } + ADD(all_chunks, ARRAY_OBJ(chunks)); + } + PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks)); + PUT(dict, "virt_lines_above", BOOLEAN_OBJ(decor->virt_lines_above)); + PUT(dict, "virt_lines_leftcol", BOOLEAN_OBJ(virt_lines_leftcol)); } if (decor->hl_id || kv_size(decor->virt_text)) { diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 209e2176f2..02472d09e4 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -17,6 +17,8 @@ typedef enum { kVTRightAlign, } VirtTextPos; +EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" }); + typedef enum { kHlModeUnknown, kHlModeReplace, @@ -24,6 +26,8 @@ typedef enum { kHlModeBlend, } HlMode; +EXTERN const char *const hl_mode_str[] INIT(= { "", "replace", "combine", "blend" }); + typedef kvec_t(VirtTextChunk) VirtText; #define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) -- cgit From aa4eadd2bee7d389edc6284078bb43c700de5b0f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 20 Jan 2022 14:34:24 +0800 Subject: vim-patch:8.2.0128: cannot list options one per line Problem: Cannot list options one per line. Solution: Use ":set!" to list one option per line. https://github.com/vim/vim/commit/6b915c0c0ee7ef82f8d3d310a4345e098cb929b0 --- src/nvim/ex_cmds.lua | 6 +++--- src/nvim/ex_docmd.c | 12 ------------ src/nvim/option.c | 28 +++++++++++++++++++++++----- src/nvim/option.h | 20 ++++++++++---------- src/nvim/testdir/test_options.vim | 11 +++++++++-- 5 files changed, 45 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index c388373ac1..c391cf96aa 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2413,7 +2413,7 @@ module.cmds = { }, { command='set', - flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), + flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK), addr_type='ADDR_NONE', func='ex_set', }, @@ -2425,13 +2425,13 @@ module.cmds = { }, { command='setglobal', - flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), + flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK), addr_type='ADDR_NONE', func='ex_set', }, { command='setlocal', - flags=bit.bor(TRLBAR, EXTRA, CMDWIN, SBOXOK), + flags=bit.bor(BANG, TRLBAR, EXTRA, CMDWIN, SBOXOK), addr_type='ADDR_NONE', func='ex_set', }, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index b33a17c631..a784aaf5f7 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9600,18 +9600,6 @@ static void ex_digraphs(exarg_T *eap) } } -static void ex_set(exarg_T *eap) -{ - int flags = 0; - - if (eap->cmdidx == CMD_setlocal) { - flags = OPT_LOCAL; - } else if (eap->cmdidx == CMD_setglobal) { - flags = OPT_GLOBAL; - } - (void)do_set(eap->arg, flags); -} - void set_no_hlsearch(bool flag) { no_hlsearch = flag; diff --git a/src/nvim/option.c b/src/nvim/option.c index 659965b64c..37cf2aa5b1 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -281,7 +281,7 @@ typedef struct vimoption { # include "options.generated.h" #endif -#define PARAM_COUNT ARRAY_SIZE(options) +#define OPTION_COUNT ARRAY_SIZE(options) static char *(p_ambw_values[]) = { "single", "double", NULL }; static char *(p_bg_values[]) = { "light", "dark", NULL }; @@ -931,6 +931,21 @@ void set_title_defaults(void) } } +void ex_set(exarg_T *eap) +{ + int flags = 0; + + if (eap->cmdidx == CMD_setlocal) { + flags = OPT_LOCAL; + } else if (eap->cmdidx == CMD_setglobal) { + flags = OPT_GLOBAL; + } + if (eap->forceit) { + flags |= OPT_ONECOLUMN; + } + (void)do_set(eap->arg, flags); +} + /// Parse 'arg' for option settings. /// /// 'arg' may be IObuff, but only when no errors can be present and option @@ -5184,7 +5199,7 @@ static void showoptions(int all, int opt_flags) #define INC 20 #define GAP 3 - vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT); + vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT); // Highlight title if (opt_flags & OPT_GLOBAL) { @@ -5198,6 +5213,7 @@ static void showoptions(int all, int opt_flags) // Do the loop two times: // 1. display the short items // 2. display the long items (only strings and numbers) + // When "opt_flags" has OPT_ONECOLUMN do everything in run 2. for (run = 1; run <= 2 && !got_int; run++) { // collect the items in items[] item_count = 0; @@ -5208,7 +5224,7 @@ static void showoptions(int all, int opt_flags) } varp = NULL; - if (opt_flags != 0) { + if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) { if (p->indir != PV_NONE) { varp = get_varp_scope(p, opt_flags); } @@ -5217,8 +5233,10 @@ static void showoptions(int all, int opt_flags) } if (varp != NULL && (all == 1 || (all == 0 && !optval_default(p, varp)))) { - if (p->flags & P_BOOL) { - len = 1; // a toggle option fits always + if (opt_flags & OPT_ONECOLUMN) { + len = Columns; + } else if (p->flags & P_BOOL) { + len = 1; // a toggle option fits always } else { option_value2string(p, opt_flags); len = (int)STRLEN(p->fullname) + vim_strsize(NameBuff) + 1; diff --git a/src/nvim/option.h b/src/nvim/option.h index f7dbaafeec..9321dd5454 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -13,16 +13,16 @@ /// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global /// values, get local value. typedef enum { - OPT_FREE = 1, ///< Free old value if it was allocated. - OPT_GLOBAL = 2, ///< Use global value. - OPT_LOCAL = 4, ///< Use local value. - OPT_MODELINE = 8, ///< Option in modeline. - OPT_WINONLY = 16, ///< Only set window-local options. - OPT_NOWIN = 32, ///< Don’t set window-local options. - OPT_ONECOLUMN = 64, ///< list options one per line - OPT_NO_REDRAW = 128, ///< ignore redraw flags on option - OPT_SKIPRTP = 256, ///< "skiprtp" in 'sessionoptions' - OPT_CLEAR = 512, ///< Clear local value of an option. + OPT_FREE = 0x01, ///< Free old value if it was allocated. + OPT_GLOBAL = 0x02, ///< Use global value. + OPT_LOCAL = 0x04, ///< Use local value. + OPT_MODELINE = 0x08, ///< Option in modeline. + OPT_WINONLY = 0x10, ///< Only set window-local options. + OPT_NOWIN = 0x20, ///< Don’t set window-local options. + OPT_ONECOLUMN = 0x40, ///< list options one per line + OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option + OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions' + OPT_CLEAR = 0x200, ///< Clear local value of an option. } OptionFlags; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 5946732937..2312df5450 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -51,7 +51,7 @@ func Test_wildoptions() call assert_equal('tagfile', &wildoptions) endfunc -function! Test_options() +func Test_options_command() let caught = 'ok' try options @@ -88,7 +88,7 @@ function! Test_options() " close option-window close -endfunction +endfunc function! Test_path_keep_commas() " Test that changing 'path' keeps two commas. @@ -368,6 +368,13 @@ func Test_set_all() set tw& iskeyword& splitbelow& endfunc +func Test_set_one_column() + let out_mult = execute('set all')->split("\n") + let out_one = execute('set! all')->split("\n") + " one column should be two to four times as many lines + call assert_inrange(len(out_mult) * 2, len(out_mult) * 4, len(out_one)) +endfunc + func Test_set_values() " The file is only generated when running "make test" in the src directory. if filereadable('opt_test.vim') -- cgit From 2fa1b4cbff06b6dabc6d59df585953e9220e5007 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 05:14:40 +0800 Subject: vim-patch:8.2.4166: undo synced when switching buffer in another window Problem: Undo synced when switching buffer in another window. Solution: Do not sync undo when not needed. (closes vim/vim#9575) https://github.com/vim/vim/commit/e615db06046312e74886fa1ef98feb5a9db2a7c3 --- src/nvim/buffer.c | 5 ++++- src/nvim/testdir/test_timers.vim | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index abd22fba26..83c95da4f7 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1454,7 +1454,10 @@ void set_curbuf(buf_T *buf, int action) } if (bufref_valid(&prevbufref) && !aborting()) { win_T *previouswin = curwin; - if (prevbuf == curbuf) { + // Do not sync when in Insert mode and the buffer is open in + // another window, might be a timer doing something in another + // window. + if (prevbuf == curbuf && ((State & INSERT) == 0 || curbuf->b_nwindows <= 1)) { u_sync(false); } close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index aae315b2c5..09eed4e10d 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -379,4 +379,27 @@ func Test_timer_invalid_callback() call assert_fails('call timer_start(0, "0")', 'E921') endfunc +func Test_timer_using_win_execute_undo_sync() + let bufnr1 = bufnr() + new + let g:bufnr2 = bufnr() + let g:winid = win_getid() + exe "buffer " .. bufnr1 + wincmd w + call setline(1, ['test']) + autocmd InsertEnter * call timer_start(100, { -> win_execute(g:winid, 'buffer ' .. g:bufnr2) }) + call timer_start(200, { -> feedkeys("\bbbb\") }) + call feedkeys("Oaaaa", 'x!t') + " will hang here until the second timer fires + call assert_equal(['aaaa', 'bbbb', 'test'], getline(1, '$')) + undo + call assert_equal(['test'], getline(1, '$')) + + bwipe! + bwipe! + unlet g:winid + unlet g:bufnr2 + au! InsertEnter +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From ff7c3d12750b1cd4baccea0baeafe7e5d5339e94 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:08:56 +0800 Subject: fix(input): never escape CSI bytes --- src/nvim/getchar.c | 7 +++---- src/nvim/os/input.c | 14 +++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5565e17597..bddf2e21d2 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1010,12 +1010,11 @@ void ins_char_typebuf(int c) buf[utf_char2bytes(c, buf)] = NUL; char_u *p = buf; while (*p) { - if ((uint8_t)(*p) == CSI || (uint8_t)(*p) == K_SPECIAL) { - bool is_csi = (uint8_t)(*p) == CSI; + if ((uint8_t)(*p) == K_SPECIAL) { memmove(p + 3, p + 1, STRLEN(p + 1) + 1); *p++ = K_SPECIAL; - *p++ = is_csi ? KS_EXTRA : KS_SPECIAL; - *p++ = is_csi ? KE_CSI : KE_FILLER; + *p++ = KS_SPECIAL; + *p++ = KE_FILLER; } else { p++; } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 3790eba212..54cfaee80a 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -234,9 +234,9 @@ size_t input_enqueue(String keys) while (rbuffer_space(input_buffer) >= 19 && ptr < end) { // A "" form occupies at least 1 characters, and produces up // to 19 characters (1 + 5 * 3 for the char and 3 for a modifier). - // In the case of K_SPECIAL(0x80) or CSI(0x9B), 3 bytes are escaped and - // needed, but since the keys are UTF-8, so the first byte cannot be - // K_SPECIAL(0x80) or CSI(0x9B). + // In the case of K_SPECIAL(0x80), 3 bytes are escaped and needed, + // but since the keys are UTF-8, so the first byte cannot be + // K_SPECIAL(0x80). uint8_t buf[19] = { 0 }; unsigned int new_size = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, @@ -263,12 +263,8 @@ size_t input_enqueue(String keys) continue; } - // copy the character, escaping CSI and K_SPECIAL - if ((uint8_t)*ptr == CSI) { - rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1); - rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_EXTRA }, 1); - rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_CSI }, 1); - } else if ((uint8_t)*ptr == K_SPECIAL) { + // copy the character, escaping K_SPECIAL + if ((uint8_t)(*ptr) == K_SPECIAL) { rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1); rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_SPECIAL }, 1); rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_FILLER }, 1); -- cgit From 8300d337c89dc3ef943694bc1d44f32145a74f0d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:08:56 +0800 Subject: refactor: remove the key CSI typed directly is now just . The key is obsolete. --- src/nvim/keymap.c | 1 - src/nvim/keymap.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index abf016b832..f8ccb79ade 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -158,7 +158,6 @@ static const struct key_name_entry { { ESC, "Esc" }, { ESC, "Escape" }, // Alternative name { CSI, "CSI" }, - { K_CSI, "xCSI" }, { '|', "Bar" }, { '\\', "Bslash" }, { K_DEL, "Del" }, diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 5a74d1dc00..ec18dc64b5 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -220,7 +220,7 @@ enum key_extra { KE_KINS = 79, // keypad Insert key KE_KDEL = 80, // keypad Delete key - KE_CSI = 81, // CSI typed directly + KE_CSI = 81, // CSI typed directly (no longer produced by Nvim) KE_SNR = 82, // KE_PLUG = 83, // KE_CMDWIN = 84, // open command-line window from Command-line Mode @@ -435,7 +435,6 @@ enum key_extra { #define K_MOUSELEFT TERMCAP2KEY(KS_EXTRA, KE_MOUSELEFT) #define K_MOUSERIGHT TERMCAP2KEY(KS_EXTRA, KE_MOUSERIGHT) -#define K_CSI TERMCAP2KEY(KS_EXTRA, KE_CSI) #define K_SNR TERMCAP2KEY(KS_EXTRA, KE_SNR) #define K_PLUG TERMCAP2KEY(KS_EXTRA, KE_PLUG) #define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN) -- cgit From 6e69a3c3e79fd78b31753343213e68e73b0048c4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:08:56 +0800 Subject: refactor: remove CSI unescaping and clean up related names and comments --- src/nvim/api/vim.c | 20 +++--- src/nvim/edit.c | 6 +- src/nvim/ex_docmd.c | 6 +- src/nvim/getchar.c | 197 +++++++++++++++++++++------------------------------- src/nvim/input.c | 2 +- src/nvim/keymap.c | 1 - src/nvim/keymap.h | 2 +- src/nvim/mbyte.c | 8 +-- src/nvim/ops.c | 12 ++-- 9 files changed, 103 insertions(+), 151 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59db12f2c0..cc622a00dc 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -187,21 +187,23 @@ static void on_redraw_event(void **argv) /// On execution error: does not fail, but updates v:errmsg. /// /// To input sequences like use |nvim_replace_termcodes()| (typically -/// with escape_csi=true) to replace |keycodes|, then pass the result to +/// with escape_ks=false) to replace |keycodes|, then pass the result to /// nvim_feedkeys(). /// /// Example: ///
 ///     :let key = nvim_replace_termcodes("", v:true, v:false, v:true)
-///     :call nvim_feedkeys(key, 'n', v:true)
+///     :call nvim_feedkeys(key, 'n', v:false)
 /// 
/// /// @param keys to be typed /// @param mode behavior flags, see |feedkeys()| -/// @param escape_csi If true, escape K_SPECIAL/CSI bytes in `keys` +/// @param escape_ks If true, escape K_SPECIAL bytes in `keys` +/// This should be false if you already used +/// |nvim_replace_termcodes()|, and true otherwise. /// @see feedkeys() -/// @see vim_strsave_escape_csi -void nvim_feedkeys(String keys, String mode, Boolean escape_csi) +/// @see vim_strsave_escape_ks +void nvim_feedkeys(String keys, String mode, Boolean escape_ks) FUNC_API_SINCE(1) { bool remap = true; @@ -232,10 +234,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) } char *keys_esc; - if (escape_csi) { - // Need to escape K_SPECIAL and CSI before putting the string in the + if (escape_ks) { + // Need to escape K_SPECIAL before putting the string in the // typeahead buffer. - keys_esc = (char *)vim_strsave_escape_csi((char_u *)keys.data); + keys_esc = (char *)vim_strsave_escape_ks((char_u *)keys.data); } else { keys_esc = keys.data; } @@ -245,7 +247,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) typebuf_was_filled = true; } - if (escape_csi) { + if (escape_ks) { xfree(keys_esc); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 9efe5a27c4..a9c606ec13 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -260,7 +260,7 @@ static colnr_T Insstart_blank_vcol; // vcol for first inserted blank static bool update_Insstart_orig = true; // set Insstart_orig to Insstart static char_u *last_insert = NULL; // the text of the previous insert, - // K_SPECIAL and CSI are escaped + // K_SPECIAL is escaped static int last_insert_skip; // nr of chars in front of previous insert static int new_insert_skip; // nr of chars in front of current insert static int did_restart_edit; // "restart_edit" when calling edit() @@ -6817,7 +6817,7 @@ void free_last_insert(void) /// Add character "c" to buffer "s" /// -/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte +/// Escapes the special meaning of K_SPECIAL, handles multi-byte /// characters. /// /// @param[in] c Character to add. @@ -6831,7 +6831,7 @@ char_u *add_char2buf(int c, char_u *s) const int len = utf_char2bytes(c, temp); for (int i = 0; i < len; i++) { c = temp[i]; - // Need to escape K_SPECIAL and CSI like in the typeahead buffer. + // Need to escape K_SPECIAL like in the typeahead buffer. if (c == K_SPECIAL) { *s++ = K_SPECIAL; *s++ = KS_SPECIAL; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index b33a17c631..54d8c93b47 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6204,7 +6204,6 @@ static void do_ucmd(exarg_T *eap) // K_SPECIAL has been put in the buffer as K_SPECIAL // KS_SPECIAL KE_FILLER, like for mappings, but // do_cmdline() doesn't handle that, so convert it back. - // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. len = ksp - p; if (len > 0) { memmove(q, p, len); @@ -8627,7 +8626,7 @@ static void ex_normal(exarg_T *eap) return; } - // vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do + // vgetc() expects K_SPECIAL to have been escaped. Don't do // this for the K_SPECIAL leading byte, otherwise special keys will not // work. { @@ -8636,8 +8635,7 @@ static void ex_normal(exarg_T *eap) // Count the number of characters to be escaped. for (p = eap->arg; *p != NUL; p++) { for (l = utfc_ptr2len(p) - 1; l > 0; l--) { - if (*++p == K_SPECIAL // trailbyte K_SPECIAL or CSI - ) { + if (*++p == K_SPECIAL) { // trailbyte K_SPECIAL len += 2; } } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index bddf2e21d2..f2b0b9a8a9 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -59,25 +59,18 @@ static int curscript = 0; FileDescriptor *scriptin[NSCRIPT] = { NULL }; -/* - * These buffers are used for storing: - * - stuffed characters: A command that is translated into another command. - * - redo characters: will redo the last change. - * - recorded characters: for the "q" command. - * - * The bytes are stored like in the typeahead buffer: - * - K_SPECIAL introduces a special key (two more bytes follow). A literal - * K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. - * - CSI introduces a GUI termcap code (also when gui.in_use is FALSE, - * otherwise switching the GUI on would make mappings invalid). - * A literal CSI is stored as CSI KS_EXTRA KE_CSI. - * These translations are also done on multi-byte characters! - * - * Escaping CSI bytes is done by the system-specific input functions, called - * by ui_inchar(). - * Escaping K_SPECIAL is done by inchar(). - * Un-escaping is done by vgetc(). - */ +// These buffers are used for storing: +// - stuffed characters: A command that is translated into another command. +// - redo characters: will redo the last change. +// - recorded characters: for the "q" command. +// +// The bytes are stored like in the typeahead buffer: +// - K_SPECIAL introduces a special key (two more bytes follow). A literal +// K_SPECIAL is stored as K_SPECIAL KS_SPECIAL KE_FILLER. +// These translations are also done on multi-byte characters! +// +// Escaping K_SPECIAL is done by inchar(). +// Un-escaping is done by vgetc(). #define MINIMAL_SIZE 20 // minimal size for b_str @@ -173,7 +166,7 @@ void free_buff(buffheader_T *buf) } /// Return the contents of a buffer as a single string. -/// K_SPECIAL and CSI in the returned string are escaped. +/// K_SPECIAL in the returned string is escaped. /// /// @param dozero count == zero is not an error static char_u *get_buffcont(buffheader_T *buffer, int dozero) @@ -202,11 +195,9 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero) return p; } -/* - * Return the contents of the record buffer as a single string - * and clear the record buffer. - * K_SPECIAL and CSI in the returned string are escaped. - */ +/// Return the contents of the record buffer as a single string +/// and clear the record buffer. +/// K_SPECIAL in the returned string is escaped. char_u *get_recorded(void) { char_u *p; @@ -236,10 +227,8 @@ char_u *get_recorded(void) return p; } -/* - * Return the contents of the redo buffer as a single string. - * K_SPECIAL and CSI in the returned string are escaped. - */ +/// Return the contents of the redo buffer as a single string. +/// K_SPECIAL in the returned string is escaped. char_u *get_inserted(void) { return get_buffcont(&redobuff, FALSE); @@ -247,7 +236,7 @@ char_u *get_inserted(void) /// Add string after the current block of the given buffer /// -/// K_SPECIAL and CSI should have been escaped already. +/// K_SPECIAL should have been escaped already. /// /// @param[out] buf Buffer to add to. /// @param[in] s String to add. @@ -305,10 +294,8 @@ static void add_num_buff(buffheader_T *buf, long n) add_buff(buf, number, -1L); } -/* - * Add character 'c' to buffer "buf". - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// Add character 'c' to buffer "buf". +/// Translates special keys, NUL, K_SPECIAL and multibyte characters. static void add_char_buff(buffheader_T *buf, int c) { uint8_t bytes[MB_MAXBYTES + 1]; @@ -340,12 +327,10 @@ static void add_char_buff(buffheader_T *buf, int c) } } -/* - * Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 - * if that one is empty. - * If advance == TRUE go to the next char. - * No translation is done K_SPECIAL and CSI are escaped. - */ +/// Get one byte from the read buffers. Use readbuf1 one first, use readbuf2 +/// if that one is empty. +/// If advance == TRUE go to the next char. +/// No translation is done K_SPECIAL is escaped. static int read_readbuffers(int advance) { int c; @@ -524,10 +509,8 @@ void restoreRedobuff(save_redo_T *save_redo) old_redobuff = save_redo->sr_old_redobuff; } -/* - * Append "s" to the redo buffer. - * K_SPECIAL and CSI should already have been escaped. - */ +/// Append "s" to the redo buffer. +/// K_SPECIAL should already have been escaped. void AppendToRedobuff(const char *s) { if (!block_redo) { @@ -536,7 +519,7 @@ void AppendToRedobuff(const char *s) } /// Append to Redo buffer literally, escaping special characters with CTRL-V. -/// K_SPECIAL and CSI are escaped as well. +/// K_SPECIAL is escaped as well. /// /// @param str String to append /// @param len Length of `str` or -1 for up to the NUL. @@ -584,10 +567,8 @@ void AppendToRedobuffLit(const char_u *str, int len) } } -/* - * Append a character to the redo buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// Append a character to the redo buffer. +/// Translates special keys, NUL, K_SPECIAL and multibyte characters. void AppendCharToRedobuff(int c) { if (!block_redo) { @@ -605,17 +586,15 @@ void AppendNumberToRedobuff(long n) } } -/* - * Append string "s" to the stuff buffer. - * CSI and K_SPECIAL must already have been escaped. - */ +/// Append string "s" to the stuff buffer. +/// K_SPECIAL must already have been escaped. void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. -/// @remark CSI and K_SPECIAL must already have been escaped. +/// @remark K_SPECIAL must already have been escaped. void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); @@ -626,11 +605,9 @@ void stuffReadbuffLen(const char *s, long len) add_buff(&readbuf1, s, len); } -/* - * Stuff "s" into the stuff buffer, leaving special key codes unmodified and - * escaping other K_SPECIAL and CSI bytes. - * Change CR, LF and ESC into a space. - */ +/// Stuff "s" into the stuff buffer, leaving special key codes unmodified and +/// escaping other K_SPECIAL bytes. +/// Change CR, LF and ESC into a space. void stuffReadbuffSpec(const char *s) { while (*s != NUL) { @@ -648,10 +625,8 @@ void stuffReadbuffSpec(const char *s) } } -/* - * Append a character to the stuff buffer. - * Translates special keys, NUL, CSI, K_SPECIAL and multibyte characters. - */ +/// Append a character to the stuff buffer. +/// Translates special keys, NUL, K_SPECIAL and multibyte characters. void stuffcharReadbuff(int c) { add_char_buff(&readbuf1, c); @@ -665,12 +640,12 @@ void stuffnumReadbuff(long n) add_num_buff(&readbuf1, n); } -// Read a character from the redo buffer. Translates K_SPECIAL, CSI and -// multibyte characters. -// The redo buffer is left as it is. -// If init is true, prepare for redo, return FAIL if nothing to redo, OK -// otherwise. -// If old_redo is true, use old_redobuff instead of redobuff. +/// Read a character from the redo buffer. Translates K_SPECIAL and +/// multibyte characters. +/// The redo buffer is left as it is. +/// If init is true, prepare for redo, return FAIL if nothing to redo, OK +/// otherwise. +/// If old_redo is true, use old_redobuff instead of redobuff. static int read_redo(bool init, bool old_redo) { static buffblock_T *bp; @@ -724,9 +699,9 @@ static int read_redo(bool init, bool old_redo) return c; } -// Copy the rest of the redo buffer into the stuff buffer (in a slow way). -// If old_redo is true, use old_redobuff instead of redobuff. -// The escaped K_SPECIAL and CSI are copied without translation. +/// Copy the rest of the redo buffer into the stuff buffer (in a slow way). +/// If old_redo is true, use old_redobuff instead of redobuff. +/// The escaped K_SPECIAL is copied without translation. static void copy_redo(bool old_redo) { int c; @@ -1426,15 +1401,13 @@ static void updatescript(int c) } } -/* - * Get the next input character. - * Can return a special key or a multi-byte character. - * Can return NUL when called recursively, use safe_vgetc() if that's not - * wanted. - * This translates escaped K_SPECIAL and CSI bytes to a K_SPECIAL or CSI byte. - * Collects the bytes of a multibyte character into the whole character. - * Returns the modifiers in the global "mod_mask". - */ +/// Get the next input character. +/// Can return a special key or a multi-byte character. +/// Can return NUL when called recursively, use safe_vgetc() if that's not +/// wanted. +/// This translates escaped K_SPECIAL bytes to a K_SPECIAL byte. +/// Collects the bytes of a multibyte character into the whole character. +/// Returns the modifiers in the global "mod_mask". int vgetc(void) { int c, c2; @@ -1571,14 +1544,9 @@ int vgetc(void) buf[i] = (char_u)vgetorpeek(true); if (buf[i] == K_SPECIAL) { // Must be a K_SPECIAL - KS_SPECIAL - KE_FILLER sequence, - // which represents a K_SPECIAL (0x80), - // or a CSI - KS_EXTRA - KE_CSI sequence, which represents - // a CSI (0x9B), - // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. - c = vgetorpeek(true); - if (vgetorpeek(true) == KE_CSI && c == KS_EXTRA) { - buf[i] = CSI; - } + // which represents a K_SPECIAL (0x80). + (void)vgetorpeek(true); // skip KS_SPECIAL + (void)vgetorpeek(true); // skip KE_FILLER } } no_mapping--; @@ -2042,7 +2010,7 @@ void vungetc(int c) /// /// When `no_mapping` (global) is zero, checks for mappings in the current mode. /// Only returns one byte (of a multi-byte character). -/// K_SPECIAL and CSI may be escaped, need to get two more bytes then. +/// K_SPECIAL may be escaped, need to get two more bytes then. static int vgetorpeek(bool advance) { int c, c1; @@ -2572,7 +2540,7 @@ int fix_input_buffer(char_u *buf, int len) FUNC_ATTR_NONNULL_ALL { if (!using_script()) { - // Should not escape K_SPECIAL/CSI reading input from the user because vim + // Should not escape K_SPECIAL reading input from the user because vim // key codes keys are processed in input.c/input_enqueue. buf[len] = NUL; return len; @@ -2583,9 +2551,8 @@ int fix_input_buffer(char_u *buf, int len) char_u *p = buf; // Two characters are special: NUL and K_SPECIAL. - // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER + // Replace NUL by K_SPECIAL KS_ZERO KE_FILLER // Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER - // Replace CSI by K_SPECIAL KS_EXTRA KE_CSI for (i = len; --i >= 0; ++p) { if (p[0] == NUL || (p[0] == K_SPECIAL @@ -3476,10 +3443,10 @@ static void showmap(mapblock_T *mp, bool local) } else if (mp->m_str == NULL) { msg_puts_attr("", HL_ATTR(HLF_8)); } else { - // Remove escaping of CSI, because "m_str" is in a format to be used + // Remove escaping of K_SPECIAL, because "m_str" is in a format to be used // as typeahead. char_u *s = vim_strsave(mp->m_str); - vim_unescape_csi(s); + vim_unescape_ks(s); msg_outtrans_special(s, false, 0); xfree(s); } @@ -3859,9 +3826,9 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) int match; if (strchr((const char *)mp->m_keys, K_SPECIAL) != NULL) { - // Might have CSI escaped mp->m_keys. + // Might have K_SPECIAL escaped mp->m_keys. q = vim_strsave(mp->m_keys); - vim_unescape_csi(q); + vim_unescape_ks(q); qlen = (int)STRLEN(q); } // find entries with right mode and keys @@ -3907,7 +3874,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) int newlen = utf_char2bytes(c, tb + j); tb[j + newlen] = NUL; // Need to escape K_SPECIAL. - char_u *escaped = vim_strsave_escape_csi(tb + j); + char_u *escaped = vim_strsave_escape_ks(tb + j); if (escaped != NULL) { newlen = (int)STRLEN(escaped); memmove(tb + j, escaped, (size_t)newlen); @@ -3960,11 +3927,11 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) int save_msg_col; int save_msg_row; - /* Remove escaping of CSI, because "str" is in a format to be used as - * typeahead. */ + // Remove escaping of K_SPECIAL, because "str" is in a format to be used as + // typeahead. if (mp->m_luaref == LUA_NOREF) { expr = vim_strsave(mp->m_str); - vim_unescape_csi(expr); + vim_unescape_ks(expr); } save_cmd = save_cmdline_alloc(); @@ -4004,18 +3971,16 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) if (p == NULL) { return NULL; } - // Escape CSI in the result to be able to use the string as typeahead. - res = vim_strsave_escape_csi(p); + // Escape K_SPECIAL in the result to be able to use the string as typeahead. + res = vim_strsave_escape_ks(p); xfree(p); return res; } -/* - * Copy "p" to allocated memory, escaping K_SPECIAL and CSI so that the result - * can be put in the typeahead buffer. - */ -char_u *vim_strsave_escape_csi(char_u *p) +/// Copy "p" to allocated memory, escaping K_SPECIAL so that the result +/// can be put in the typeahead buffer. +char_u *vim_strsave_escape_ks(char_u *p) { // Need a buffer to hold up to three times as much. Four in case of an // illegal utf-8 byte: @@ -4030,7 +3995,7 @@ char_u *vim_strsave_escape_csi(char_u *p) *d++ = *s++; } else { // Add character, possibly multi-byte to destination, escaping - // CSI and K_SPECIAL. Be careful, it can be an illegal byte! + // K_SPECIAL. Be careful, it can be an illegal byte! d = add_char2buf(utf_ptr2char(s), d); s += utf_ptr2len(s); } @@ -4040,11 +4005,9 @@ char_u *vim_strsave_escape_csi(char_u *p) return res; } -/* - * Remove escaping from CSI and K_SPECIAL characters. Reverse of - * vim_strsave_escape_csi(). Works in-place. - */ -void vim_unescape_csi(char_u *p) +/// Remove escaping from K_SPECIAL characters. Reverse of +/// vim_strsave_escape_ks(). Works in-place. +void vim_unescape_ks(char_u *p) { char_u *s = p, *d = p; @@ -4052,10 +4015,6 @@ void vim_unescape_csi(char_u *p) if (s[0] == K_SPECIAL && s[1] == KS_SPECIAL && s[2] == KE_FILLER) { *d++ = K_SPECIAL; s += 3; - } else if ((s[0] == K_SPECIAL || s[0] == CSI) - && s[1] == KS_EXTRA && s[2] == (int)KE_CSI) { - *d++ = CSI; - s += 3; } else { *d++ = *s++; } @@ -4298,7 +4257,7 @@ int put_escstr(FILE *fd, char_u *strstart, int what) for (; *str != NUL; str++) { // Check for a multi-byte character, which may contain escaped - // K_SPECIAL and CSI bytes. + // K_SPECIAL bytes. const char *p = mb_unescape((const char **)&str); if (p != NULL) { while (*p != NUL) { diff --git a/src/nvim/input.c b/src/nvim/input.c index 2f7c5c2c16..5fa9b8b343 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -105,7 +105,7 @@ int get_keystroke(MultiQueue *events) // terminal code to complete. n = os_inchar(buf + len, maxlen, len == 0 ? -1L : 100L, 0, events); if (n > 0) { - // Replace zero and CSI by a special key code. + // Replace zero and K_SPECIAL by a special key code. n = fix_input_buffer(buf + len, n); len += n; waited = 0; diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index f8ccb79ade..32f2158d7b 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -963,7 +963,6 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, char_u **bu for (i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) { // If the character is K_SPECIAL, replace it with K_SPECIAL // KS_SPECIAL KE_FILLER. - // If compiled with the GUI replace CSI with K_CSI. if (*src == K_SPECIAL) { result[dlen++] = K_SPECIAL; result[dlen++] = KS_SPECIAL; diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index ec18dc64b5..42cae0c35e 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -220,7 +220,7 @@ enum key_extra { KE_KINS = 79, // keypad Insert key KE_KDEL = 80, // keypad Delete key - KE_CSI = 81, // CSI typed directly (no longer produced by Nvim) + // KE_CSI = 81, // Nvim doesn't need escaping CSI KE_SNR = 82, // KE_PLUG = 83, // KE_CMDWIN = 84, // open command-line window from Command-line Mode diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 5eb209a6f6..1d1cd5e271 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2089,8 +2089,7 @@ const char *mb_unescape(const char **const pp) size_t buf_idx = 0; uint8_t *str = (uint8_t *)(*pp); - // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI - // KS_EXTRA KE_CSI to CSI. + // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL. // Maximum length of a utf-8 character is 4 bytes. for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) { if (str[str_idx] == K_SPECIAL @@ -2098,11 +2097,6 @@ const char *mb_unescape(const char **const pp) && str[str_idx + 2] == KE_FILLER) { buf[buf_idx++] = (char)K_SPECIAL; str_idx += 2; - } else if ((str[str_idx] == K_SPECIAL) - && str[str_idx + 1] == KS_EXTRA - && str[str_idx + 2] == KE_CSI) { - buf[buf_idx++] = (char)CSI; - str_idx += 2; } else if (str[str_idx] == K_SPECIAL) { break; // A special key can't be a multibyte char. } else { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1a12cb636a..2bc92ce295 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -922,8 +922,8 @@ int do_record(int c) // The recorded text contents. p = get_recorded(); if (p != NULL) { - // Remove escaping for CSI and K_SPECIAL in multi-byte chars. - vim_unescape_csi(p); + // Remove escaping for K_SPECIAL in multi-byte chars. + vim_unescape_ks(p); (void)tv_dict_add_str(dict, S_LEN("regcontents"), (const char *)p); } @@ -933,7 +933,7 @@ int do_record(int c) buf[1] = NUL; (void)tv_dict_add_str(dict, S_LEN("regname"), buf); - // Get the recorded key hits. K_SPECIAL and CSI will be escaped, this + // Get the recorded key hits. K_SPECIAL will be escaped, this // needs to be removed again to put it in a register. exec_reg then // adds the escaping back later. apply_autocmds(EVENT_RECORDINGLEAVE, NULL, NULL, false, curbuf); @@ -1099,7 +1099,7 @@ int do_execreg(int regname, int colon, int addcr, int silent) return FAIL; } } - escaped = vim_strsave_escape_csi(reg->y_array[i]); + escaped = vim_strsave_escape_ks(reg->y_array[i]); retval = ins_typebuf(escaped, remap, 0, true, silent); xfree(escaped); if (retval == FAIL) { @@ -1141,7 +1141,7 @@ static void put_reedit_in_typebuf(int silent) /// Insert register contents "s" into the typeahead buffer, so that it will be /// executed again. /// -/// @param esc when true then it is to be taken literally: Escape CSI +/// @param esc when true then it is to be taken literally: Escape K_SPECIAL /// characters and no remapping. /// @param colon add ':' before the line static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent) @@ -1156,7 +1156,7 @@ static int put_in_typebuf(char_u *s, bool esc, bool colon, int silent) char_u *p; if (esc) { - p = vim_strsave_escape_csi(s); + p = vim_strsave_escape_ks(s); } else { p = s; } -- cgit From 296b8fbe3b62984210bb93c7d64b68cd1dd479b7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:16:16 +0800 Subject: vim-patch:8.2.3121: 'listchars' "exceeds" character appears in foldcolumn Problem: 'listchars' "exceeds" character appears in foldcolumn. Window separator is missing. (Leonid V. Fedorenchik) Solution: Only draw the "exceeds" character in the text area. Break the loop when not drawing the text. (closes vim/vim#8524) https://github.com/vim/vim/commit/41fb723ee97baa2f095cde601a5a144b168b7a6b --- src/nvim/screen.c | 4 +++- src/nvim/testdir/test_listchars.vim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 538604cf79..ab726dfdb0 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4232,6 +4232,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Show "extends" character from 'listchars' if beyond the line end and // 'list' is set. if (wp->w_p_lcs_chars.ext != NUL + && draw_state == WL_LINE && wp->w_p_list && !wp->w_p_wrap && filler_todo <= 0 @@ -4427,7 +4428,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc */ if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns)) && foldinfo.fi_lines == 0 - && (*ptr != NUL + && (draw_state != WL_LINE + || *ptr != NUL || filler_todo > 0 || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && p_extra != at_end_str) diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim index 0bcbd9c4a5..c6e2ebd406 100644 --- a/src/nvim/testdir/test_listchars.vim +++ b/src/nvim/testdir/test_listchars.vim @@ -1,6 +1,8 @@ " Tests for 'listchars' display with 'list' and :list +source check.vim source view_util.vim +source screendump.vim func Test_listchars() enew! @@ -517,4 +519,34 @@ func Test_listchars_window_local() set list& listchars& endfunc +func Test_listchars_foldcolumn() + CheckScreendump + + let lines =<< trim END + call setline(1, ['aaa', '', 'a', 'aaaaaa']) + vsplit + vsplit + windo set signcolumn=yes foldcolumn=1 winminwidth=0 nowrap list listchars=extends:>,precedes:< + END + call writefile(lines, 'XTest_listchars') + + let buf = RunVimInTerminal('-S XTest_listchars', {'rows': 10, 'cols': 60}) + + call term_sendkeys(buf, "13\>") + call VerifyScreenDump(buf, 'Test_listchars_01', {}) + call term_sendkeys(buf, "\>") + call VerifyScreenDump(buf, 'Test_listchars_02', {}) + call term_sendkeys(buf, "\>") + call VerifyScreenDump(buf, 'Test_listchars_03', {}) + call term_sendkeys(buf, "\>") + call VerifyScreenDump(buf, 'Test_listchars_04', {}) + call term_sendkeys(buf, "\>") + call VerifyScreenDump(buf, 'Test_listchars_05', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_listchars') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From cb39c825c49c5557f8df5a1242907c49cacad4b1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:16:16 +0800 Subject: vim-patch:8.2.3410: crash with linebreak, listchars and large tabstop Problem: Crash with linebreak, listchars and large tabstop. Solution: Account for different size listchars for a tab. (closes vim/vim#8841) https://github.com/vim/vim/commit/89a54b413a8c96206ce7e038dde81a6eff6cd6b8 --- src/nvim/screen.c | 55 ++++++++++++++++++---------------- src/nvim/testdir/test_listlbr_utf8.vim | 10 +++++++ 2 files changed, 40 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index ab726dfdb0..6a8792b7b6 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3720,41 +3720,46 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc tab_len += n_extra - tab_len; } - // if n_extra > 0, it gives the number of chars + // If n_extra > 0, it gives the number of chars // to use for a tab, else we need to calculate the width - // for a tab + // for a tab. int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2)); + if (wp->w_p_lcs_chars.tab3) { + len += utf_char2len(wp->w_p_lcs_chars.tab3); + } if (n_extra > 0) { len += n_extra - tab_len; } c = wp->w_p_lcs_chars.tab1; p = xmalloc(len + 1); - memset(p, ' ', len); - p[len] = NUL; - xfree(p_extra_free); - p_extra_free = p; - for (i = 0; i < tab_len; i++) { - if (*p == NUL) { - tab_len = i; - break; - } - int lcs = wp->w_p_lcs_chars.tab2; + if (p == NULL) { + n_extra = 0; + } else { + memset(p, ' ', len); + p[len] = NUL; + xfree(p_extra_free); + p_extra_free = p; + for (i = 0; i < tab_len; i++) { + if (*p == NUL) { + tab_len = i; + break; + } + int lcs = wp->w_p_lcs_chars.tab2; - // if tab3 is given, need to change the char - // for tab - if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { - lcs = wp->w_p_lcs_chars.tab3; + // if tab3 is given, use it for the last char + if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { + lcs = wp->w_p_lcs_chars.tab3; + } + p += utf_char2bytes(lcs, p); + n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); } - utf_char2bytes(lcs, p); - p += utf_char2len(lcs); - n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); - } - p_extra = p_extra_free; + p_extra = p_extra_free; - // n_extra will be increased by FIX_FOX_BOGUSCOLS - // macro below, so need to adjust for that here - if (vcol_off > 0) { - n_extra -= vcol_off; + // n_extra will be increased by FIX_FOX_BOGUSCOLS + // macro below, so need to adjust for that here + if (vcol_off > 0) { + n_extra -= vcol_off; + } } } diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim index c38e0c5f3c..1f100d6244 100644 --- a/src/nvim/testdir/test_listlbr_utf8.vim +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -69,6 +69,16 @@ func Test_nolinebreak_with_list() call s:close_windows() endfunc +" this was causing a crash +func Test_linebreak_with_list_and_tabs() + set linebreak list listchars=tab:⇤\ ⇥ tabstop=100 + new + call setline(1, "\t\t\ttext") + redraw + bwipe! + set nolinebreak nolist listchars&vim tabstop=8 +endfunc + func Test_linebreak_with_nolist() call s:test_windows('setl nolist') call setline(1, "\t*mask = nil;") -- cgit From 8e84d1b93043f33d997627f99a181159aa66434d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:18:18 +0800 Subject: vim-patch:8.2.3584: "verbose set efm" reports location of the :compiler command Problem: "verbose set efm" reports the location of the :compiler command. (Gary Johnson) Solution: Add the "-keepscript" argument to :command and use it when defining CompilerSet. https://github.com/vim/vim/commit/58ef8a31d7087d495ab1582be5b7a22796ac2451 --- src/nvim/api/keysets.lua | 1 + src/nvim/api/private/helpers.c | 6 ++++++ src/nvim/ex_cmds2.c | 2 +- src/nvim/ex_cmds_defs.h | 1 + src/nvim/ex_docmd.c | 13 ++++++++++--- src/nvim/testdir/test_compiler.vim | 3 +++ 6 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index a385cfc64f..7d521bbf25 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -44,6 +44,7 @@ return { "count"; "desc"; "force"; + "keepscript"; "nargs"; "range"; "register"; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f777fa1d27..f540f8f7ee 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1510,6 +1510,12 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int goto err; } + if (api_object_to_bool(opts->keepscript, "keepscript", false, err)) { + argt |= EX_KEEPSCRIPT; + } else if (ERROR_SET(err)) { + goto err; + } + bool force = api_object_to_bool(opts->force, "force", true, err); if (ERROR_SET(err)) { goto err; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 2e8d39ec30..267f5616f5 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1613,7 +1613,7 @@ void ex_compiler(exarg_T *eap) if (old_cur_comp != NULL) { old_cur_comp = vim_strsave(old_cur_comp); } - do_cmdline_cmd("command -nargs=* CompilerSet setlocal "); + do_cmdline_cmd("command -nargs=* -keepscript CompilerSet setlocal "); } do_unlet(S_LEN("g:current_compiler"), true); do_unlet(S_LEN("b:current_compiler"), true); diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index e5eab61f9e..eaf5f627b6 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -61,6 +61,7 @@ // current buffer is locked #define EX_MODIFY 0x100000 // forbidden in non-'modifiable' buffer #define EX_FLAGS 0x200000 // allow flags after count in argument +#define EX_KEEPSCRIPT 0x4000000 // keep sctx of where command was invoked #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file #define EX_WORD1 (EX_EXTRA | EX_NOSPC) // one extra word allowed diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index b33a17c631..625e0ea1d5 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5517,6 +5517,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags |= UC_BUFFER; } else if (STRNICMP(attr, "register", len) == 0) { *argt |= EX_REGSTR; + } else if (STRNICMP(attr, "keepscript", len) == 0) { + *argt |= EX_KEEPSCRIPT; } else if (STRNICMP(attr, "bar", len) == 0) { *argt |= EX_TRLBAR; } else { @@ -6257,10 +6259,14 @@ static void do_ucmd(exarg_T *eap) buf = xmalloc(totlen + 1); } - current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + } (void)do_cmdline(buf, eap->getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); - current_sctx = save_current_sctx; + if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + current_sctx = save_current_sctx; + } xfree(buf); xfree(split_buf); } @@ -6306,7 +6312,7 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx) { static char *user_cmd_flags[] = { "addr", "bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register" }; + "nargs", "range", "register", "keepscript" }; if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { return NULL; @@ -9846,6 +9852,7 @@ Dictionary commands_array(buf_T *buf) PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); + PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index aaa2301bca..da1c847d96 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -19,6 +19,9 @@ func Test_compiler() call assert_equal('perl', b:current_compiler) call assert_fails('let g:current_compiler', 'E121:') + let verbose_efm = execute('verbose set efm') + call assert_match('Last set from .*/compiler/perl.vim ', verbose_efm) + call setline(1, ['#!/usr/bin/perl -w', 'use strict;', 'my $foo=1']) w! call feedkeys(":make\\", 'tx') -- cgit From 792381e1a07cccd82d0a8a49c32ffffebe4531b2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:18:18 +0800 Subject: vim-patch:8.2.3586: command completion test fails Problem: Command completion test fails. Solution: Add new argument to expected output https://github.com/vim/vim/commit/326e7da609a1b115b0ed535e89e970afebe99e35 --- src/nvim/testdir/test_usercommands.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 29e578ac6d..481959d43d 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -269,10 +269,10 @@ endfunc func Test_CmdCompletion() call feedkeys(":com -\\\"\", 'tx') - call assert_equal('"com -addr bang bar buffer complete count nargs range register', @:) + call assert_equal('"com -addr bang bar buffer complete count keepscript nargs range register', @:) call feedkeys(":com -nargs=0 -\\\"\", 'tx') - call assert_equal('"com -nargs=0 -addr bang bar buffer complete count nargs range register', @:) + call assert_equal('"com -nargs=0 -addr bang bar buffer complete count keepscript nargs range register', @:) call feedkeys(":com -nargs=\\\"\", 'tx') call assert_equal('"com -nargs=* + 0 1 ?', @:) -- cgit From f2dbeca863ae5ce5d557cd1047ed6f44b5607a27 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Jan 2022 18:18:18 +0800 Subject: vim-patch:8.2.3587: compiler test fails with backslash file separator Problem: Compiler test fails with backslash file separator. Solution: Accept slash and backslash. https://github.com/vim/vim/commit/0a15c7676bccb0c9483579106318e785c6e40a7f --- src/nvim/testdir/test_compiler.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index da1c847d96..c0c572ce65 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -20,7 +20,7 @@ func Test_compiler() call assert_fails('let g:current_compiler', 'E121:') let verbose_efm = execute('verbose set efm') - call assert_match('Last set from .*/compiler/perl.vim ', verbose_efm) + call assert_match('Last set from .*[/\\]compiler[/\\]perl.vim ', verbose_efm) call setline(1, ['#!/usr/bin/perl -w', 'use strict;', 'my $foo=1']) w! -- cgit From 9d02fc4c00f61724610224f91950c51bd2700c97 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 21 Jan 2022 16:45:32 +0100 Subject: vim-patch:8.2.4172: filetype detection for BASIC is not optimal (#17161) Problem: Filetype detection for BASIC is not optimal. Solution: Improve BASIC filetype detection. (Doug Kearns) https://github.com/vim/vim/commit/6517f14165cdebf83a07ab9d4aeeb102b4e16e92 --- src/nvim/testdir/test_filetype.vim | 64 +++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 7db05e34d5..014cb929bf 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -76,6 +76,7 @@ let s:filename_checks = { \ 'ave': ['file.ave'], \ 'awk': ['file.awk', 'file.gawk'], \ 'b': ['file.mch', 'file.ref', 'file.imp'], + \ 'basic': ['file.bas', 'file.bi', 'file.bm'], \ 'bzl': ['file.bazel', 'file.bzl', 'WORKSPACE'], \ 'bc': ['file.bc'], \ 'bdf': ['file.bdf'], @@ -186,7 +187,7 @@ let s:filename_checks = { \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'], \ 'fpcmake': ['file.fpc'], \ 'framescript': ['file.fsl'], - \ 'freebasic': ['file.fb', 'file.bi'], + \ 'freebasic': ['file.fb'], \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'], \ 'fstab': ['fstab', 'mtab'], \ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'], @@ -1146,4 +1147,65 @@ func Test_foam_file() filetype off endfunc +func Test_bas_file() + filetype on + + call writefile(['looks like BASIC'], 'Xfile.bas') + split Xfile.bas + call assert_equal('basic', &filetype) + bwipe! + + " Test dist#ft#FTbas() + + let g:filetype_bas = 'freebasic' + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + unlet g:filetype_bas + + " FreeBASIC + + call writefile(["/' FreeBASIC multiline comment '/"], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + + call writefile(['#define TESTING'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + + call writefile(['option byval'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + + call writefile(['extern "C"'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + + " QB64 + + call writefile(['$LET TESTING = 1'], 'Xfile.bas') + split Xfile.bas + call assert_equal('qb64', &filetype) + bwipe! + + call writefile(['OPTION _EXPLICIT'], 'Xfile.bas') + split Xfile.bas + call assert_equal('qb64', &filetype) + bwipe! + + " Visual Basic + + call writefile(['Attribute VB_NAME = "Testing"'], 'Xfile.bas') + split Xfile.bas + call assert_equal('vb', &filetype) + bwipe! + + call delete('Xfile.bas') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From d7ab4e819a89c12b74772217e725c640858a18c6 Mon Sep 17 00:00:00 2001 From: Shougo Matsushita Date: Fri, 21 Jan 2022 09:36:26 +0900 Subject: vim-patch:8.2.4160: cannot change the register used for Select mode delete Problem: Cannot change the register used for Select mode delete. Solution: Make CTRL-R set the register to be used when deleting text for Select mode. (Shougo Matsushita, closes vim/vim#9531) https://github.com/vim/vim/commit/4ede01f18884961f2e008880b4964e5d61ea5c36 --- src/nvim/globals.h | 2 ++ src/nvim/normal.c | 28 ++++++++++++++---- src/nvim/ops.c | 5 ++++ src/nvim/testdir/test_selectmode.vim | 57 ++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 src/nvim/testdir/test_selectmode.vim (limited to 'src') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 40c61d01b5..e0ea4060fd 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -524,6 +524,8 @@ EXTERN pos_T VIsual; EXTERN int VIsual_active INIT(= false); /// Whether Select mode is active. EXTERN int VIsual_select INIT(= false); +/// Register name for Select mode +EXTERN int VIsual_select_reg INIT(= 0); /// Restart Select mode when next cmd finished EXTERN int restart_VIsual_select INIT(= 0); /// Whether to restart the selection after a Select-mode mapping or menu. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2b5b47c0b3..2fce3e10b8 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -164,7 +164,7 @@ static const struct nv_cmd { { Ctrl_O, nv_ctrlo, 0, 0 }, { Ctrl_P, nv_up, NV_STS, false }, { Ctrl_Q, nv_visual, 0, false }, - { Ctrl_R, nv_redo, 0, 0 }, + { Ctrl_R, nv_redo_or_register, 0, 0 }, { Ctrl_S, nv_ignore, 0, 0 }, { Ctrl_T, nv_tagpop, NV_NCW, 0 }, { Ctrl_U, nv_halfpage, 0, 0 }, @@ -961,6 +961,7 @@ normal_end: && s->oa.regname == 0) { if (restart_VIsual_select == 1) { VIsual_select = true; + VIsual_select_reg = 0; trigger_modechanged(); showmode(); restart_VIsual_select = 0; @@ -6186,6 +6187,7 @@ static void nv_g_cmd(cmdarg_T *cap) // start Select mode. if (cap->arg) { VIsual_select = true; + VIsual_select_reg = 0; } else { may_start_select('c'); } @@ -6698,11 +6700,26 @@ static void nv_dot(cmdarg_T *cap) } } -/* - * CTRL-R: undo undo - */ -static void nv_redo(cmdarg_T *cap) +// CTRL-R: undo undo or specify register in select mode +static void nv_redo_or_register(cmdarg_T *cap) { + if (VIsual_select && VIsual_active) { + int reg; + // Get register name + no_mapping++; + reg = plain_vgetc(); + LANGMAP_ADJUST(reg, true); + no_mapping--; + + if (reg == '"') { + // the unnamed register is 0 + reg = 0; + } + + VIsual_select_reg = valid_yank_reg(reg, true) ? reg : 0; + return; + } + if (!checkclearopq(cap->oap)) { u_redo((int)cap->count1); curwin->w_set_curswant = true; @@ -7023,6 +7040,7 @@ static void nv_select(cmdarg_T *cap) { if (VIsual_active) { VIsual_select = true; + VIsual_select_reg = 0; } else if (VIsual_reselect) { cap->nchar = 'v'; // fake "gv" command cap->arg = true; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 1a12cb636a..497d756cdd 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1435,6 +1435,11 @@ int op_delete(oparg_T *oap) return FAIL; } + if (VIsual_select && oap->is_VIsual) { + // Use the register given with CTRL_R, defaults to zero + oap->regname = VIsual_select_reg; + } + mb_adjust_opend(oap); /* diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim new file mode 100644 index 0000000000..b483841060 --- /dev/null +++ b/src/nvim/testdir/test_selectmode.vim @@ -0,0 +1,57 @@ +" Test for Select-mode + +source shared.vim + +" Test for selecting a register with CTRL-R +func Test_selectmode_register() + new + + " Default behavior: use unnamed register + call setline(1, 'foo') + call setreg('"', 'bar') + call setreg('a', 'baz') + exe ":norm! v\a" + call assert_equal(getline('.'), 'aoo') + call assert_equal('f', getreg('"')) + call assert_equal('baz', getreg('a')) + + " Use the black hole register + call setline(1, 'foo') + call setreg('"', 'bar') + call setreg('a', 'baz') + exe ":norm! v\\_a" + call assert_equal(getline('.'), 'aoo') + call assert_equal('bar', getreg('"')) + call assert_equal('baz', getreg('a')) + + " Invalid register: use unnamed register + call setline(1, 'foo') + call setreg('"', 'bar') + call setreg('a', 'baz') + exe ":norm! v\\?a" + call assert_equal(getline('.'), 'aoo') + call assert_equal('f', getreg('"')) + call assert_equal('baz', getreg('a')) + + " Use unnamed register + call setline(1, 'foo') + call setreg('"', 'bar') + call setreg('a', 'baz') + exe ":norm! v\\\"a" + call assert_equal(getline('.'), 'aoo') + call assert_equal('f', getreg('"')) + call assert_equal('baz', getreg('a')) + + " use specicifed register, unnamed register is also written + call setline(1, 'foo') + call setreg('"', 'bar') + call setreg('a', 'baz') + exe ":norm! v\\aa" + call assert_equal(getline('.'), 'aoo') + call assert_equal('f', getreg('"')) + call assert_equal('f', getreg('a')) + + bw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 818456470c0ce0d463b0be82e9c35eb77cc65f19 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 23 Jan 2022 05:58:32 +0800 Subject: fix(input): put modifiers back into typeahead buffer when needed --- src/nvim/getchar.c | 39 ++++++++++++++++++++++++--------------- src/nvim/globals.h | 2 +- src/nvim/message.c | 4 ++-- src/nvim/normal.c | 2 +- src/nvim/terminal.c | 2 +- 5 files changed, 29 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index f2b0b9a8a9..ef590adb3a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -973,27 +973,35 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to * the char. */ -void ins_char_typebuf(int c) +void ins_char_typebuf(int c, int modifier) { - char_u buf[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) { + char_u buf[MB_MAXBYTES + 4]; + int idx = 0; + if (modifier != 0) { buf[0] = K_SPECIAL; - buf[1] = (char_u)K_SECOND(c); - buf[2] = (char_u)K_THIRD(c); + buf[1] = KS_MODIFIER; + buf[2] = (char_u)modifier; buf[3] = NUL; + idx = 3; + } + if (IS_SPECIAL(c)) { + buf[idx] = K_SPECIAL; + buf[idx + 1] = (char_u)K_SECOND(c); + buf[idx + 2] = (char_u)K_THIRD(c); + buf[idx + 3] = NUL; } else { - buf[utf_char2bytes(c, buf)] = NUL; - char_u *p = buf; - while (*p) { + char_u *p = buf + idx; + int char_len = utf_char2bytes(c, p); + // If the character contains K_SPECIAL bytes they need escaping. + for (int i = char_len; --i >= 0; p++) { if ((uint8_t)(*p) == K_SPECIAL) { - memmove(p + 3, p + 1, STRLEN(p + 1) + 1); + memmove(p + 3, p + 1, (size_t)i); *p++ = K_SPECIAL; *p++ = KS_SPECIAL; - *p++ = KE_FILLER; - } else { - p++; + *p = KE_FILLER; } } + *p = NUL; } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); } @@ -1433,8 +1441,9 @@ int vgetc(void) mouse_row = old_mouse_row; mouse_col = old_mouse_col; } else { - mod_mask = 0x0; + mod_mask = 0; last_recorded_len = 0; + for (;;) { // this is done twice if there are modifiers bool did_inc = false; if (mod_mask) { // no mapping after modifier has been read @@ -1560,8 +1569,8 @@ int vgetc(void) if (!no_mapping && KeyTyped && !(State & TERM_FOCUS) && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { mod_mask = 0; - ins_char_typebuf(c); - ins_char_typebuf(ESC); + ins_char_typebuf(c, 0); + ins_char_typebuf(ESC, 0); continue; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 40c61d01b5..31a09b3969 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -127,7 +127,7 @@ typedef off_t off_T; // When vgetc() is called, it sets mod_mask to the set of modifiers that are // held down based on the MOD_MASK_* symbols that are read first. -EXTERN int mod_mask INIT(= 0x0); // current key modifiers +EXTERN int mod_mask INIT(= 0); // current key modifiers // Cmdline_row is the row where the command line starts, just below the diff --git a/src/nvim/message.c b/src/nvim/message.c index 39b023132e..17ccef37f1 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1215,7 +1215,7 @@ void wait_return(int redraw) } else if (vim_strchr((char_u *)"\r\n ", c) == NULL && c != Ctrl_C) { // Put the character back in the typeahead buffer. Don't use the // stuff buffer, because lmaps wouldn't work. - ins_char_typebuf(c); + ins_char_typebuf(c, mod_mask); do_redraw = true; // need a redraw even though there is // typeahead } @@ -3497,7 +3497,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl } if (c == ':' && ex_cmd) { retval = dfltbutton; - ins_char_typebuf(':'); + ins_char_typebuf(':', 0); break; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2b5b47c0b3..c3b7e81d17 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1010,7 +1010,7 @@ static int normal_execute(VimState *state, int key) // restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - ins_char_typebuf(s->c); + ins_char_typebuf(s->c, mod_mask); if (restart_edit != 0) { s->c = 'd'; } else { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 70a5c7aa08..a2d855244c 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1299,7 +1299,7 @@ static bool send_mouse_event(Terminal *term, int c) } end: - ins_char_typebuf(c); + ins_char_typebuf(c, mod_mask); return true; } -- cgit From 3d62dd207733bbdc46fd5b6807e5d7dcf95681e2 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 23 Jan 2022 13:52:37 +0100 Subject: vim-patch:8.2.4187: gnuplot file not recognized (#17177) Problem: Gnuplot file not recognized. Solution: Recognize ".gnuplot". (closes vim/vim#9588) https://github.com/vim/vim/commit/ff5cbe8133c6eb5dd86b9e042f32f589627e9bf9 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 014cb929bf..e2cf0c3e69 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -203,7 +203,7 @@ let s:filename_checks = { \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], \ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'], \ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'], - \ 'gnuplot': ['file.gpi'], + \ 'gnuplot': ['file.gpi', '.gnuplot'], \ 'go': ['file.go'], \ 'gomod': ['go.mod'], \ 'gp': ['file.gp', '.gprc'], -- cgit From ffd9551aa210ecd652044ad3c43eb480858f191a Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 23 Jan 2022 16:19:48 +0100 Subject: vim-patch:8.2.4191: json5 files are not recognized (#17180) Problem: json5 files are not recognized. Solution: Add a pattern for json5 files. (closes vim/vim#9601) https://github.com/vim/vim/commit/e15ebeffb35da4bb7d9054358671735ce6988c28 --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index e2cf0c3e69..b5fa362e48 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -265,6 +265,7 @@ let s:filename_checks = { \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc', 'file.slnf'], + \ 'json5': ['file.json5'], \ 'jsonc': ['file.jsonc'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], -- cgit From 28352dc6e50b9559f32f054c56dc950a79ae45bc Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 23 Jan 2022 17:50:45 +0100 Subject: vim-patch:8.2.4188: not all gitconfig files are recognized (#17178) Problem: Not all gitconfig files are recognized. Solution: Add a few more patterns. (Tim Pope, closes vim/vim#9597) https://github.com/vim/vim/commit/bcfa11b7dfdfbb4d412dd843a6da3fce68ba2e39 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index b5fa362e48..8930d62fd3 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -197,7 +197,7 @@ let s:filename_checks = { \ 'gemtext': ['file.gmi', 'file.gemini'], \ 'gift': ['file.gift'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'], - \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], + \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], \ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'], \ 'gitrebase': ['git-rebase-todo'], \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], -- cgit From 3d9ae9d2dad88a4e2c2263dc7e256657842244c0 Mon Sep 17 00:00:00 2001 From: notomo Date: Mon, 24 Jan 2022 09:52:13 +0900 Subject: feat(api): expose extmark right_gravity and end_right_gravity --- src/nvim/api/extmark.c | 3 +++ src/nvim/extmark.c | 18 +++++++++++------- src/nvim/extmark.h | 2 ++ src/nvim/marktree.c | 7 ++++++- 4 files changed, 22 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 80bd88c4ee..3a968f07ab 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -106,9 +106,12 @@ static Array extmark_to_array(ExtmarkInfo extmark, bool id, bool add_dict) if (add_dict) { Dictionary dict = ARRAY_DICT_INIT; + PUT(dict, "right_gravity", BOOLEAN_OBJ(extmark.right_gravity)); + if (extmark.end_row >= 0) { PUT(dict, "end_row", INTEGER_OBJ(extmark.end_row)); PUT(dict, "end_col", INTEGER_OBJ(extmark.end_col)); + PUT(dict, "end_right_gravity", BOOLEAN_OBJ(extmark.end_right_gravity)); } Decoration *decor = &extmark.decor; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index cee657c8c9..48e57e20e1 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -305,12 +305,14 @@ ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_co } if (mark.ns == ns_id) { - mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL); kv_push(array, ((ExtmarkInfo) { .ns_id = mark.ns, .mark_id = mark.id, .row = mark.pos.row, .col = mark.pos.col, - .end_row = endpos.row, - .end_col = endpos.col, + .end_row = end.pos.row, + .end_col = end.pos.col, + .right_gravity = mt_right(mark), + .end_right_gravity = mt_right(end), .decor = get_decor(mark) })); } next_mark: @@ -326,20 +328,22 @@ next_mark: // Lookup an extmark by id ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) { - ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, DECORATION_INIT }; + ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, DECORATION_INIT }; mtkey_t mark = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, NULL); if (!mark.id) { return ret; } assert(mark.pos.row >= 0); - mtpos_t endpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + mtkey_t end = marktree_get_alt(buf->b_marktree, mark, NULL); ret.ns_id = ns_id; ret.mark_id = id; ret.row = mark.pos.row; ret.col = mark.pos.col; - ret.end_row = endpos.row; - ret.end_col = endpos.col; + ret.end_row = end.pos.row; + ret.end_col = end.pos.col; + ret.right_gravity = mt_right(mark); + ret.end_right_gravity = mt_right(end); ret.decor = get_decor(mark); return ret; diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h index c6ec1d0aa8..af9526cd43 100644 --- a/src/nvim/extmark.h +++ b/src/nvim/extmark.h @@ -16,6 +16,8 @@ typedef struct { colnr_T col; int end_row; colnr_T end_col; + bool right_gravity; + bool end_right_gravity; Decoration decor; // TODO(bfredl): CHONKY } ExtmarkInfo; diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 8ae138b2eb..918db8b76c 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -1078,12 +1078,17 @@ found_node: } mtpos_t marktree_get_altpos(MarkTree *b, mtkey_t mark, MarkTreeIter *itr) +{ + return marktree_get_alt(b, mark, itr).pos; +} + +mtkey_t marktree_get_alt(MarkTree *b, mtkey_t mark, MarkTreeIter *itr) { mtkey_t end = MT_INVALID_KEY; if (mt_paired(mark)) { end = marktree_lookup_ns(b, mark.ns, mark.id, !mt_end(mark), itr); } - return end.pos; + return end; } static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) -- cgit From 1b184cea3b6d93fddb34a77fa2ddaf3bca8d1bb7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 11:10:37 +0800 Subject: vim-patch:8.2.4190: all conceal tests are skipped without the screendumps feature Problem: All conceal tests are skipped without the screendumps feature. Solution: Only skip the tests that use screendumps. (closes vim/vim#9599) https://github.com/vim/vim/commit/206919191fe1881dea00d60d392cc68a07c0106f --- src/nvim/testdir/test_conceal.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_conceal.vim b/src/nvim/testdir/test_conceal.vim index 1306dbe5cf..bffc2f49d3 100644 --- a/src/nvim/testdir/test_conceal.vim +++ b/src/nvim/testdir/test_conceal.vim @@ -4,10 +4,10 @@ source check.vim CheckFeature conceal source screendump.vim -" CheckScreendump func Test_conceal_two_windows() CheckScreendump + let code =<< trim [CODE] let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"] call setline(1, lines) @@ -111,6 +111,7 @@ endfunc func Test_conceal_with_cursorline() CheckScreendump + " Opens a help window, where 'conceal' is set, switches to the other window " where 'cursorline' needs to be updated when the cursor moves. let code =<< trim [CODE] @@ -139,6 +140,7 @@ endfunc func Test_conceal_resize_term() CheckScreendump + let code =<< trim [CODE] call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed') setl cocu=n cole=3 -- cgit From 8f1efb018bff6ae95fae62e5ae943d6478e9c547 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 12:46:52 +0800 Subject: vim-patch:8.2.3993: when recording a change in Select mode char appears twice Problem: When recording a change in Select mode the first typed character appears twice. Solution: When putting the character back into typeahead remove it from recorded characters. (closes vim/vim#9462) https://github.com/vim/vim/commit/c88e977862ba6477a3b5b28706c45f96069a3073 --- src/nvim/getchar.c | 58 ++++++++++++++++++++++++++----------- src/nvim/normal.c | 7 ++++- src/nvim/testdir/test_registers.vim | 11 +++++++ 3 files changed, 58 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ef590adb3a..72f7f9cd5c 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -284,9 +284,19 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle } } -/* - * Add number "n" to buffer "buf". - */ +/// Delete "slen" bytes from the end of "buf". +/// Only works when it was just added. +static void delete_buff_tail(buffheader_T *buf, int slen) +{ + int len = (int)STRLEN(buf->bh_curr->b_str); + + if (len >= slen) { + buf->bh_curr->b_str[len - slen] = NUL; + buf->bh_space += (size_t)slen; + } +} + +/// Add number "n" to buffer "buf". static void add_num_buff(buffheader_T *buf, long n) { char number[32]; @@ -967,31 +977,31 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent return OK; } -/* - * Put character "c" back into the typeahead buffer. - * Can be used for a character obtained by vgetc() that needs to be put back. - * Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to - * the char. - */ -void ins_char_typebuf(int c, int modifier) +/// Put character "c" back into the typeahead buffer. +/// Can be used for a character obtained by vgetc() that needs to be put back. +/// Uses cmd_silent, KeyTyped and KeyNoremap to restore the flags belonging to +/// the char. +/// @return the length of what was inserted +int ins_char_typebuf(int c, int modifier) { char_u buf[MB_MAXBYTES + 4]; - int idx = 0; + int len = 0; if (modifier != 0) { buf[0] = K_SPECIAL; buf[1] = KS_MODIFIER; buf[2] = (char_u)modifier; buf[3] = NUL; - idx = 3; + len = 3; } if (IS_SPECIAL(c)) { - buf[idx] = K_SPECIAL; - buf[idx + 1] = (char_u)K_SECOND(c); - buf[idx + 2] = (char_u)K_THIRD(c); - buf[idx + 3] = NUL; + buf[len] = K_SPECIAL; + buf[len + 1] = (char_u)K_SECOND(c); + buf[len + 2] = (char_u)K_THIRD(c); + buf[len + 3] = NUL; } else { - char_u *p = buf + idx; + char_u *p = buf + len; int char_len = utf_char2bytes(c, p); + len += char_len; // If the character contains K_SPECIAL bytes they need escaping. for (int i = char_len; --i >= 0; p++) { if ((uint8_t)(*p) == K_SPECIAL) { @@ -999,11 +1009,13 @@ void ins_char_typebuf(int c, int modifier) *p++ = K_SPECIAL; *p++ = KS_SPECIAL; *p = KE_FILLER; + len += 2; } } *p = NUL; } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); + return len; } /// Return TRUE if the typeahead buffer was changed (while waiting for a @@ -1163,6 +1175,18 @@ static void gotchars(const char_u *chars, size_t len) maptick++; } +/// Undo the last gotchars() for "len" bytes. To be used when putting a typed +/// character back into the typeahead buffer, thus gotchars() will be called +/// again. +/// Only affects recorded characters. +void ungetchars(int len) +{ + if (reg_recording != 0) { + delete_buff_tail(&recordbuff, len); + last_recorded_len -= (size_t)len; + } +} + /* * Sync undo. Called when typed characters are obtained from the typeahead * buffer, or when a menu is used. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index c3b7e81d17..f802dce20d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1010,7 +1010,12 @@ static int normal_execute(VimState *state, int key) // restart automatically. // Insert the typed character in the typeahead buffer, so that it can // be mapped in Insert mode. Required for ":lmap" to work. - ins_char_typebuf(s->c, mod_mask); + int len = ins_char_typebuf(s->c, mod_mask); + + // When recording the character will be recorded again, remove the + // previously recording. + ungetchars(len); + if (restart_edit != 0) { s->c = 'd'; } else { diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 4371828a72..1be61d1519 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -121,6 +121,17 @@ func Test_recording_esc_sequence() endif endfunc +func Test_recording_with_select_mode() + new + call feedkeys("qacc12345\gH98765\q", "tx") + call assert_equal("98765", getline(1)) + call assert_equal("cc12345\gH98765\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("98765", getline(1)) + bwipe! +endfunc + " Test for executing the last used register (@) func Test_last_used_exec_reg() " Test for the @: command -- cgit From 5c897b6d0ceb7e7d40c0b740ab36d38bf3a4162d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 12:46:52 +0800 Subject: vim-patch:8.2.4002: first char typed in Select mode can be wrong Problem: First char typed in Select mode can be wrong. Solution: Escape special bytes in the input buffer. (closes vim/vim#9469) https://github.com/vim/vim/commit/6cac77016b1636e04073e8348b7cee02259ef928 The `buf` should already be large enough, but I'll change its size anyway in case future patches change the meaning of `MB_MAXBYTES` macro. `fix_input_buffer()` cannot be used here because of the `using_script()` check, and there is already equivalent code in its place. --- src/nvim/getchar.c | 2 +- src/nvim/testdir/test_utf8.vim | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 72f7f9cd5c..95720c498a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -984,7 +984,7 @@ int ins_typebuf(char_u *str, int noremap, int offset, bool nottyped, bool silent /// @return the length of what was inserted int ins_char_typebuf(int c, int modifier) { - char_u buf[MB_MAXBYTES + 4]; + char_u buf[MB_MAXBYTES * 3 + 4]; int len = 0; if (modifier != 0) { buf[0] = K_SPECIAL; diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 0818c2e4b0..36776d5a64 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -1,5 +1,6 @@ " Tests for Unicode manipulations +source check.vim source view_util.vim " Visual block Insert adjusts for multi-byte char @@ -148,4 +149,55 @@ func Test_print_overlong() bwipe! endfunc +func Test_recording_with_select_mode_utf8() + call Run_test_recording_with_select_mode_utf8() +endfunc + +func Run_test_recording_with_select_mode_utf8() + new + + " No escaping + call feedkeys("qacc12345\gH哦\q", "tx") + call assert_equal("哦", getline(1)) + call assert_equal("cc12345\gH哦\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("哦", getline(1)) + + " 固 is 0xE5 0x9B 0xBA where 0x9B is CSI + call feedkeys("qacc12345\gH固\q", "tx") + call assert_equal("固", getline(1)) + call assert_equal("cc12345\gH固\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("固", getline(1)) + + " å›› is 0xE5 0x9B 0x9B where 0x9B is CSI + call feedkeys("qacc12345\gHå››\q", "tx") + call assert_equal("å››", getline(1)) + call assert_equal("cc12345\gHå››\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("å››", getline(1)) + + " 倒 is 0xE5 0x80 0x92 where 0x80 is K_SPECIAL + call feedkeys("qacc12345\gH倒\q", "tx") + call assert_equal("倒", getline(1)) + call assert_equal("cc12345\gH倒\", @a) + call setline(1, 'asdf') + normal! @a + call assert_equal("倒", getline(1)) + + bwipe! +endfunc + +" This must be done as one of the last tests, because it starts the GUI, which +" cannot be undone. +func Test_zz_recording_with_select_mode_utf8_gui() + CheckCanRunGui + + gui -f + call Run_test_recording_with_select_mode_utf8() +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 9bb9f175925a9bc467f39921bcc2861ff45eeaa1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 24 Jan 2022 13:01:35 +0800 Subject: vim-patch:8.2.3153: URLs with a dash in the scheme are not recognized Problem: URLs with a dash in the scheme are not recognized. Solution: Allow for a scheme with a dash, but not at the start or end. (Tsuyoshi CHO, closes vim/vim#8299) https://github.com/vim/vim/commit/7b7a118e74d25ff35cd277c2bb5191ae44bb20b2 --- src/nvim/path.c | 24 +++++++++++++++++++++--- src/nvim/testdir/test_buffer.vim | 31 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/path.c b/src/nvim/path.c index 674d67e21a..b12dd57938 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1743,14 +1743,32 @@ int path_is_url(const char *p) return 0; } -/// Check if "fname" starts with "name://". Return URL_SLASH if it does. +/// Check if "fname" starts with "name://" or "name:\\". /// /// @param fname is the filename to test -/// @return URL_BACKSLASH for "name:\\", zero otherwise. +/// @return URL_SLASH for "name://", URL_BACKSLASH for "name:\\", zero otherwise. int path_with_url(const char *fname) { const char *p; - for (p = fname; isalpha(*p); p++) {} + + // We accept alphabetic characters and a dash in scheme part. + // RFC 3986 allows for more, but it increases the risk of matching + // non-URL text. + + // first character must be alpha + if (!isalpha(*fname)) { + return 0; + } + + // check body: alpha or dash + for (p = fname; (isalpha(*p) || (*p == '-')); p++) {} + + // check last char is not a dash + if (p[-1] == '-') { + return 0; + } + + // "://" or ":\\" must follow return path_is_url(p); } diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim index 40111fdf06..a31cdbb49a 100644 --- a/src/nvim/testdir/test_buffer.vim +++ b/src/nvim/testdir/test_buffer.vim @@ -1,5 +1,7 @@ " Tests for Vim buffer +source check.vim + func Test_buffer_error() new foo1 new foo2 @@ -30,4 +32,33 @@ func Test_balt() call assert_equal('OtherBuffer', bufname()) endfunc +" Test for buffer match URL(scheme) check +" scheme is alpha and inner hyphen only. +func Test_buffer_scheme() + CheckMSWindows + + set noshellslash + %bwipe! + let bufnames = [ + \ #{id: 'b0', name: 'test://xyz/foo/b0' , match: 1}, + \ #{id: 'b1', name: 'test+abc://xyz/foo/b1', match: 0}, + \ #{id: 'b2', name: 'test_abc://xyz/foo/b2', match: 0}, + \ #{id: 'b3', name: 'test-abc://xyz/foo/b3', match: 1}, + \ #{id: 'b4', name: '-test://xyz/foo/b4' , match: 0}, + \ #{id: 'b5', name: 'test-://xyz/foo/b5' , match: 0}, + \] + for buf in bufnames + new `=buf.name` + if buf.match + call assert_equal(buf.name, getbufinfo(buf.id)[0].name) + else + " slashes will have become backslashes + call assert_notequal(buf.name, getbufinfo(buf.id)[0].name) + endif + bwipe + endfor + + set shellslash& +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From a4069a3eed65f14b1149c6cda8638dcb49ab5027 Mon Sep 17 00:00:00 2001 From: glacambre Date: Sun, 3 Oct 2021 16:19:25 +0200 Subject: feat(--headless): add on_print callback to stdioopen This commit adds an on_print callback to stdioopen's dictionary argument which lets the caller specify a function called each time neovim will try to output something to stdout (e.g. on "echo" or "echoerr" in --headless mode). --- src/nvim/channel.h | 2 ++ src/nvim/eval/funcs.c | 4 ++++ src/nvim/eval/typval.h | 3 ++- src/nvim/message.c | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 81b75e2d31..50d6b3600a 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -96,6 +96,8 @@ struct Channel { EXTERN PMap(uint64_t) channels INIT(= MAP_INIT); +EXTERN Callback on_print INIT(= CALLBACK_INIT); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "channel.h.generated.h" #endif diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 5252c940f7..6d43097ddd 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -10213,6 +10213,10 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) { return; } + if (!tv_dict_get_callback(opts, S_LEN("on_print"), &on_print)) { + return; + } + on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered"); if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) { on_stdin.self = opts; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index d1275d6512..ad01c01499 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -81,7 +81,8 @@ typedef struct { } data; CallbackType type; } Callback; -#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) +#define CALLBACK_INIT { .type = kCallbackNone } +#define CALLBACK_NONE ((Callback)CALLBACK_INIT) /// Structure holding dictionary watcher typedef struct dict_watcher { diff --git a/src/nvim/message.c b/src/nvim/message.c index befca8c76b..76aa863bde 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2647,6 +2647,17 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) char buf[7]; char *p; + if (on_print.type != kCallbackNone) { + typval_T argv[1]; + argv[0].v_type = VAR_STRING; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_string = (char_u *)str; + typval_T rettv = TV_INITIAL_VALUE; + callback_call(&on_print, 1, argv, &rettv); + tv_clear(&rettv); + return; + } + while ((maxlen < 0 || s - str < maxlen) && *s != NUL) { int len = utf_ptr2len((const char_u *)s); if (!(silent_mode && p_verbose == 0)) { -- cgit From 3d9ff675f8135be6ef17df4da07781cd05dbcd55 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 25 Jan 2022 07:37:28 +0800 Subject: vim-patch:8.2.4203: entering a character with CTRL-V may include modifiers Problem: Entering a character with CTRL-V may include modifiers. Solution: Reset "mod_mask" when entering a character with digits after CTRL-V. (closes vim/vim#9610) https://github.com/vim/vim/commit/502d8ae3e8ed8b6f8dd2ff175f154f9aa87228ef Commenting out test_override() as before. Commenting out part of CheckNotFeature() because Vim patch 8.2.0427 cannot be ported without breaking a lot of oldtests that check for removed features. --- src/nvim/edit.c | 10 ++++++++-- src/nvim/testdir/check.vim | 6 +++--- src/nvim/testdir/test_edit.vim | 23 +++++++++++++++++------ 3 files changed, 28 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a9c606ec13..f3226d4f6c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -5631,8 +5631,12 @@ int get_literal(void) i = 0; for (;;) { nc = plain_vgetc(); - if (!(State & CMDLINE) - && MB_BYTE2LEN_CHECK(nc) == 1) { + if ((mod_mask & ~MOD_MASK_SHIFT) != 0) { + // A character with non-Shift modifiers should not be a valid + // character for i_CTRL-V_digit. + break; + } + if (!(State & CMDLINE) && MB_BYTE2LEN_CHECK(nc) == 1) { add_to_showcmd(nc); } if (nc == 'x' || nc == 'X') { @@ -5698,6 +5702,8 @@ int get_literal(void) --no_mapping; if (nc) { vungetc(nc); + // A character typed with i_CTRL-V_digit cannot have modifiers. + mod_mask = 0; } got_int = false; // CTRL-C typed after CTRL-V is not an interrupt return cc; diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 14bab33a2f..ab26dddbe0 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -12,9 +12,9 @@ endfunc " Command to check for the absence of a feature. command -nargs=1 CheckNotFeature call CheckNotFeature() func CheckNotFeature(name) - if !has(a:name, 1) - throw 'Checking for non-existent feature ' .. a:name - endif + " if !has(a:name, 1) + " throw 'Checking for non-existent feature ' .. a:name + " endif if has(a:name) throw 'Skipped: ' .. a:name .. ' feature present' endif diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index fc4e80f0d6..c1f74e7675 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1006,16 +1006,16 @@ func Test_edit_DROP() endfunc func Test_edit_CTRL_V() - if has("ebcdic") - return - endif + CheckNotFeature ebcdic + new call setline(1, ['abc']) call cursor(2, 1) + " force some redraws set showmode showcmd - "call test_override_char_avail(1) - " call test_override('ALL', 1) + " call test_override('char_avail', 1) + call feedkeys("A\\\\\\\", 'tnix') call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$')) @@ -1028,8 +1028,19 @@ func Test_edit_CTRL_V() set norl endif - " call test_override('ALL', 0) set noshowmode showcmd + " call test_override('char_avail', 0) + + " No modifiers should be applied to the char typed using i_CTRL-V_digit. + call feedkeys(":append\\76c\76\\u3c0j\u3c0\\.\", 'tnix') + call assert_equal('LcLÏ€jÏ€', getline(2)) + + if has('osx') + " A char with a modifier should not be a valid char for i_CTRL-V_digit. + call feedkeys("o\\\\\\\\\\", 'tnix') + call assert_equal('', getline(3)) + endif + bw! endfunc -- cgit From dd21e21e9783293c168839e477078bb99b8c720f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 25 Jan 2022 09:44:37 +0800 Subject: fix: set RedrawingDisabled before entering aucmd_win --- src/nvim/autocmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 463bd5e0e6..94ac389139 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1160,7 +1160,10 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) // Prevent chdir() call in win_enter_ext(), through do_autochdir() int save_acd = p_acd; p_acd = false; + // no redrawing and don't set the window title + RedrawingDisabled++; win_enter(aucmd_win, false); + RedrawingDisabled--; p_acd = save_acd; unblock_autocmds(); curwin = aucmd_win; -- cgit From ecec957125ca95ef5fbc4534d62ed16cfedb0c44 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 25 Jan 2022 08:22:15 +0100 Subject: vim-patch:8.2.4196: various file types not recognized (#17182) Problem: Various file types not recognized. Solution: Add patterns to recognize more file types (closes vim/vim#9607) https://github.com/vim/vim/commit/428058ab3213e81531cbd7989f4267870f35d52e --- src/nvim/testdir/test_filetype.vim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 8930d62fd3..eb4824aa32 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -190,8 +190,11 @@ let s:filename_checks = { \ 'freebasic': ['file.fb'], \ 'fsharp': ['file.fs', 'file.fsi', 'file.fsx'], \ 'fstab': ['fstab', 'mtab'], + \ 'fusion': ['file.fusion'], \ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'], \ 'gdb': ['.gdbinit', 'gdbinit'], + \ 'gdresource': ['file.tscn', 'file.tres'], + \ 'gdscript': ['file.gd'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], \ 'gemtext': ['file.gmi', 'file.gemini'], @@ -202,28 +205,36 @@ let s:filename_checks = { \ 'gitrebase': ['git-rebase-todo'], \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], \ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'], + \ 'glsl': ['file.glsl'], \ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'], \ 'gnuplot': ['file.gpi', '.gnuplot'], \ 'go': ['file.go'], \ 'gomod': ['go.mod'], + \ 'gowork': ['go.work'], \ 'gp': ['file.gp', '.gprc'], \ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel', 'any/.gnupg/gpg.conf', 'any/.gnupg/options', 'any/usr/any/gnupg/options.skel'], \ 'grads': ['file.gs'], + \ 'graphql': ['file.graphql', 'file.graphqls', 'file.gql'], \ 'gretl': ['file.gretl'], \ 'groovy': ['file.gradle', 'file.groovy'], \ 'group': ['any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak', '/etc/group', '/etc/group-', '/etc/group.edit', '/etc/gshadow', '/etc/gshadow-', '/etc/gshadow.edit', '/var/backups/group.bak', '/var/backups/gshadow.bak'], \ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf', 'any/boot/grub/grub.conf', 'any/boot/grub/menu.lst', 'any/etc/grub.conf'], \ 'gsp': ['file.gsp'], \ 'gtkrc': ['.gtkrc', 'gtkrc', '.gtkrc-file', 'gtkrc-file'], + \ 'hack': ['file.hack', 'file.hackpartial'], \ 'haml': ['file.haml'], \ 'hamster': ['file.hsm'], + \ 'handlebars': ['file.hbs'], \ 'haskell': ['file.hs', 'file.hsc', 'file.hs-boot', 'file.hsig'], \ 'haste': ['file.ht'], \ 'hastepreproc': ['file.htpp'], \ 'hb': ['file.hb'], + \ 'hcl': ['file.hcl'], \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'], + \ 'heex': ['file.heex'], \ 'hex': ['file.hex', 'file.h32'], \ 'hgcommit': ['hg-editor-file.txt'], + \ 'hjson': ['file.hjson'], \ 'hog': ['file.hog', 'snort.conf', 'vision.conf'], \ 'hollywood': ['file.hws'], \ 'hostconf': ['/etc/host.conf', 'any/etc/host.conf'], @@ -279,6 +290,7 @@ let s:filename_checks = { \ 'latte': ['file.latte', 'file.lte'], \ 'ld': ['file.ld'], \ 'ldif': ['file.ldif'], + \ 'ledger': ['file.ldg', 'file.ledger', 'file.journal'], \ 'less': ['file.less'], \ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'], \ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc', 'lftp/rc', 'some-lftp/rc'], @@ -352,6 +364,7 @@ let s:filename_checks = { \ 'netrc': ['.netrc'], \ 'nginx': ['file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'], \ 'ninja': ['file.ninja'], + \ 'nix': ['file.nix'], \ 'nqc': ['file.nqc'], \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'], \ 'nsis': ['file.nsi', 'file.nsh'], @@ -394,6 +407,7 @@ let s:filename_checks = { \ 'ppd': ['file.ppd'], \ 'ppwiz': ['file.it', 'file.ih'], \ 'privoxy': ['file.action'], + \ 'prisma': ['file.prisma'], \ 'proc': ['file.pc'], \ 'procmail': ['.procmail', '.procmailrc'], \ 'prolog': ['file.pdb'], @@ -404,10 +418,12 @@ let s:filename_checks = { \ 'ps1xml': ['file.ps1xml'], \ 'psf': ['file.psf'], \ 'psl': ['file.psl'], + \ 'pug': ['file.pug'], \ 'puppet': ['file.pp'], \ 'pyret': ['file.arr'], \ 'pyrex': ['file.pyx', 'file.pxd'], \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], + \ 'ql': ['file.ql', 'file.qll'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], \ 'radiance': ['file.rad', 'file.mat'], \ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'], @@ -489,6 +505,7 @@ let s:filename_checks = { \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], + \ 'surface': ['file.sface'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], \ 'swift': ['file.swift'], @@ -502,8 +519,10 @@ let s:filename_checks = { \ 'taskdata': ['pending.data', 'completed.data', 'undo.data'], \ 'taskedit': ['file.task'], \ 'tcl': ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc'], + \ 'teal': ['file.tl'], \ 'teraterm': ['file.ttl'], \ 'terminfo': ['file.ti'], + \ 'terraform': ['file.tfvars'], \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], @@ -511,6 +530,7 @@ let s:filename_checks = { \ 'tf': ['file.tf', '.tfrc', 'tfrc'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], + \ 'tla': ['file.tla'], \ 'tli': ['file.tli'], \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf', '.tmux-file.conf', '.tmux.conf', 'tmux-file.conf', 'tmux.conf', 'tmux.conf.local'], \ 'toml': ['file.toml', 'Gopkg.lock', 'Pipfile', '/home/user/.cargo/config'], @@ -571,6 +591,7 @@ let s:filename_checks = { \ 'xslt': ['file.xsl', 'file.xslt'], \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], \ 'yaml': ['file.yaml', 'file.yml'], + \ 'yang': ['file.yang'], \ 'raml': ['file.raml'], \ 'z8a': ['file.z8a'], \ 'zig': ['file.zig'], -- cgit From d11bbacf0ffa45096364195bda4739984d25121e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 25 Jan 2022 16:18:58 +0800 Subject: fix(inccommand): do not change reg_prev_sub when previewing --- src/nvim/ex_cmds.c | 10 ++++++++++ src/nvim/regexp.c | 15 ++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 95390b1a70..66e9738f98 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3629,8 +3629,14 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle // We do it here once to avoid it to be replaced over and over again. // But don't do it when it starts with "\=", then it's an expression. assert(sub != NULL); + + bool sub_needs_free = false; if (!(sub[0] == '\\' && sub[1] == '=')) { + char_u *source = sub; sub = regtilde(sub, p_magic); + // When previewing, the new pattern allocated by regtilde() needs to be freed + // in this function because it will not be used or freed by regtilde() later. + sub_needs_free = preview && sub != source; } // Check for a match on each line. @@ -4425,6 +4431,10 @@ skip: kv_destroy(preview_lines.subresults); + if (sub_needs_free) { + xfree(sub); + } + return preview_buf; #undef ADJUST_SUB_FIRSTLNUM #undef PUSH_PREVIEW_LINES diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 45e580dbee..9bea54ad2c 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -6538,11 +6538,16 @@ char_u *regtilde(char_u *source, int magic) } } - xfree(reg_prev_sub); - if (newsub != source) /* newsub was allocated, just keep it */ - reg_prev_sub = newsub; - else /* no ~ found, need to save newsub */ - reg_prev_sub = vim_strsave(newsub); + // Only change reg_prev_sub when not previewing. + if (!(State & CMDPREVIEW)) { + xfree(reg_prev_sub); + if (newsub != source) { // newsub was allocated, just keep it + reg_prev_sub = newsub; + } else { // no ~ found, need to save newsub + reg_prev_sub = vim_strsave(newsub); + } + } + return newsub; } -- cgit From b7dc4491a962af59b8823b00c653aeeee15c110e Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Tue, 25 Jan 2022 12:44:33 +0100 Subject: refactor: include missing assert header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will solve the "implicit declaration of function ‘assert’" warning when running "make lint". --- src/nvim/marktree.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 95d63dd14a..30f5aacebc 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -2,6 +2,7 @@ #define NVIM_MARKTREE_H #include +#include #include "nvim/assert.h" #include "nvim/garray.h" -- cgit From de673966c3b681e8354a26a1f054fbda6e07294a Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 26 Jan 2022 16:36:05 +0100 Subject: vim-patch:8.2.4214: illegal memory access with large 'tabstop' in Ex mode Problem: Illegal memory access with large 'tabstop' in Ex mode. Solution: Allocate enough memory. https://github.com/vim/vim/commit/85b6747abc15a7a81086db31289cf1b8b17e6cb1 --- src/nvim/ex_getln.c | 2 +- src/nvim/testdir/test_ex_mode.vim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 78b8e43e65..fd75cfc7f8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -772,7 +772,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.cmdindent = (s->firstc > 0 ? s->indent : 0); // alloc initial ccline.cmdbuff - alloc_cmdbuff(exmode_active ? 250 : s->indent + 1); + alloc_cmdbuff(indent + 50); ccline.cmdlen = ccline.cmdpos = 0; ccline.cmdbuff[0] = NUL; diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 92e0559618..78663f7deb 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -98,4 +98,14 @@ func Test_ex_mode_count_overflow() call delete('Xexmodescript') endfunc +func Test_ex_mode_large_indent() + new + set ts=500 ai + call setline(1, "\t") + exe "normal gQi\." + set ts=8 noai + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From b2b288f33c7a8e780654d5f883dfc2948ff7edc3 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 26 Jan 2022 16:37:25 +0100 Subject: vim-patch:8.2.4215: illegal memory access when copying lines in Visual mode Problem: Illegal memory access when copying lines in Visual mode. Solution: Adjust the Visual position after copying lines. https://github.com/vim/vim/commit/dc5490e2cbc8c16022a23b449b48c1bd0083f366 --- src/nvim/ex_cmds.c | 3 +++ src/nvim/testdir/test_visual.vim | 11 +++++++++++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 95390b1a70..ca5e14ee63 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1099,6 +1099,9 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) } appended_lines_mark(n, count); + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } msgmore((long)count); } diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index e1e7765e06..5fb4934a1d 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1151,5 +1151,16 @@ func Test_visual_reselect_with_count() call delete('XvisualReselect') endfunc +" this was leaving the end of the Visual area beyond the end of a line +func Test_visual_ex_copy_line() + new + call setline(1, ["aaa", "bbbbbbbbbxbb"]) + /x + exe "normal ggvjfxO" + t0 + normal gNU + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From e9247b0d5da8f2158b8a64c736919faa7d708519 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 26 Jan 2022 16:40:54 +0100 Subject: vim-patch:8.2.4217: illegal memory access when undo makes Visual area invalid Problem: Illegal memory access when undo makes Visual area invalid. Solution: Correct the Visual area after undo. https://github.com/vim/vim/commit/8d02ce1ed75d008c34a5c9aaa51b67cbb9d33baa vim-patch:8.2.4218: illegal memory access with bracketed paste in Ex mode (N/A) --- src/nvim/testdir/test_visual.vim | 15 +++++++++++++++ src/nvim/undo.c | 4 ++++ 2 files changed, 19 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 5fb4934a1d..8520c8e900 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1162,5 +1162,20 @@ func Test_visual_ex_copy_line() bwipe! endfunc +" This was leaving the end of the Visual area beyond the end of a line. +" Set 'undolevels' to start a new undo block. +func Test_visual_undo_deletes_last_line() + new + call setline(1, ["aaa", "ccc", "dyd"]) + set undolevels=100 + exe "normal obbbbbbbbbxbb\" + set undolevels=100 + /y + exe "normal ggvjfxO" + undo + normal gNU + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/undo.c b/src/nvim/undo.c index d18f35a43a..2d8df4cad8 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2633,6 +2633,10 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet) } } + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } + smsg_attr_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"), u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount, -- cgit From 540264306b6340bdd8133cd3307b169f7708c4d6 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 26 Jan 2022 16:47:39 +0100 Subject: vim-patch:8.2.4219: reading before the start of the line Problem: Reading before the start of the line. Solution: Check boundary before trying to read the character. https://github.com/vim/vim/commit/44db8213d38c39877d2148eff6a72f4beccfb94e --- src/nvim/ops.c | 2 +- src/nvim/testdir/test_visual.vim | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 2bc92ce295..b2554cbf0d 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2801,7 +2801,7 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, size_t y_idx, if (exclude_trailing_space) { int s = bd->textlen + bd->endspaces; - while (ascii_iswhite(*(bd->textstart + s - 1)) && s > 0) { + while (s > 0 && ascii_iswhite(*(bd->textstart + s - 1))) { s = s - utf_head_off(bd->textstart, bd->textstart + s - 1) - 1; pnew--; } diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 8520c8e900..b087c88c35 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1103,6 +1103,13 @@ func Test_visual_put_blockedit_zy_and_zp() bw! endfunc +func Test_visual_block_yank_zy() + new + " this was reading before the start of the line + exe "norm o\\\zy" + bwipe! +endfunc + func Test_visual_block_with_virtualedit() CheckScreendump -- cgit From 81950af22d71a42c17ec9ae3f25b69025657cbff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Jan 2022 07:24:25 +0800 Subject: refactor: allocate an empty string as unused orig_rhs for Lua mappings --- src/nvim/getchar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ef590adb3a..0b47c5e1b0 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2640,9 +2640,10 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, } } else { char tmp_buf[64]; + // orig_rhs is not used for Lua mappings, but still needs to be a string. + mapargs->orig_rhs = xcalloc(1, sizeof(char_u)); + mapargs->orig_rhs_len = 0; // stores ref_no in map_str - mapargs->orig_rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%d", rhs_lua); - mapargs->orig_rhs = vim_strsave((char_u *)tmp_buf); mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL, (char_u)KEY2TERMCAP0(K_LUA), KEY2TERMCAP1(K_LUA), rhs_lua); -- cgit From a9f112ce3a076e9405a3c18fa608747d56c64726 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Jan 2022 15:53:00 +0800 Subject: vim-patch:8.2.4133: output of ":scriptnames" goes into the message history Problem: output of ":scriptnames" goes into the message history, while this des not happen for other commands, such as ":ls". Solution: Use msg_outtrans() instead of smsg(). (closes vim/vim#9551) https://github.com/vim/vim/commit/840f16202e1ae2d574507ef52a7e8a98775f243c --- src/nvim/ex_cmds2.c | 8 +++++--- src/nvim/testdir/test_scriptnames.vim | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 267f5616f5..846789233f 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2323,9 +2323,11 @@ void ex_scriptnames(exarg_T *eap) for (int i = 1; i <= script_items.ga_len && !got_int; i++) { if (SCRIPT_ITEM(i).sn_name != NULL) { - home_replace(NULL, SCRIPT_ITEM(i).sn_name, - NameBuff, MAXPATHL, true); - smsg("%3d: %s", i, NameBuff); + home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true); + vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff); + msg_putchar('\n'); + msg_outtrans(IObuff); + line_breakcheck(); } } } diff --git a/src/nvim/testdir/test_scriptnames.vim b/src/nvim/testdir/test_scriptnames.vim index fc6c910bfa..44ec146666 100644 --- a/src/nvim/testdir/test_scriptnames.vim +++ b/src/nvim/testdir/test_scriptnames.vim @@ -23,4 +23,10 @@ func Test_scriptnames() bwipe call delete('Xscripting') + + let msgs = execute('messages') + scriptnames + call assert_equal(msgs, execute('messages')) endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 5864edac7be49a0cd0080a65330dd7661ffb5641 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 29 Dec 2021 02:01:02 +0000 Subject: vim-patch:8.1.2302: :lockmarks does not work for '[ and '] Problem: :lockmarks does not work for '[ and ']. Solution: save and restore '[ and '] marks. (James McCoy, closes vim/vim#5222) https://github.com/vim/vim/commit/f4a1d1c0542df151bc59ac3b798ed198b5c71ccc Test_diff_maintains_change_mark doesn't actually fail without these changes. This is fixed in v8.2.3936. --- src/nvim/diff.c | 5 ++ src/nvim/ex_cmds.c | 60 ++++++++++++------- src/nvim/fileio.c | 48 +++++++++------- src/nvim/ops.c | 114 ++++++++++++++++++++++--------------- src/nvim/testdir/test_autocmd.vim | 34 +++++++++++ src/nvim/testdir/test_diffmode.vim | 19 +++++++ 6 files changed, 197 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 340fec230c..233753839b 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -790,9 +790,14 @@ static int diff_write(buf_T *buf, diffin_T *din) // Always use 'fileformat' set to "unix". char_u *save_ff = buf->b_p_ff; buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + const bool save_lockmarks = cmdmod.lockmarks; + // Writing the buffer is an implementation detail of performing the diff, + // so it shouldn't update the '[ and '] marks. + cmdmod.lockmarks = true; int r = buf_write(buf, din->din_fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, NULL, false, false, false, true); + cmdmod.lockmarks = save_lockmarks; free_string_option(buf->b_p_ff); buf->b_p_ff = save_ff; return r; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ca5e14ee63..9f30f98c07 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -980,8 +980,10 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) foldMoveRange(win, &win->w_folds, line1, line2, dest); } } - curbuf->b_op_start.lnum = dest - num_lines + 1; - curbuf->b_op_end.lnum = dest; + if (!cmdmod.lockmarks) { + curbuf->b_op_start.lnum = dest - num_lines + 1; + curbuf->b_op_end.lnum = dest; + } line_off = -num_lines; byte_off = -extent_byte; } else { @@ -991,10 +993,14 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) foldMoveRange(win, &win->w_folds, dest + 1, line1 - 1, line2); } } - curbuf->b_op_start.lnum = dest + 1; - curbuf->b_op_end.lnum = dest + num_lines; + if (!cmdmod.lockmarks) { + curbuf->b_op_start.lnum = dest + 1; + curbuf->b_op_end.lnum = dest + num_lines; + } + } + if (!cmdmod.lockmarks) { + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; } - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust_nofold(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L, kExtmarkNOOP); @@ -1057,9 +1063,11 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) char_u *p; count = line2 - line1 + 1; - curbuf->b_op_start.lnum = n + 1; - curbuf->b_op_end.lnum = n + count; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) { + curbuf->b_op_start.lnum = n + 1; + curbuf->b_op_end.lnum = n + count; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + } /* * there are three situations: @@ -1272,12 +1280,18 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, char_u *cmd_buf; buf_T *old_curbuf = curbuf; int shell_flags = 0; + const pos_T orig_start = curbuf->b_op_start; + const pos_T orig_end = curbuf->b_op_end; const int stmp = p_stmp; if (*cmd == NUL) { // no filter command return; } + const bool save_lockmarks = cmdmod.lockmarks; + // Temporarily disable lockmarks since that's needed to propagate changed + // regions of the buffer for foldUpdate(), linecount, etc. + cmdmod.lockmarks = false; cursor_save = curwin->w_cursor; linecount = line2 - line1 + 1; @@ -1458,10 +1472,15 @@ error: filterend: + cmdmod.lockmarks = save_lockmarks; if (curbuf != old_curbuf) { no_wait_return--; emsg(_("E135: *Filter* Autocommands must not change current buffer")); + } else if (cmdmod.lockmarks) { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; } + if (itmp != NULL) { os_remove((char *)itmp); } @@ -3034,14 +3053,15 @@ void ex_append(exarg_T *eap) // eap->line2 pointed to the end of the buffer and nothing was appended) // "end" is set to lnum when something has been appended, otherwise // it is the same as "start" -- Acevedo - curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? - eap->line2 + 1 : curbuf->b_ml.ml_line_count; - if (eap->cmdidx != CMD_append) { - --curbuf->b_op_start.lnum; + if (!cmdmod.lockmarks) { + curbuf->b_op_start.lnum + = (eap->line2 < curbuf->b_ml.ml_line_count) ? eap->line2 + 1 : curbuf->b_ml.ml_line_count; + if (eap->cmdidx != CMD_append) { + curbuf->b_op_start.lnum--; + } + curbuf->b_op_end.lnum = (eap->line2 < lnum) ? lnum : curbuf->b_op_start.lnum; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; } - curbuf->b_op_end.lnum = (eap->line2 < lnum) - ? lnum : curbuf->b_op_start.lnum; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_SOL | BL_FIX); @@ -4354,10 +4374,12 @@ skip: } if (sub_nsubs > start_nsubs) { - // Set the '[ and '] marks. - curbuf->b_op_start.lnum = eap->line1; - curbuf->b_op_end.lnum = line2; - curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) { + // Set the '[ and '] marks. + curbuf->b_op_start.lnum = eap->line1; + curbuf->b_op_end.lnum = line2; + curbuf->b_op_start.col = curbuf->b_op_end.col = 0; + } if (!global_busy) { // when interactive leave cursor on the match diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f8cf341836..f6d37adf89 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -242,6 +242,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski bool notconverted = false; // true if conversion wanted but it wasn't possible char_u conv_rest[CONV_RESTLEN]; int conv_restlen = 0; // nr of bytes in conv_rest[] + pos_T orig_start; buf_T *old_curbuf; char_u *old_b_ffname; char_u *old_b_fname; @@ -298,14 +299,10 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski fname = sfname; #endif - /* - * The BufReadCmd and FileReadCmd events intercept the reading process by - * executing the associated commands instead. - */ + // The BufReadCmd and FileReadCmd events intercept the reading process by + // executing the associated commands instead. if (!filtering && !read_stdin && !read_buffer) { - pos_T pos; - - pos = curbuf->b_op_start; + orig_start = curbuf->b_op_start; // Set '[ mark to the line above where the lines go (line 1 if zero). curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); @@ -335,7 +332,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski return aborting() ? FAIL : OK; } - curbuf->b_op_start = pos; + curbuf->b_op_start = orig_start; } if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) { @@ -576,9 +573,8 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski ++no_wait_return; // don't wait for return yet - /* - * Set '[ mark to the line above where the lines go (line 1 if zero). - */ + // Set '[ mark to the line above where the lines go (line 1 if zero). + orig_start = curbuf->b_op_start; curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); curbuf->b_op_start.col = 0; @@ -618,6 +614,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski try_mac = (vim_strchr(p_ffs, 'm') != NULL); try_dos = (vim_strchr(p_ffs, 'd') != NULL); try_unix = (vim_strchr(p_ffs, 'x') != NULL); + curbuf->b_op_start = orig_start; if (msg_scrolled == n) { msg_scroll = m; @@ -1888,13 +1885,13 @@ failed: check_cursor_lnum(); beginline(BL_WHITE | BL_FIX); // on first non-blank - /* - * Set '[ and '] marks to the newly read lines. - */ - curbuf->b_op_start.lnum = from + 1; - curbuf->b_op_start.col = 0; - curbuf->b_op_end.lnum = from + linecnt; - curbuf->b_op_end.col = 0; + if (!cmdmod.lockmarks) { + // Set '[ and '] marks to the newly read lines. + curbuf->b_op_start.lnum = from + 1; + curbuf->b_op_start.col = 0; + curbuf->b_op_end.lnum = from + linecnt; + curbuf->b_op_end.col = 0; + } } msg_scroll = msg_save; @@ -2252,6 +2249,8 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ int write_undo_file = FALSE; context_sha256_T sha_ctx; unsigned int bkc = get_bkc_value(buf); + const pos_T orig_start = buf->b_op_start; + const pos_T orig_end = buf->b_op_end; if (fname == NULL || *fname == NUL) { // safety check return FAIL; @@ -2432,7 +2431,13 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline) || did_cmd || nofile_err || aborting()) { - --no_wait_return; + if (buf != NULL && cmdmod.lockmarks) { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } + + no_wait_return--; msg_scroll = msg_save; if (nofile_err) { emsg(_("E676: No matching autocommands for acwrite buffer")); @@ -2513,6 +2518,11 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ } } + if (cmdmod.lockmarks) { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } if (shortmess(SHM_OVER) && !exiting) { msg_scroll = FALSE; // overwrite previous file message diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e66935db7e..83a7c31991 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -267,14 +267,14 @@ void op_shift(oparg_T *oap, int curs_top, int amount) msg_attr_keep((char *)IObuff, 0, true, false); } - /* - * Set "'[" and "']" marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); - if (curbuf->b_op_end.col > 0) { - curbuf->b_op_end.col--; + if (!cmdmod.lockmarks) { + // Set "'[" and "']" marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end.lnum = oap->end.lnum; + curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); + if (curbuf->b_op_end.col > 0) { + curbuf->b_op_end.col--; + } } changed_lines(oap->start.lnum, 0, oap->end.lnum + 1, 0L, true); @@ -694,9 +694,11 @@ void op_reindent(oparg_T *oap, Indenter how) "%" PRId64 " lines indented ", i), (int64_t)i); } - // set '[ and '] marks - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) { + // set '[ and '] marks + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } } /* @@ -1736,13 +1738,15 @@ int op_delete(oparg_T *oap) msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: - if (oap->motion_type == kMTBlockWise) { - curbuf->b_op_end.lnum = oap->end.lnum; - curbuf->b_op_end.col = oap->start.col; - } else { - curbuf->b_op_end = oap->start; + if (!cmdmod.lockmarks) { + if (oap->motion_type == kMTBlockWise) { + curbuf->b_op_end.lnum = oap->end.lnum; + curbuf->b_op_end.col = oap->start.col; + } else { + curbuf->b_op_end = oap->start; + } + curbuf->b_op_start = oap->start; } - curbuf->b_op_start = oap->start; return OK; } @@ -2007,9 +2011,11 @@ static int op_replace(oparg_T *oap, int c) check_cursor(); changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L, true); - // Set "'[" and "']" marks. - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) { + // Set "'[" and "']" marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } return OK; } @@ -2078,11 +2084,11 @@ void op_tilde(oparg_T *oap) redraw_curbuf_later(INVERTED); } - /* - * Set '[ and '] marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; + if (!cmdmod.lockmarks) { + // Set '[ and '] marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + } if (oap->line_count > p_report) { smsg(NGETTEXT("%" PRId64 " line changed", @@ -2774,14 +2780,14 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } } - /* - * Set "'[" and "']" marks. - */ - curbuf->b_op_start = oap->start; - curbuf->b_op_end = oap->end; - if (yank_type == kMTLineWise) { - curbuf->b_op_start.col = 0; - curbuf->b_op_end.col = MAXCOL; + if (!cmdmod.lockmarks) { + // Set "'[" and "']" marks. + curbuf->b_op_start = oap->start; + curbuf->b_op_end = oap->end; + if (yank_type == kMTLineWise) { + curbuf->b_op_start.col = 0; + curbuf->b_op_end.col = MAXCOL; + } } return; @@ -2914,6 +2920,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) char_u *insert_string = NULL; bool allocated = false; long cnt; + const pos_T orig_start = curbuf->b_op_start; + const pos_T orig_end = curbuf->b_op_end; unsigned int cur_ve_flags = get_ve_flags(); if (flags & PUT_FIXINDENT) { @@ -3618,6 +3626,10 @@ error: curwin->w_set_curswant = TRUE; end: + if (cmdmod.lockmarks) { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } if (allocated) { xfree(insert_string); } @@ -3964,7 +3976,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions // and setup the array of space strings lengths for (t = 0; t < (linenr_T)count; t++) { curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t)); - if (t == 0 && setmark) { + if (t == 0 && setmark && !cmdmod.lockmarks) { // Set the '[ mark. curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum; curwin->w_buffer->b_op_start.col = (colnr_T)STRLEN(curr); @@ -4085,7 +4097,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions ml_replace(curwin->w_cursor.lnum, newp, false); - if (setmark) { + if (setmark && !cmdmod.lockmarks) { // Set the '] mark. curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum; curwin->w_buffer->b_op_end.col = sumsize; @@ -4223,8 +4235,10 @@ static void op_format(oparg_T *oap, int keep_cursor) redraw_curbuf_later(INVERTED); } - // Set '[ mark at the start of the formatted area - curbuf->b_op_start = oap->start; + if (!cmdmod.lockmarks) { + // Set '[ mark at the start of the formatted area + curbuf->b_op_start = oap->start; + } // For "gw" remember the cursor position and put it back below (adjusted // for joined and split lines). @@ -4246,8 +4260,10 @@ static void op_format(oparg_T *oap, int keep_cursor) old_line_count = curbuf->b_ml.ml_line_count - old_line_count; msgmore(old_line_count); - // put '] mark on the end of the formatted area - curbuf->b_op_end = curwin->w_cursor; + if (!cmdmod.lockmarks) { + // put '] mark on the end of the formatted area + curbuf->b_op_end = curwin->w_cursor; + } if (keep_cursor) { curwin->w_cursor = saved_cursor; @@ -4845,7 +4861,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd) // Set '[ mark if something changed. Keep the last end // position from do_addsub(). - if (change_cnt > 0) { + if (change_cnt > 0 && !cmdmod.lockmarks) { curbuf->b_op_start = startpos; } @@ -5199,11 +5215,13 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1) } } - // set the '[ and '] marks - curbuf->b_op_start = startpos; - curbuf->b_op_end = endpos; - if (curbuf->b_op_end.col > 0) { - curbuf->b_op_end.col--; + if (!cmdmod.lockmarks) { + // set the '[ and '] marks + curbuf->b_op_start = startpos; + curbuf->b_op_end = endpos; + if (curbuf->b_op_end.col > 0) { + curbuf->b_op_end.col--; + } } theend: @@ -6016,6 +6034,8 @@ static void op_function(const oparg_T *oap) { const TriState save_virtual_op = virtual_op; const bool save_finish_op = finish_op; + const pos_T orig_start = curbuf->b_op_start; + const pos_T orig_end = curbuf->b_op_end; if (*p_opfunc == NUL) { emsg(_("E774: 'operatorfunc' is empty")); @@ -6049,6 +6069,10 @@ static void op_function(const oparg_T *oap) virtual_op = save_virtual_op; finish_op = save_finish_op; + if (cmdmod.lockmarks) { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } } } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 45285b69a1..231ab2acf1 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2380,6 +2380,40 @@ func Test_autocmd_was_using_freed_memory() pclose endfunc +func Test_BufWrite_lockmarks() + edit! Xtest + call setline(1, ['a', 'b', 'c', 'd']) + + " :lockmarks preserves the marks + call SetChangeMarks(2, 3) + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + " *WritePre autocmds get the correct line range, but lockmarks preserves the + " original values for the user + augroup lockmarks + au! + au BufWritePre,FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")]) + au FileWritePre * call assert_equal([3, 4], [line("'["), line("']")]) + augroup END + + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + if executable('cat') + lockmarks %!cat + call assert_equal([2, 3], [line("'["), line("']")]) + endif + + lockmarks 3,4write Xtest2 + call assert_equal([2, 3], [line("'["), line("']")]) + + au! lockmarks + augroup! lockmarks + call delete('Xtest') + call delete('Xtest2') +endfunc + " FileChangedShell tested in test_filechanged.vim func LogACmd() diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 3a0c615cf6..fe928c0f65 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1146,6 +1146,25 @@ func Test_diff_followwrap() bwipe! endfunc +func Test_diff_maintains_change_mark() + enew! + call setline(1, ['a', 'b', 'c', 'd']) + diffthis + new + call setline(1, ['a', 'b', 'c', 'e']) + " Set '[ and '] marks + 2,3yank + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by the implicit diff + diffthis + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by an explicit diff + diffupdate + call assert_equal([2, 3], [line("'["), line("']")]) + bwipe! + bwipe! +endfunc + func Test_diff_rnu() CheckScreendump -- cgit From e8af051f1bfa0791a294ab3725dce1dca1cd8d6b Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 14 Jan 2022 15:05:48 +0000 Subject: test(oldtest): partially port v8.1.2381 Problem: Not all register related code is covered by tests. Solution: Add more test cases. (Yegappan Lakshmanan, closes vim/vim#5301) https://github.com/vim/vim/commit/54c8d229f54e36e89fcd5d84e523fd894d018024 Can't be fully ported until "set clipboard=autoselect,autoselectplus" is re-implemented for Test_clipboard_regs (and last visual selection to PRIMARY selection works). --- src/nvim/testdir/test_marks.vim | 17 ++++++ src/nvim/testdir/test_registers.vim | 101 +++++++++++++++++++++++++++++--- src/nvim/testdir/test_virtualedit.vim | 105 ++++++++++++++++++++++++++++++---- 3 files changed, 204 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index b3035d73ce..4ef42946cb 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -207,6 +207,21 @@ func Test_mark_error() call assert_fails('mark _', 'E191:') endfunc +" Test for :lockmarks when pasting content +func Test_lockmarks_with_put() + new + call append(0, repeat(['sky is blue'], 4)) + normal gg + 1,2yank r + put r + normal G + lockmarks put r + call assert_equal(2, line("'[")) + call assert_equal(3, line("']")) + + bwipe! +endfunc + " Test for the getmarklist() function func Test_getmarklist() new @@ -231,3 +246,5 @@ func Test_getmarklist() call assert_equal([], {}->getmarklist()) close! endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 4371828a72..d0bafe47b5 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -174,15 +174,6 @@ func Test_get_register() call assert_equal('', getregtype('!')) - " Test for clipboard registers (* and +) - if has("clipboard_working") - call append(0, "text for clipboard test") - normal gg"*yiw - call assert_equal('text', getreg('*')) - normal gg2w"+yiw - call assert_equal('clipboard', getreg('+')) - endif - " Test for inserting an invalid register content call assert_beeps('exe "normal i\!"') @@ -238,9 +229,101 @@ func Test_set_register() call feedkeys('qRhhq', 'xt') call assert_equal('llhh', getreg('r')) + " Appending a list of characters to a register from different lines + let @r = '' + call append(0, ['abcdef', '123456']) + normal gg"ry3l + call cursor(2, 4) + normal "Ry3l + call assert_equal('abc456', @r) + + " Test for gP with multiple lines selected using characterwise motion + %delete + call append(0, ['vim editor', 'vim editor']) + let @r = '' + exe "normal ggwy/vim /e\gP" + call assert_equal(['vim editor', 'vim editor', 'vim editor'], getline(1, 3)) + + " Test for gP with . register + %delete + normal iabc + normal ".gp + call assert_equal('abcabc', getline(1)) + normal 0".gP + call assert_equal('abcabcabc', getline(1)) + enew! endfunc +" Test for clipboard registers (* and +) +func Test_clipboard_regs() + throw 'skipped: needs clipboard=autoselect,autoselectplus' + + CheckNotGui + CheckFeature clipboard_working + + new + call append(0, "text for clipboard test") + normal gg"*yiw + call assert_equal('text', getreg('*')) + normal gg2w"+yiw + call assert_equal('clipboard', getreg('+')) + + " Test for replacing the clipboard register contents + set clipboard=unnamed + let @* = 'food' + normal ggviw"*p + call assert_equal('text', getreg('*')) + call assert_equal('food for clipboard test', getline(1)) + normal ggviw"*p + call assert_equal('food', getreg('*')) + call assert_equal('text for clipboard test', getline(1)) + + " Test for replacing the selection register contents + set clipboard=unnamedplus + let @+ = 'food' + normal ggviw"+p + call assert_equal('text', getreg('+')) + call assert_equal('food for clipboard test', getline(1)) + normal ggviw"+p + call assert_equal('food', getreg('+')) + call assert_equal('text for clipboard test', getline(1)) + + " Test for auto copying visually selected text to clipboard register + call setline(1, "text for clipboard test") + let @* = '' + set clipboard=autoselect + normal ggwwviwy + call assert_equal('clipboard', @*) + + " Test for auto copying visually selected text to selection register + let @+ = '' + set clipboard=autoselectplus + normal ggwviwy + call assert_equal('for', @+) + + set clipboard&vim + bwipe! +endfunc + +" Test for restarting the current mode (insert or virtual replace) after +" executing the contents of a register +func Test_put_reg_restart_mode() + new + call append(0, 'editor') + normal gg + let @r = "ivim \" + call feedkeys("i\@r\=mode()\", 'xt') + call assert_equal('vimi editor', getline(1)) + + call setline(1, 'editor') + normal gg + call feedkeys("gR\@r\=mode()\", 'xt') + call assert_equal('vimReditor', getline(1)) + + bwipe! +endfunc + func Test_v_register() enew call setline(1, 'nothing') diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim index d2a5258bd3..250b896532 100644 --- a/src/nvim/testdir/test_virtualedit.vim +++ b/src/nvim/testdir/test_virtualedit.vim @@ -84,42 +84,127 @@ func Test_edit_change() set virtualedit= endfunc -" Test for pasting before and after a tab character +" Tests for pasting at the beginning, end and middle of a tab character +" in virtual edit mode. func Test_paste_in_tab() new - let @" = 'xyz' + call append(0, '') set virtualedit=all - call append(0, "a\tb") + + " Tests for pasting a register with characterwise mode type + call setreg('"', 'xyz', 'c') + + " paste (p) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal p + call assert_equal('a xyz b', getline(1)) + + " paste (P) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal P + call assert_equal("axyz\tb", getline(1)) + + " paste (p) unnamed register at the end of a tab + call setline(1, "a\tb") call cursor(1, 2, 6) normal p call assert_equal("a\txyzb", getline(1)) + + " paste (P) unnamed register at the end of a tab call setline(1, "a\tb") - call cursor(1, 2) + call cursor(1, 2, 6) normal P - call assert_equal("axyz\tb", getline(1)) + call assert_equal('a xyz b', getline(1)) - " Test for virtual block paste + " Tests for pasting a register with blockwise mode type call setreg('"', 'xyz', 'b') + + " paste (p) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal p + call assert_equal('a xyz b', getline(1)) + + " paste (P) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal P + call assert_equal("axyz\tb", getline(1)) + + " paste (p) unnamed register at the end of a tab call setline(1, "a\tb") call cursor(1, 2, 6) normal p call assert_equal("a\txyzb", getline(1)) + + " paste (P) unnamed register at the end of a tab call setline(1, "a\tb") call cursor(1, 2, 6) normal P - call assert_equal("a xyz b", getline(1)) + call assert_equal('a xyz b', getline(1)) - " Test for virtual block paste with gp and gP + " Tests for pasting with gp and gP in virtual edit mode + + " paste (gp) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal gp + call assert_equal('a xyz b', getline(1)) + call assert_equal([0, 1, 12, 0, 12], getcurpos()) + + " paste (gP) unnamed register at the beginning of a tab + call setline(1, "a\tb") + call cursor(1, 2, 0) + normal gP + call assert_equal("axyz\tb", getline(1)) + call assert_equal([0, 1, 5, 0, 5], getcurpos()) + + " paste (gp) unnamed register at the end of a tab call setline(1, "a\tb") call cursor(1, 2, 6) normal gp call assert_equal("a\txyzb", getline(1)) call assert_equal([0, 1, 6, 0, 12], getcurpos()) + + " paste (gP) unnamed register at the end of a tab call setline(1, "a\tb") call cursor(1, 2, 6) normal gP - call assert_equal("a xyz b", getline(1)) - call assert_equal([0, 1, 12, 0 ,12], getcurpos()) + call assert_equal('a xyz b', getline(1)) + call assert_equal([0, 1, 12, 0, 12], getcurpos()) + + " Tests for pasting a named register + let @r = 'xyz' + + " paste (gp) named register in the middle of a tab + call setline(1, "a\tb") + call cursor(1, 2, 2) + normal "rgp + call assert_equal('a xyz b', getline(1)) + call assert_equal([0, 1, 8, 0, 8], getcurpos()) + + " paste (gP) named register in the middle of a tab + call setline(1, "a\tb") + call cursor(1, 2, 2) + normal "rgP + call assert_equal('a xyz b', getline(1)) + call assert_equal([0, 1, 7, 0, 7], getcurpos()) + + bwipe! + set virtualedit= +endfunc + +" Test for yanking a few spaces within a tab to a register +func Test_yank_in_tab() + new + let @r = '' + call setline(1, "a\tb") + set virtualedit=all + call cursor(1, 2, 2) + normal "ry5l + call assert_equal(' ', @r) bwipe! set virtualedit= -- cgit From c0ff0cac8719a175a2cdd8244d12015aef854b51 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 29 Dec 2021 16:57:40 +0000 Subject: vim-patch:8.2.3936: no proper test for maintaining change mark in diff mode Problem: No proper test for maintaining change mark in diff mode. Solution: Run the test with internal and external diff. (Sean Dewar, closes vim/vim#9424) https://github.com/vim/vim/commit/ccc1644f95e7833c23fa0d440e42293c1622fdcb --- src/nvim/testdir/test_diffmode.vim | 40 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index fe928c0f65..9829b6b654 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1147,22 +1147,30 @@ func Test_diff_followwrap() endfunc func Test_diff_maintains_change_mark() - enew! - call setline(1, ['a', 'b', 'c', 'd']) - diffthis - new - call setline(1, ['a', 'b', 'c', 'e']) - " Set '[ and '] marks - 2,3yank - call assert_equal([2, 3], [line("'["), line("']")]) - " Verify they aren't affected by the implicit diff - diffthis - call assert_equal([2, 3], [line("'["), line("']")]) - " Verify they aren't affected by an explicit diff - diffupdate - call assert_equal([2, 3], [line("'["), line("']")]) - bwipe! - bwipe! + func DiffMaintainsChangeMark() + enew! + call setline(1, ['a', 'b', 'c', 'd']) + diffthis + new + call setline(1, ['a', 'b', 'c', 'e']) + " Set '[ and '] marks + 2,3yank + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by the implicit diff + diffthis + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by an explicit diff + diffupdate + call assert_equal([2, 3], [line("'["), line("']")]) + bwipe! + bwipe! + endfunc + + set diffopt-=internal + call DiffMaintainsChangeMark() + set diffopt+=internal + call DiffMaintainsChangeMark() + set diffopt& endfunc func Test_diff_rnu() -- cgit From d8adb3a72122aa36571acdff552dddf7aff3d4ff Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 17 Jan 2022 00:15:51 +0000 Subject: vim-patch:8.2.4112: function not deleted at end of test Problem: Function not deleted at end of test. Solution: Delete the function. https://github.com/vim/vim/commit/d9b74a2a41e43ca17a9885ec0a58404adff4273f oops -- my fault :P --- src/nvim/testdir/test_diffmode.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 9829b6b654..482d39056f 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1170,7 +1170,9 @@ func Test_diff_maintains_change_mark() call DiffMaintainsChangeMark() set diffopt+=internal call DiffMaintainsChangeMark() + set diffopt& + delfunc DiffMaintainsChangeMark endfunc func Test_diff_rnu() -- cgit From f2d84df4a856810276eb83b08d5c51d5c6407374 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 27 Jan 2022 18:55:21 +0800 Subject: vim-patch:8.2.3095: with 'virtualedit' set to "block" block selection is wrong Problem: With 'virtualedit' set to "block" block selection is wrong after using "$". (Marco Trosi) Solution: Compute the longest selected line. (closes vim/vim#8495) https://github.com/vim/vim/commit/b17ab86e7b8712206aa9ea7198c28db969e25936 --- src/nvim/screen.c | 27 ++++++++++++++++++++++++--- src/nvim/testdir/test_visual.vim | 3 +++ 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7e7b34fb14..af023d6785 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1222,12 +1222,33 @@ static void win_update(win_T *wp, Providers *providers) } getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); - curwin->w_ve_flags = save_ve_flags; toc++; + curwin->w_ve_flags = save_ve_flags; // Highlight to the end of the line, unless 'virtualedit' has // "block". - if (curwin->w_curswant == MAXCOL && !(get_ve_flags() & VE_BLOCK)) { - toc = MAXCOL; + if (curwin->w_curswant == MAXCOL) { + if (get_ve_flags() & VE_BLOCK) { + pos_T pos; + int cursor_above = curwin->w_cursor.lnum < VIsual.lnum; + + // Need to find the longest line. + toc = 0; + pos.coladd = 0; + for (pos.lnum = curwin->w_cursor.lnum; + cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum; + pos.lnum += cursor_above ? 1 : -1) { + colnr_T t; + + pos.col = STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false)); + getvvcol(wp, &pos, NULL, NULL, &t); + if (toc < t) { + toc = t; + } + } + toc++; + } else { + toc = MAXCOL; + } } if (fromc != wp->w_old_cursor_fcol diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index b087c88c35..76274fb038 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1124,6 +1124,9 @@ func Test_visual_block_with_virtualedit() call term_sendkeys(buf, "\gg$") call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {}) + call term_sendkeys(buf, "\gg\G$") + call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit2', {}) + " clean up call term_sendkeys(buf, "\") call StopVimInTerminal(buf) -- cgit From 8c140be31f0d203b63e7052e698fdfe253e0b5d4 Mon Sep 17 00:00:00 2001 From: Thomas Vigouroux Date: Thu, 27 Jan 2022 12:46:56 +0100 Subject: feat(ts): expose minimum language version to lua (#17186) --- src/nvim/lua/executor.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index cfdbe7b344..5c4d7e3c91 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "luv/luv.h" #include "nvim/api/private/defs.h" @@ -1267,6 +1268,12 @@ int tslua_get_language_version(lua_State *L) return 1; } +int tslua_get_minimum_language_version(lua_State *L) +{ + lua_pushnumber(L, TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION); + return 1; +} + static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { tslua_init(lstate); @@ -1288,6 +1295,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, tslua_get_language_version); lua_setfield(lstate, -2, "_ts_get_language_version"); + + lua_pushcfunction(lstate, tslua_get_minimum_language_version); + lua_setfield(lstate, -2, "_ts_get_minimum_language_version"); } int nlua_expand_pat(expand_T *xp, char_u *pat, int *num_results, char_u ***results) -- cgit From 5182627ce97162b90d8d6c34408c4ce937e1072c Mon Sep 17 00:00:00 2001 From: f380cedric Date: Thu, 27 Jan 2022 14:58:53 +0100 Subject: vim-patch:8.2.3669: buffer overflow with long help argument (#16971) Problem: Buffer overflow with long help argument. Solution: Use snprintf(). https://github.com/vim/vim/commit/bd228fd097b41a798f90944b5d1245eddd484142 --- src/nvim/ex_cmds.c | 3 +-- src/nvim/testdir/test_help.vim | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index ca5e14ee63..12598a88d1 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5071,8 +5071,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool && ((arg[1] != NUL && arg[2] == NUL) || (vim_strchr((char_u *)"%_z@", arg[1]) != NULL && arg[2] != NUL))) { - STRCPY(d, "/\\\\"); - STRCPY(d + 3, arg + 1); + vim_snprintf((char *)d, IOSIZE, "/\\\\%s", arg + 1); // Check for "/\\_$", should be "/\\_\$" if (d[3] == '_' && d[4] == '$') { STRCPY(d + 4, "\\$"); diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 8e59efd22d..977dad6a45 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -101,4 +101,12 @@ func Test_helptag_cmd() call delete('Xdir', 'rf') endfunc +func Test_help_long_argument() + try + exe 'help \%' .. repeat('0', 1021) + catch + call assert_match("E149:", v:exception) + endtry +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From c5ac04331bdde5dd226c8e058103ed0fd1d140b7 Mon Sep 17 00:00:00 2001 From: f380cedric Date: Thu, 27 Jan 2022 14:59:30 +0100 Subject: vim-patch:8.2.3612: using freed memory with regexp using a mark (#16973) Problem: Using freed memory with regexp using a mark. Solution: Get the line again after getting the mark position. https://github.com/vim/vim/commit/64066b9acd9f8cffdf4840f797748f938a13f2d6 --- src/nvim/regexp.c | 2 +- src/nvim/regexp_nfa.c | 7 +++++++ src/nvim/testdir/test_regexp_latin.vim | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 45e580dbee..c8508179a1 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3232,7 +3232,7 @@ typedef struct { // The current match-position is remembered with these variables: linenr_T lnum; ///< line number, relative to first line char_u *line; ///< start of current line - char_u *input; ///< current input, points into "regline" + char_u *input; ///< current input, points into "line" int need_clear_subexpr; ///< subexpressions still need to be cleared int need_clear_zsubexpr; ///< extmatch subexpressions still need to be diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index cafffc0319..133858f113 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -6071,8 +6071,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MARK_GT: case NFA_MARK_LT: { + size_t col = rex.input - rex.line; pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false); + // Line may have been freed, get it again. + if (REG_MULTI) { + rex.line = reg_getline(rex.lnum); + rex.input = rex.line + col; + } + // Compare the mark position to the match position, if the mark // exists and mark is set in reg_buf. if (pos != NULL && pos->lnum > 0) { diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 712f1e6025..a92f7e1192 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -787,4 +787,12 @@ func Test_regexp_error() set re& endfunc +func Test_using_mark_position() + " this was using freed memory + new + norm O0 + call assert_fails("s/\\%')", 'E486:') + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 9779f5c84c2c0210b050b0e81dde66e21e384469 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 28 Jan 2022 06:26:24 +0800 Subject: vim-patch:8.2.3903: "gM" does not count tabs as expected (#16796) --- src/nvim/normal.c | 4 +--- src/nvim/testdir/test_normal.vim | 8 ++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ddebfb9756..76ee9f1f40 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6329,11 +6329,9 @@ static void nv_g_cmd(cmdarg_T *cap) break; case 'M': { - const char_u *const ptr = get_cursor_line_ptr(); - oap->motion_type = kMTCharWise; oap->inclusive = false; - i = (int)mb_string2cells_len(ptr, STRLEN(ptr)); + i = linetabsize(get_cursor_line_ptr()); if (cap->count0 > 0 && cap->count0 <= 100) { coladvance((colnr_T)(i * cap->count0 / 100)); } else { diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index cb1d66cb98..5b7cf6fee5 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1812,7 +1812,15 @@ fun! Test_normal33_g_cmd2() call assert_equal(87, col('.')) call assert_equal('E', getreg(0)) + " Test for gM with Tab characters + call setline('.', "\ta\tb\tc\td\te\tf") + norm! gMyl + call assert_equal(6, col('.')) + call assert_equal("c", getreg(0)) + " Test for g Ctrl-G + call setline('.', lineC) + norm! 60gMyl set ff=unix let a=execute(":norm! g\") call assert_match('Col 87 of 144; Line 2 of 2; Word 1 of 1; Byte 88 of 146', a) -- cgit From 503e6f7832dd07b077b3ae654fd7ae46e905aaf3 Mon Sep 17 00:00:00 2001 From: f380cedric Date: Tue, 18 Jan 2022 10:00:30 +0100 Subject: vim-patch:8.2.3403: memory leak for :retab with invalid argument Problem: Memory leak for :retab with invalid argument. Solution: Free the memory. Make error messages consistent. https://github.com/vim/vim/commit/2ddb89f8a94425cda1e5491efc80c1ccccb6e08e Changes in ex_retab are N/A (behind a non-FEAT_) and have been dropped. --- src/nvim/option.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 9471974d2b..bd76dbda95 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7513,8 +7513,10 @@ bool tabstop_set(char_u *var, long **array) for (cp = var; *cp != NUL;) { int n = atoi((char *)cp); + // Catch negative values, overflow and ridiculous big values. if (n < 0 || n > 9999) { semsg(_(e_invarg2), cp); + XFREE_CLEAR(*array); return false; } (*array)[t++] = n; -- cgit From 69f37197c094c71072c3262fefb2ce9f09619384 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 28 Jan 2022 07:55:10 +0800 Subject: fix(completion): update submode message when selecting from API (#17022) --- src/nvim/edit.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b7656230dd..095fa14752 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1093,6 +1093,8 @@ check_pum: // equivalent to selecting the item with a typed key. if (pum_want.active) { if (pum_visible()) { + // Set this to NULL so that ins_complete() will update the message. + edit_submode_extra = NULL; insert_do_complete(s); if (pum_want.finish) { // accept the item and stop completion -- cgit From d0493d1104232766b2ca79c8e8c0b91c68b52128 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 28 Jan 2022 09:34:03 +0800 Subject: test(old): reorder test_register.vim according to upstream (#17215) --- src/nvim/testdir/test_registers.vim | 162 ++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 5061ebe202..f16793a08c 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -1,6 +1,4 @@ -" " Tests for register operations -" source check.vim source view_util.vim @@ -335,6 +333,86 @@ func Test_put_reg_restart_mode() bwipe! endfunc +" Test for getting register info +func Test_get_reginfo() + enew + call setline(1, ['foo', 'bar']) + + exe 'norm! "zyy' + let info = getreginfo('"') + call assert_equal('z', info.points_to) + call setreg('y', 'baz') + call assert_equal('z', getreginfo('').points_to) + call setreg('y', { 'isunnamed': v:true }) + call assert_equal('y', getreginfo('"').points_to) + + exe '$put' + call assert_equal(getreg('y'), getline(3)) + call setreg('', 'qux') + call assert_equal('0', getreginfo('').points_to) + call setreg('x', 'quux') + call assert_equal('0', getreginfo('').points_to) + + let info = getreginfo('') + call assert_equal(getreg('', 1, 1), info.regcontents) + call assert_equal(getregtype(''), info.regtype) + + exe "norm! 0\e" .. '"zy' + let info = getreginfo('z') + call assert_equal(getreg('z', 1, 1), info.regcontents) + call assert_equal(getregtype('z'), info.regtype) + call assert_equal(1, +info.isunnamed) + + let info = getreginfo('"') + call assert_equal('z', info.points_to) + + bwipe! +endfunc + +" Test for restoring register with dict from getreginfo +func Test_set_register_dict() + enew! + + call setreg('"', #{ regcontents: ['one', 'two'], + \ regtype: 'V', points_to: 'z' }) + call assert_equal(['one', 'two'], getreg('"', 1, 1)) + let info = getreginfo('"') + call assert_equal('z', info.points_to) + call assert_equal('V', info.regtype) + call assert_equal(1, +getreginfo('z').isunnamed) + + call setreg('x', #{ regcontents: ['three', 'four'], + \ regtype: 'v', isunnamed: v:true }) + call assert_equal(['three', 'four'], getreg('"', 1, 1)) + let info = getreginfo('"') + call assert_equal('x', info.points_to) + call assert_equal('v', info.regtype) + call assert_equal(1, +getreginfo('x').isunnamed) + + call setreg('y', #{ regcontents: 'five', + \ regtype: "\", isunnamed: v:false }) + call assert_equal("\4", getreginfo('y').regtype) + call assert_equal(0, +getreginfo('y').isunnamed) + call assert_equal(['three', 'four'], getreg('"', 1, 1)) + call assert_equal('x', getreginfo('"').points_to) + + call setreg('"', #{ regcontents: 'six' }) + call assert_equal('0', getreginfo('"').points_to) + call assert_equal(1, +getreginfo('0').isunnamed) + call assert_equal(['six'], getreginfo('0').regcontents) + call assert_equal(['six'], getreginfo('"').regcontents) + + let @x = 'one' + call setreg('x', {}) + call assert_equal(1, len(split(execute('reg x'), '\n'))) + + call assert_fails("call setreg('0', #{regtype: 'V'}, 'v')", 'E118:') + call assert_fails("call setreg('0', #{regtype: 'X'})", 'E475:') + call assert_fails("call setreg('0', #{regtype: 'vy'})", 'E475:') + + bwipe! +endfunc + func Test_v_register() enew call setline(1, 'nothing') @@ -433,84 +511,4 @@ func Test_insert_small_delete() bwipe! endfunc -" Test for getting register info -func Test_get_reginfo() - enew - call setline(1, ['foo', 'bar']) - - exe 'norm! "zyy' - let info = getreginfo('"') - call assert_equal('z', info.points_to) - call setreg('y', 'baz') - call assert_equal('z', getreginfo('').points_to) - call setreg('y', { 'isunnamed': v:true }) - call assert_equal('y', getreginfo('"').points_to) - - exe '$put' - call assert_equal(getreg('y'), getline(3)) - call setreg('', 'qux') - call assert_equal('0', getreginfo('').points_to) - call setreg('x', 'quux') - call assert_equal('0', getreginfo('').points_to) - - let info = getreginfo('') - call assert_equal(getreg('', 1, 1), info.regcontents) - call assert_equal(getregtype(''), info.regtype) - - exe "norm! 0\e" .. '"zy' - let info = getreginfo('z') - call assert_equal(getreg('z', 1, 1), info.regcontents) - call assert_equal(getregtype('z'), info.regtype) - call assert_equal(1, +info.isunnamed) - - let info = getreginfo('"') - call assert_equal('z', info.points_to) - - bwipe! -endfunc - -" Test for restoring register with dict from getreginfo -func Test_set_register_dict() - enew! - - call setreg('"', #{ regcontents: ['one', 'two'], - \ regtype: 'V', points_to: 'z' }) - call assert_equal(['one', 'two'], getreg('"', 1, 1)) - let info = getreginfo('"') - call assert_equal('z', info.points_to) - call assert_equal('V', info.regtype) - call assert_equal(1, +getreginfo('z').isunnamed) - - call setreg('x', #{ regcontents: ['three', 'four'], - \ regtype: 'v', isunnamed: v:true }) - call assert_equal(['three', 'four'], getreg('"', 1, 1)) - let info = getreginfo('"') - call assert_equal('x', info.points_to) - call assert_equal('v', info.regtype) - call assert_equal(1, +getreginfo('x').isunnamed) - - call setreg('y', #{ regcontents: 'five', - \ regtype: "\", isunnamed: v:false }) - call assert_equal("\4", getreginfo('y').regtype) - call assert_equal(0, +getreginfo('y').isunnamed) - call assert_equal(['three', 'four'], getreg('"', 1, 1)) - call assert_equal('x', getreginfo('"').points_to) - - call setreg('"', #{ regcontents: 'six' }) - call assert_equal('0', getreginfo('"').points_to) - call assert_equal(1, +getreginfo('0').isunnamed) - call assert_equal(['six'], getreginfo('0').regcontents) - call assert_equal(['six'], getreginfo('"').regcontents) - - let @x = 'one' - call setreg('x', {}) - call assert_equal(1, len(split(execute('reg x'), '\n'))) - - call assert_fails("call setreg('0', #{regtype: 'V'}, 'v')", 'E118:') - call assert_fails("call setreg('0', #{regtype: 'X'})", 'E475:') - call assert_fails("call setreg('0', #{regtype: 'vy'})", 'E475:') - - bwipe! -endfunc - " vim: shiftwidth=2 sts=2 expandtab -- cgit From e691ef338c04893f3766fe9ba4e368cf8d5d5ad0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 28 Jan 2022 09:30:52 +0100 Subject: vim-patch:8.2.4233: crash when recording and using Select mode Problem: Crash when recording and using Select mode. Solution: When deleting the last recorded character check there is something to delete. https://github.com/vim/vim/commit/a4bc2dd7cccf5a4a9f78b58b6f35a45d17164323 --- src/nvim/getchar.c | 6 +++++- src/nvim/testdir/test_registers.vim | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 95720c498a..9c8872d2be 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -288,8 +288,12 @@ static void add_buff(buffheader_T *const buf, const char *const s, ptrdiff_t sle /// Only works when it was just added. static void delete_buff_tail(buffheader_T *buf, int slen) { - int len = (int)STRLEN(buf->bh_curr->b_str); + int len; + if (buf->bh_curr == NULL || buf->bh_curr->b_str == NULL) { + return; // nothing to delete + } + len = (int)STRLEN(buf->bh_curr->b_str); if (len >= slen) { buf->bh_curr->b_str[len - slen] = NUL; buf->bh_space += (size_t)slen; diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index f16793a08c..43af229739 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -511,4 +511,14 @@ func Test_insert_small_delete() bwipe! endfunc +func Test_record_in_select_mode() + new + call setline(1, 'text') + sil norm q00 + sil norm q + call assert_equal('0ext', getline(1)) + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 7114764ffbabe7024be6c9ed594423894d7aa95d Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 28 Jan 2022 09:33:07 +0100 Subject: vim-patch:8.2.4235: invalid check for NULL pointer Problem: Invalid check for NULL pointer. Solution: Remove the check. https://github.com/vim/vim/commit/37cf413e3e768b76c975e4a7081472d75d649c72 --- src/nvim/getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 9c8872d2be..e3c338c500 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -290,7 +290,7 @@ static void delete_buff_tail(buffheader_T *buf, int slen) { int len; - if (buf->bh_curr == NULL || buf->bh_curr->b_str == NULL) { + if (buf->bh_curr == NULL) { return; // nothing to delete } len = (int)STRLEN(buf->bh_curr->b_str); -- cgit From 175692325bfeea280b105951949e71a5a4b430d1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 28 Jan 2022 09:33:37 +0100 Subject: vim-patch:8.2.4236: accessing freed memory Problem: Accessing freed memory. Solution: Set the bh_curr pointer to NULL. https://github.com/vim/vim/commit/166788c657f4b1090a31ea37a023b1f2c78790c8 --- src/nvim/getchar.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index e3c338c500..55bcfa0e97 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -163,6 +163,7 @@ void free_buff(buffheader_T *buf) xfree(p); } buf->bh_first.b_next = NULL; + buf->bh_curr = NULL; } /// Return the contents of a buffer as a single string. -- cgit From bea439fe99bb49d116f9f5d288ea13d76fb520f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 28 Jan 2022 18:11:05 +0800 Subject: vim-patch:8.2.4237: record buffer wrong if character in Select mode was not typed Problem: Record buffer wrong if character in Select mode was not typed. Solution: Only delete the tail from the record buffer if the character was typed. (closes vim/vim#9650) https://github.com/vim/vim/commit/fbf4f1ca159028382eaeb3bfc31bb6bb96dbb67a --- src/nvim/normal.c | 8 +++++--- src/nvim/testdir/test_registers.vim | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 76ee9f1f40..28e5d47dbc 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1013,9 +1013,11 @@ static int normal_execute(VimState *state, int key) // be mapped in Insert mode. Required for ":lmap" to work. int len = ins_char_typebuf(s->c, mod_mask); - // When recording the character will be recorded again, remove the - // previously recording. - ungetchars(len); + // When recording and gotchars() was called the character will be + // recorded again, remove the previous recording. + if (KeyTyped) { + ungetchars(len); + } if (restart_edit != 0) { s->c = 'd'; diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 43af229739..2e92d9aa2f 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -517,6 +517,17 @@ func Test_record_in_select_mode() sil norm q00 sil norm q call assert_equal('0ext', getline(1)) + + %delete + let @r = '' + call setline(1, ['abc', 'abc', 'abc']) + smap , + call feedkeys("qrgh\Dk\q", 'xt') + call assert_equal("gh\Dk\", @r) + norm j0@rj0@@ + call assert_equal([',Dk', ',Dk', ',Dk'], getline(1, 3)) + sunmap + bwipe! endfunc -- cgit From 5b9980f01eb021b0d84f540a6618f94ad2727153 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 28 Jan 2022 16:59:17 +0100 Subject: vim-patch:8.2.4238: *.tf file could be fileytpe "tf" or "terraform" Problem: *.tf file could be fileytpe "tf" or "terraform". Solution: Detect the type from the file contents. (closes vim/vim#9642) https://github.com/vim/vim/commit/bd8168c7705e315827642f2976ec59e26b7fe009 --- src/nvim/testdir/test_filetype.vim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index eb4824aa32..8d6f9ded1d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -746,6 +746,24 @@ func Test_hook_file() filetype off endfunc +func Test_tf_file() + filetype on + + call writefile([';;; TF MUD client is super duper cool'], 'Xfile.tf') + split Xfile.tf + call assert_equal('tf', &filetype) + bwipe! + + call writefile(['provider "azurerm" {'], 'Xfile.tf') + split Xfile.tf + call assert_equal('terraform', &filetype) + bwipe! + + call delete('Xfile.tf') + filetype off +endfunc + + func Test_ts_file() filetype on -- cgit From fb8cd340dcd102773f0651c75436271fcab721d4 Mon Sep 17 00:00:00 2001 From: bb010g Date: Wed, 26 Jan 2022 20:35:12 -0800 Subject: fix(eval): v:lua support for `-` in module names --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d25903c12a..40fa05da4f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8956,7 +8956,7 @@ static bool tv_is_luafunc(typval_T *tv) const char *skip_luafunc_name(const char *p) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '-' || *p == '.' || *p == '\'') { p++; } return p; -- cgit From 75f4741db92768db1db937fd4dbce15a01b65042 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:14 +0800 Subject: fix(input): remove reinterpreted ALT/META chords from recorded macro --- src/nvim/getchar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 55bcfa0e97..3e0f01852e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1598,8 +1598,9 @@ int vgetc(void) if (!no_mapping && KeyTyped && !(State & TERM_FOCUS) && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { mod_mask = 0; - ins_char_typebuf(c, 0); - ins_char_typebuf(ESC, 0); + int len = ins_char_typebuf(c, 0); + (void)ins_char_typebuf(ESC, 0); + ungetchars(len + 3); // The ALT/META modifier takes three more bytes continue; } -- cgit From fee7d6fba4a940bdc76661fb06eaa91e24149b51 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3454: using a count with "gp" leave cursor in wrong position Problem: Using a count with "gp" leave cursor in wrong position. (Naohiro Ono) Solution: Count the inserted lines. (closes vim/vim#8899) https://github.com/vim/vim/commit/23003e51e18371afda4420d9e171a3dcba5a31cc --- src/nvim/ops.c | 18 +++++++++++++----- src/nvim/testdir/test_put.vim | 10 ++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 83a7c31991..52164f6c38 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3472,6 +3472,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curwin->w_cursor.col -= first_byte_off; } } else { + linenr_T new_lnum = new_cursor.lnum; + // Insert at least one line. When y_type is kMTCharWise, break the first // line in two. for (cnt = 1; cnt <= count; cnt++) { @@ -3488,6 +3490,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) STRCAT(newp, ptr); // insert second line ml_append(lnum, newp, (colnr_T)0, false); + new_lnum++; xfree(newp); oldp = ml_get(lnum); @@ -3503,10 +3506,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } for (; i < y_size; i++) { - if ((y_type != kMTCharWise || i < y_size - 1) - && ml_append(lnum, y_array[i], (colnr_T)0, false) - == FAIL) { - goto error; + if ((y_type != kMTCharWise || i < y_size - 1)) { + if (ml_append(lnum, y_array[i], (colnr_T)0, false) == FAIL) { + goto error; + } + new_lnum++; } lnum++; ++nr_lines; @@ -3556,6 +3560,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) extmark_splice(curbuf, (int)new_cursor.lnum-1, col + 1, 0, 0, 0, (int)y_size+1, 0, totsize+2, kExtmarkUndo); } + + if (cnt == 1) { + new_lnum = lnum; + } } error: @@ -3606,7 +3614,7 @@ error: } curwin->w_cursor.col = 0; } else { - curwin->w_cursor.lnum = lnum; + curwin->w_cursor.lnum = new_lnum; curwin->w_cursor.col = col; } } else if (y_type == kMTLineWise) { diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 440717eaa8..aceac58493 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -112,6 +112,16 @@ func Test_put_p_indent_visual() bwipe! endfunc +func Test_gp_with_count_leaves_cursor_at_end() + new + call setline(1, '<---->') + call setreg('@', "foo\nbar", 'c') + exe "normal 1G3|3gpix\" + call assert_equal(['<--foo', 'barfoo', 'barfoo', 'barx-->'], getline(1, '$')) + + bwipe! +endfunc + func Test_multibyte_op_end_mark() new call setline(1, 'теÑÑ‚') -- cgit From 7812c6830cb8a600c33eec1854c97ac31e4e03fb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3455: using a count with "gp" leaves '] in wrong position Problem: Using a count with "gp" leaves '] in wrong position. (Naohiro Ono) Solution: Correct the mark position. (closes vim/vim#8899) https://github.com/vim/vim/commit/56858e4ed4e338e15821767b8303b06099e40384 --- src/nvim/ops.c | 4 ++++ src/nvim/testdir/test_put.vim | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 52164f6c38..52c55c8de3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3616,6 +3616,10 @@ error: } else { curwin->w_cursor.lnum = new_lnum; curwin->w_cursor.col = col; + curbuf->b_op_end = curwin->w_cursor; + if (col > 1) { + curbuf->b_op_end.col = col - 1; + } } } else if (y_type == kMTLineWise) { // put cursor on first non-blank in first inserted line diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index aceac58493..3b6b2e0e0d 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -116,8 +116,10 @@ func Test_gp_with_count_leaves_cursor_at_end() new call setline(1, '<---->') call setreg('@', "foo\nbar", 'c') - exe "normal 1G3|3gpix\" - call assert_equal(['<--foo', 'barfoo', 'barfoo', 'barx-->'], getline(1, '$')) + normal 1G3|3gp + call assert_equal([0, 4, 4, 0], getpos(".")) + call assert_equal(['<--foo', 'barfoo', 'barfoo', 'bar-->'], getline(1, '$')) + call assert_equal([0, 4, 3, 0], getpos("']")) bwipe! endfunc -- cgit From 5228850749903c2a19984012ae57ae3960ce87cf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3497: put test fails when run by itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Put test fails when run by itself. Solution: Source check.vim. (Dominique Pellé, closes vim/vim#8990) https://github.com/vim/vim/commit/a9173d06f7ca320fc84f4ffa993861d21710bc41 --- src/nvim/testdir/test_put.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 3b6b2e0e0d..45c9ec9ba5 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -1,5 +1,7 @@ " Tests for put commands, e.g. ":put", "p", "gp", "P", "gP", etc. +source check.vim + func Test_put_block() new call feedkeys("i\u2500\x\", 'x') -- cgit From 436a470ef52e60e0a5c553aab53adbc216e650a1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3540: the mark '] is wrong after put with a count Problem: The mark '] is wrong after put with a count. (Naohiro Ono) Solution: Use the right line number. (closes vim/vim#8956) https://github.com/vim/vim/commit/f47ebf1e1a0a6473b10fb4c92c9c6427aab4dc91 --- src/nvim/ops.c | 2 +- src/nvim/testdir/test_put.vim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 52c55c8de3..c29668f541 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3591,7 +3591,7 @@ error: // Put the '] mark on the first byte of the last inserted character. // Correct the length for change in indent. - curbuf->b_op_end.lnum = lnum; + curbuf->b_op_end.lnum = new_lnum; col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff; if (col > 1) { curbuf->b_op_end.col = col - 1 - utf_head_off(y_array[y_size - 1], diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 45c9ec9ba5..4b708fc6a1 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -126,6 +126,18 @@ func Test_gp_with_count_leaves_cursor_at_end() bwipe! endfunc +func Test_p_with_count_leaves_mark_at_end() + new + call setline(1, '<---->') + call setreg('@', "start\nend", 'c') + normal 1G3|3p + call assert_equal([0, 1, 4, 0], getpos(".")) + call assert_equal(['<--start', 'endstart', 'endstart', 'end-->'], getline(1, '$')) + call assert_equal([0, 4, 3, 0], getpos("']")) + + bwipe! +endfunc + func Test_multibyte_op_end_mark() new call setline(1, 'теÑÑ‚') -- cgit From 6f04d3f3ef5eb45c224dabc64373783de9d3f721 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3581: reading character past end of line Problem: Reading character past end of line. Solution: Correct the cursor column. https://github.com/vim/vim/commit/0b5b06cb4777d1401fdf83e7d48d287662236e7e --- src/nvim/ex_docmd.c | 1 + src/nvim/testdir/test_put.vim | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c30d58a8eb..43c0d1d0a2 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8133,6 +8133,7 @@ static void ex_put(exarg_T *eap) eap->forceit = TRUE; } curwin->w_cursor.lnum = eap->line2; + check_cursor_col(); do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1, PUT_LINE|PUT_CURSLINE); } diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 4b708fc6a1..ed76709a56 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -138,6 +138,15 @@ func Test_p_with_count_leaves_mark_at_end() bwipe! endfunc +func Test_put_above_first_line() + new + let @" = 'text' + silent! normal 0o00 + 0put + call assert_equal('text', getline(1)) + bwipe! +endfunc + func Test_multibyte_op_end_mark() new call setline(1, 'теÑÑ‚') -- cgit From 3adc3fc540ada79860d90b48b66a1e967421aabf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 06:05:39 +0800 Subject: vim-patch:8.2.3678: illegal memory access Problem: Illegal memory access. Solution: Ignore changed indent when computing byte offset. https://github.com/vim/vim/commit/85be8563fe5aff686e9e30d6afff401ccd976f2a --- src/nvim/ops.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c29668f541..3a2851f84f 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3473,6 +3473,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } else { linenr_T new_lnum = new_cursor.lnum; + size_t len; // Insert at least one line. When y_type is kMTCharWise, break the first // line in two. @@ -3592,10 +3593,11 @@ error: // Put the '] mark on the first byte of the last inserted character. // Correct the length for change in indent. curbuf->b_op_end.lnum = new_lnum; - col = (colnr_T)STRLEN(y_array[y_size - 1]) - lendiff; + len = STRLEN(y_array[y_size - 1]); + col = (colnr_T)len - lendiff; if (col > 1) { curbuf->b_op_end.col = col - 1 - utf_head_off(y_array[y_size - 1], - y_array[y_size - 1] + col - 1); + y_array[y_size - 1] + len - 1); } else { curbuf->b_op_end.col = 0; } -- cgit From 082ff2190c793d21c213748e556191f8aaa76cde Mon Sep 17 00:00:00 2001 From: Daniel Steinberg Date: Fri, 28 Jan 2022 19:22:42 -0500 Subject: refactor: add `static` to some functions in `funcs.c` (#17030) --- src/nvim/eval/funcs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4a07f6a850..29b6310c54 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3795,7 +3795,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "getmousepos()" function -void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; win_T *wp; @@ -6927,7 +6927,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fpt } /// "prompt_getprompt({buffer})" function -void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { // return an empty string by default, e.g. it's not a prompt buffer @@ -10673,7 +10673,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* * "string()" function */ -void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)encode_tv2string(&argvars[0], NULL); -- cgit From 15c9d88bb70b8ee9bdcf3c6fe7debc01a1ee5f36 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 29 Jan 2022 05:27:29 +0000 Subject: vim-patch:8.2.4245: ":retab 0" may cause illegal memory access Problem: ":retab 0" may cause illegal memory access. Solution: Limit the value of 'tabstop' to 10000. https://github.com/vim/vim/commit/652dee448618589de5528a9e9a36995803f5557a ex_retab change is N/A (+vartabs always available). Nvim's set_num_option validation logic was refactored, hence why it looks different from Vim's. Also use XFREE_CLEAR in other places. --- src/nvim/buffer.c | 6 ++---- src/nvim/option.c | 27 +++++++++------------------ src/nvim/option_defs.h | 2 ++ src/nvim/testdir/test_options.vim | 2 ++ 4 files changed, 15 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index eee5a0b46c..52a7db3be2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1899,10 +1899,8 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_flp); clear_string_option(&buf->b_p_isk); clear_string_option(&buf->b_p_vsts); - xfree(buf->b_p_vsts_nopaste); - buf->b_p_vsts_nopaste = NULL; - xfree(buf->b_p_vsts_array); - buf->b_p_vsts_array = NULL; + XFREE_CLEAR(buf->b_p_vsts_nopaste); + XFREE_CLEAR(buf->b_p_vsts_array); clear_string_option(&buf->b_p_vts); XFREE_CLEAR(buf->b_p_vts_array); clear_string_option(&buf->b_p_keymap); diff --git a/src/nvim/option.c b/src/nvim/option.c index a4a6423ac7..af4bc7ec42 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3178,10 +3178,7 @@ ambw_end: char_u *cp; if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) { - if (curbuf->b_p_vsts_array) { - xfree(curbuf->b_p_vsts_array); - curbuf->b_p_vsts_array = 0; - } + XFREE_CLEAR(curbuf->b_p_vsts_array); } else { for (cp = *varp; *cp; cp++) { if (ascii_isdigit(*cp)) { @@ -3206,10 +3203,7 @@ ambw_end: char_u *cp; if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) { - if (curbuf->b_p_vts_array) { - xfree(curbuf->b_p_vts_array); - curbuf->b_p_vts_array = NULL; - } + XFREE_CLEAR(curbuf->b_p_vts_array); } else { for (cp = *varp; *cp; cp++) { if (ascii_isdigit(*cp)) { @@ -4414,6 +4408,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } else if (pp == &curbuf->b_p_ts || pp == &p_ts) { if (value < 1) { errmsg = e_positive; + } else if (value > TABSTOP_MAX) { + errmsg = e_invarg; } } else if (pp == &curbuf->b_p_tw || pp == &p_tw) { if (value < 0) { @@ -6414,7 +6410,7 @@ void buf_copy_options(buf_T *buf, int flags) if (p_vsts && p_vsts != empty_option) { (void)tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { - buf->b_p_vsts_array = 0; + buf->b_p_vsts_array = NULL; } buf->b_p_vsts_nopaste = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) @@ -7151,10 +7147,7 @@ static void paste_option_changed(void) free_string_option(buf->b_p_vsts); } buf->b_p_vsts = empty_option; - if (buf->b_p_vsts_array) { - xfree(buf->b_p_vsts_array); - } - buf->b_p_vsts_array = 0; + XFREE_CLEAR(buf->b_p_vsts_array); } // set global options @@ -7191,13 +7184,11 @@ static void paste_option_changed(void) buf->b_p_vsts = buf->b_p_vsts_nopaste ? vim_strsave(buf->b_p_vsts_nopaste) : empty_option; - if (buf->b_p_vsts_array) { - xfree(buf->b_p_vsts_array); - } + xfree(buf->b_p_vsts_array); if (buf->b_p_vsts && buf->b_p_vsts != empty_option) { (void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array); } else { - buf->b_p_vsts_array = 0; + buf->b_p_vsts_array = NULL; } } @@ -7558,7 +7549,7 @@ bool tabstop_set(char_u *var, long **array) int n = atoi((char *)cp); // Catch negative values, overflow and ridiculous big values. - if (n < 0 || n > 9999) { + if (n < 0 || n > TABSTOP_MAX) { semsg(_(e_invarg2), cp); XFREE_CLEAR(*array); return false; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 5d6aca9574..d88cd6b9b9 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -903,6 +903,8 @@ enum { #define SB_MAX 100000 // Maximum 'scrollback' value. +#define TABSTOP_MAX 9999 + /// Stores an identifier of a script or channel that last set an option. typedef struct { sctx_T script_ctx; /// script context where the option was last set diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 2312df5450..f7bfa48943 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -259,6 +259,8 @@ func Test_set_errors() call assert_fails('set shiftwidth=-1', 'E487:') call assert_fails('set sidescroll=-1', 'E487:') call assert_fails('set tabstop=-1', 'E487:') + call assert_fails('set tabstop=10000', 'E474:') + call assert_fails('set tabstop=5500000000', 'E474:') call assert_fails('set textwidth=-1', 'E487:') call assert_fails('set timeoutlen=-1', 'E487:') call assert_fails('set updatecount=-1', 'E487:') -- cgit From 8fc9a58256e1865f29bfe6e3ba7a778ee91adb21 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 16:34:11 +0800 Subject: vim-patch:8.2.0028: searchpairpos() is not tested (#17232) --- src/nvim/testdir/test_search.vim | 103 +++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index c796f1f676..2a1a93b5e3 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -292,15 +292,84 @@ endfunc func Test_searchpair() new - call setline(1, ['other code here', '', '[', '" cursor here', ']']) + call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]']) + + 4 + call assert_equal(3, searchpair('\[', '', ']', 'bW')) + call assert_equal([0, 3, 2, 0], getpos('.')) + 4 + call assert_equal(2, searchpair('\[', '', ']', 'bWr')) + call assert_equal([0, 2, 6, 0], getpos('.')) + 4 + call assert_equal(1, searchpair('\[', '', ']', 'bWm')) + call assert_equal([0, 3, 2, 0], getpos('.')) + 4|norm ^ + call assert_equal(5, searchpair('\[', '', ']', 'Wn')) + call assert_equal([0, 4, 2, 0], getpos('.')) + 4 + call assert_equal(2, searchpair('\[', '', ']', 'bW', + \ 'getline(".") =~ "^\\s*\["')) + call assert_equal([0, 2, 6, 0], getpos('.')) + set nomagic + 4 + call assert_equal(3, searchpair('\[', '', ']', 'bW')) + call assert_equal([0, 3, 2, 0], getpos('.')) + set magic + 4|norm ^ + call assert_equal(0, searchpair('{', '', '}', 'bW')) + call assert_equal([0, 4, 2, 0], getpos('.')) + + %d + call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1']) + + /\', '\', '\', 'W')) + call assert_equal([0, 5, 1, 0], getpos('.')) + /\', '\', '\', 'W')) + call assert_equal([0, 3, 3, 0], getpos('.')) + + q! +endfunc + +func Test_searchpairpos() + new + call setline(1, ['other code', 'here [', ' [', ' " cursor here', ' ]]']) + + 4 + call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW')) + call assert_equal([0, 3, 2, 0], getpos('.')) 4 - let a = searchpair('\[','',']','bW') - call assert_equal(3, a) + call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr')) + call assert_equal([0, 2, 6, 0], getpos('.')) + 4|norm ^ + call assert_equal([5, 2], searchpairpos('\[', '', ']', 'Wn')) + call assert_equal([0, 4, 2, 0], getpos('.')) + 4 + call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bW', + \ 'getline(".") =~ "^\\s*\["')) + call assert_equal([0, 2, 6, 0], getpos('.')) + 4 + call assert_equal([2, 6], searchpairpos('\[', '', ']', 'bWr')) + call assert_equal([0, 2, 6, 0], getpos('.')) set nomagic 4 - let a = searchpair('\[','',']','bW') - call assert_equal(3, a) + call assert_equal([3, 2], searchpairpos('\[', '', ']', 'bW')) + call assert_equal([0, 3, 2, 0], getpos('.')) set magic + 4|norm ^ + call assert_equal([0, 0], searchpairpos('{', '', '}', 'bW')) + call assert_equal([0, 4, 2, 0], getpos('.')) + + %d + call setline(1, ['if 1', ' if 2', ' else', ' endif 2', 'endif 1']) + /\', '\', '\', 'W')) + call assert_equal([0, 5, 1, 0], getpos('.')) + /\', '\', '\', 'W')) + call assert_equal([0, 3, 3, 0], getpos('.')) + q! endfunc @@ -312,14 +381,28 @@ func Test_searchpair_errors() call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 0, 99, 100)", 'E475: Invalid argument: 0') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') + call assert_fails("call searchpair('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') + call assert_fails("call searchpair('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn') +endfunc + +func Test_searchpairpos_errors() + call assert_fails("call searchpairpos([0], 'middle', 'end', 'bW', 'skip', 99, 100)", 'E730: using List as a String') + call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String') + call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 0, 99, 100)", 'E475: Invalid argument: 0') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') + call assert_fails("call searchpairpos('start', 'middle', 'end', 'sn')", 'E475: Invalid argument: sn') endfunc func Test_searchpair_skip() func Zero() - return 0 + return 0 endfunc func Partial(x) - return a:x + return a:x endfunc new call setline(1, ['{', 'foo', 'foo', 'foo', '}']) @@ -1192,7 +1275,7 @@ endfunc " This was causing E874. Also causes an invalid read? func Test_look_behind() new - call setline(1, '0\|\&\n\@<=') + call setline(1, '0\|\&\n\@<=') call search(getline(".")) bwipe! endfunc @@ -1236,11 +1319,11 @@ endfunc func Test_search_Ctrl_L_combining() " Make sure, that Ctrl-L works correctly with combining characters. " It uses an artificial example of an 'a' with 4 combining chars: - " 'a' U+0061 Dec:97 LATIN SMALL LETTER A a /\%u61\Z "\u0061" + " 'a' U+0061 Dec:97 LATIN SMALL LETTER A a /\%u61\Z "\u0061" " ' Ì€' U+0300 Dec:768 COMBINING GRAVE ACCENT ̀ /\%u300\Z "\u0300" " ' Ì' U+0301 Dec:769 COMBINING ACUTE ACCENT ́ /\%u301\Z "\u0301" " ' ̇' U+0307 Dec:775 COMBINING DOT ABOVE ̇ /\%u307\Z "\u0307" - " ' Ì£' U+0323 Dec:803 COMBINING DOT BELOW ̣ /\%u323 "\u0323" + " ' Ì£' U+0323 Dec:803 COMBINING DOT BELOW ̣ /\%u323 "\u0323" " Those should also appear on the commandline CheckOption incsearch -- cgit From 6dcdec8042e69c151f40974486b0e3d254596e6c Mon Sep 17 00:00:00 2001 From: Daniel Steinberg Date: Sat, 29 Jan 2022 07:37:07 -0500 Subject: vim-patch:8.2.4052: not easy to resize a window from a plugin (#17028) --- src/nvim/eval.lua | 2 + src/nvim/eval/funcs.c | 36 +++++++++++++ src/nvim/testdir/test_window_cmd.vim | 100 +++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index e445a08227..0b460fe887 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -413,6 +413,8 @@ return { win_gotoid={args=1, base=1}, win_id2tabwin={args=1, base=1}, win_id2win={args=1, base=1}, + win_move_separator={args=2, base=1}, + win_move_statusline={args=2, base=1}, win_screenpos={args=1, base=1}, win_splitmove={args={2, 3}, base=1}, winbufnr={args=1, base=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 29b6310c54..ae383dc981 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11989,6 +11989,42 @@ static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = win_id2win(argvars); } +/// "win_move_separator()" function +static void f_win_move_separator(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *wp; + int offset; + + rettv->vval.v_number = false; + + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL || wp->w_floating) { + return; + } + + offset = (int)tv_get_number(&argvars[1]); + win_drag_vsep_line(wp, offset); + rettv->vval.v_number = true; +} + +/// "win_move_statusline()" function +static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *wp; + int offset; + + rettv->vval.v_number = false; + + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL || wp->w_floating) { + return; + } + + offset = (int)tv_get_number(&argvars[1]); + win_drag_status_line(wp, offset); + rettv->vval.v_number = true; +} + /// "winbufnr(nr)" function static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index a200bf7d42..9b07d83f43 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -939,4 +939,104 @@ func Test_window_resize() %bwipe! endfunc +func Test_win_move_separator() + edit a + leftabove vsplit b + let w = winwidth(0) + " check win_move_separator from left window on left window + call assert_equal(1, winnr()) + for offset in range(5) + call assert_true(win_move_separator(0, offset)) + call assert_equal(w + offset, winwidth(0)) + call assert_true(0->win_move_separator(-offset)) + call assert_equal(w, winwidth(0)) + endfor + " check win_move_separator from right window on left window number + wincmd l + call assert_notequal(1, winnr()) + for offset in range(5) + call assert_true(1->win_move_separator(offset)) + call assert_equal(w + offset, winwidth(1)) + call assert_true(win_move_separator(1, -offset)) + call assert_equal(w, winwidth(1)) + endfor + " check win_move_separator from right window on left window ID + let id = win_getid(1) + for offset in range(5) + call assert_true(win_move_separator(id, offset)) + call assert_equal(w + offset, winwidth(id)) + call assert_true(id->win_move_separator(-offset)) + call assert_equal(w, winwidth(id)) + endfor + " check that win_move_separator doesn't error with offsets beyond moving + " possibility + call assert_true(win_move_separator(id, 5000)) + call assert_true(winwidth(id) > w) + call assert_true(win_move_separator(id, -5000)) + call assert_true(winwidth(id) < w) + " check that win_move_separator returns false for an invalid window + wincmd = + let w = winwidth(0) + call assert_false(win_move_separator(-1, 1)) + call assert_equal(w, winwidth(0)) + " check that win_move_separator returns false for a floating window + let id = nvim_open_win( + \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3}) + let w = winwidth(id) + call assert_false(win_move_separator(id, 1)) + call assert_equal(w, winwidth(id)) + call nvim_win_close(id, 1) + %bwipe! +endfunc + +func Test_win_move_statusline() + edit a + leftabove split b + let h = winheight(0) + " check win_move_statusline from top window on top window + call assert_equal(1, winnr()) + for offset in range(5) + call assert_true(win_move_statusline(0, offset)) + call assert_equal(h + offset, winheight(0)) + call assert_true(0->win_move_statusline(-offset)) + call assert_equal(h, winheight(0)) + endfor + " check win_move_statusline from bottom window on top window number + wincmd j + call assert_notequal(1, winnr()) + for offset in range(5) + call assert_true(1->win_move_statusline(offset)) + call assert_equal(h + offset, winheight(1)) + call assert_true(win_move_statusline(1, -offset)) + call assert_equal(h, winheight(1)) + endfor + " check win_move_statusline from bottom window on top window ID + let id = win_getid(1) + for offset in range(5) + call assert_true(win_move_statusline(id, offset)) + call assert_equal(h + offset, winheight(id)) + call assert_true(id->win_move_statusline(-offset)) + call assert_equal(h, winheight(id)) + endfor + " check that win_move_statusline doesn't error with offsets beyond moving + " possibility + call assert_true(win_move_statusline(id, 5000)) + call assert_true(winheight(id) > h) + call assert_true(win_move_statusline(id, -5000)) + call assert_true(winheight(id) < h) + " check that win_move_statusline returns false for an invalid window + wincmd = + let h = winheight(0) + call assert_false(win_move_statusline(-1, 1)) + call assert_equal(h, winheight(0)) + " check that win_move_statusline returns false for a floating window + let id = nvim_open_win( + \ 0, 0, #{relative: 'editor', row: 2, col: 2, width: 5, height: 3}) + let h = winheight(id) + call assert_false(win_move_statusline(id, 1)) + call assert_equal(h, winheight(id)) + call nvim_win_close(id, 1) + %bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 950a88d4c2a9c17b5a679b4e41fab9f64b8cf9df Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 29 Jan 2022 20:37:28 +0800 Subject: vim-patch:8.2.4248: no proper test for moving the window separator Problem: No proper test for moving the window separator. Solution: Add a test. Add comment in code. (closes vim/vim#9656) https://github.com/vim/vim/commit/a0c4e2f2d7aa164d9d7692702c752ea063bd3a8c Remove the assertion as it is now possible for `fr` to be `NULL`. The test fails without clearing messages. Not sure if this is a bug. --- src/nvim/testdir/test_window_cmd.vim | 20 ++++++++++++++++++++ src/nvim/window.c | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 9b07d83f43..db5c0b2a11 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -968,6 +968,12 @@ func Test_win_move_separator() call assert_true(id->win_move_separator(-offset)) call assert_equal(w, winwidth(id)) endfor + " check win_move_separator from right window on right window is no-op + let w0 = winwidth(0) + call assert_true(win_move_separator(0, 1)) + call assert_equal(w0, winwidth(0)) + call assert_true(win_move_separator(0, -1)) + call assert_equal(w0, winwidth(0)) " check that win_move_separator doesn't error with offsets beyond moving " possibility call assert_true(win_move_separator(id, 5000)) @@ -990,6 +996,7 @@ func Test_win_move_separator() endfunc func Test_win_move_statusline() + redraw " This test fails in Nvim without a redraw to clear messages. edit a leftabove split b let h = winheight(0) @@ -1010,6 +1017,19 @@ func Test_win_move_statusline() call assert_true(win_move_statusline(1, -offset)) call assert_equal(h, winheight(1)) endfor + " check win_move_statusline from bottom window on bottom window + let h0 = winheight(0) + for offset in range(5) + call assert_true(0->win_move_statusline(-offset)) + call assert_equal(h0 - offset, winheight(0)) + call assert_equal(1 + offset, &cmdheight) + call assert_true(win_move_statusline(0, offset)) + call assert_equal(h0, winheight(0)) + call assert_equal(1, &cmdheight) + endfor + call assert_true(win_move_statusline(0, 1)) + call assert_equal(h0, winheight(0)) + call assert_equal(1, &cmdheight) " check win_move_statusline from bottom window on top window ID let id = win_getid(1) for offset in range(5) diff --git a/src/nvim/window.c b/src/nvim/window.c index 1e747a67e9..8e44fd5014 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5795,7 +5795,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } fr = curfr; // put fr at window that grows } - assert(fr); // Not enough room if (room < offset) { @@ -5808,7 +5807,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } if (fr == NULL) { - return; // Safety check, should not happen. + // This can happen when calling win_move_separator() on the rightmost + // window. Just don't do anything. + return; } // grow frame fr by offset lines -- cgit From 5e1c487d9915f84c95222f05ceeec66572fb604e Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Sat, 29 Jan 2022 16:08:44 +0200 Subject: vim-patch:8.2.4090: after restoring a session buffer order can be quite different (#17112) Problem: After restoring a session buffer order can be quite different. Solution: Create buffers first. (Evgeni Chasnovski, closes vim/vim#9520) https://github.com/vim/vim/commit/26ebf1f036517ebeacf571c333a83cca7e13bbe2 --------------- As in Vim, this basically reverts 8.1.0829 providing different solution (see vim/vim#9520). Regarding Neovim, this basically reverts changes from #15062. Test about restoring same terminals was a bit too restrictive with using actual buffer ids, which changed with this patch (now they should be in the same order as at `mksession` call), so I tweaked it. --- src/nvim/ex_session.c | 42 +++++++++++++++++++------------------ src/nvim/testdir/test_mksession.vim | 25 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index a6d6bcbb8d..ca07174543 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -591,6 +591,24 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } + // Put all buffers into the buffer list. + // Do it very early to preserve buffer order after loading session (which + // can be disrupted by prior `edit` or `tabedit` calls). + FOR_ALL_BUFFERS(buf) { + if (!(only_save_windows && buf->b_nwindows == 0) + && !(buf->b_help && !(ssop_flags & SSOP_HELP)) + && buf->b_fname != NULL + && buf->b_p_bl) { + if (fprintf(fd, "badd +%" PRId64 " ", + buf->b_wininfo == NULL + ? (int64_t)1L + : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 + || ses_fname(fd, buf, &ssop_flags, true) == FAIL) { + return FAIL; + } + } + } + // the global argument list if (ses_arglist(fd, "argglobal", &global_alist.al_ga, !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) { @@ -626,7 +644,10 @@ static int makeopens(FILE *fd, char_u *dirnow) // Similar to ses_win_rec() below, populate the tab pages first so // later local options won't be copied to the new tabs. FOR_ALL_TABS(tp) { - if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL) { + // Use `bufhidden=wipe` to remove empty "placeholder" buffers once + // they are not needed. This prevents creating extra buffers (see + // cause of Vim patch 8.1.0829) + if (tp->tp_next != NULL && put_line(fd, "tabnew +setlocal\\ bufhidden=wipe") == FAIL) { return FAIL; } } @@ -804,25 +825,6 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } - // Now put the remaining buffers into the buffer list. - // This is near the end, so that when 'hidden' is set we don't create extra - // buffers. If the buffer was already created with another command the - // ":badd" will have no effect. - FOR_ALL_BUFFERS(buf) { - if (!(only_save_windows && buf->b_nwindows == 0) - && !(buf->b_help && !(ssop_flags & SSOP_HELP)) - && buf->b_fname != NULL - && buf->b_p_bl) { - if (fprintf(fd, "badd +%" PRId64 " ", - buf->b_wininfo == NULL - ? (int64_t)1L - : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 - || ses_fname(fd, buf, &ssop_flags, true) == FAIL) { - return FAIL; - } - } - } - // // Wipe out an empty unnamed buffer we started in. // diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index a87691abf9..a8e50af510 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -309,6 +309,31 @@ func Test_mksession_buffer_count() set nohidden endfunc +func Test_mksession_buffer_order() + %bwipe! + e Xfoo | e Xbar | e Xbaz | e Xqux + bufdo write + mksession! Xtest_mks.out + + " Verify that loading the session preserves order of buffers + %bwipe! + source Xtest_mks.out + + let s:buf_info = getbufinfo() + call assert_true(s:buf_info[0]['name'] =~# 'Xfoo$') + call assert_true(s:buf_info[1]['name'] =~# 'Xbar$') + call assert_true(s:buf_info[2]['name'] =~# 'Xbaz$') + call assert_true(s:buf_info[3]['name'] =~# 'Xqux$') + + " Clean up. + call delete('Xfoo') + call delete('Xbar') + call delete('Xbaz') + call delete('Xqux') + call delete('Xtest_mks.out') + %bwipe! +endfunc + if has('extra_search') func Test_mksession_hlsearch() -- cgit From b2f77c354a289ac99de4c28425dc39d7d057cf90 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 29 Jan 2022 15:40:29 +0100 Subject: vim-patch:8.2.4251: vala files are not recognized (#17235) Problem: Vala files are not recognized. Solution: Add the *.vala pattern. (closes vim/vim#9654) https://github.com/vim/vim/commit/97c554d5149c2aa4a43d689c59563e77277265d4 --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 8d6f9ded1d..dcf20e2c1b 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -556,6 +556,7 @@ let s:filename_checks = { \ 'usserverlog': ['usserver.log', 'USSERVER.LOG', 'usserver.file.log', 'USSERVER.FILE.LOG', 'file.usserver.log', 'FILE.USSERVER.LOG'], \ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'], \ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'], + \ 'vala': ['file.vala'], \ 'vera': ['file.vr', 'file.vri', 'file.vrh'], \ 'verilog': ['file.v'], \ 'verilogams': ['file.va', 'file.vams'], -- cgit From baec0d3152afeab3007ebb505f3fc274511db434 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Fri, 28 Jan 2022 15:42:19 +0100 Subject: feat(provider)!: remove support for python2 and python3.[3-5] These versions of python has reached End-of-life. getting rid of python2 support removes a lot of logic to support two incompatible python versions in the same version. --- src/nvim/eval.lua | 4 +- src/nvim/eval/funcs.c | 25 +------ src/nvim/ex_cmds.lua | 14 ++-- src/nvim/ex_cmds2.c | 135 -------------------------------------- src/nvim/option.c | 10 +-- src/nvim/options.lua | 2 +- src/nvim/testdir/test_python3.vim | 1 + src/nvim/testdir/test_pyx2.vim | 2 +- 8 files changed, 19 insertions(+), 174 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0b460fe887..18967b80f2 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -270,8 +270,8 @@ return { pum_getpos={}, pumvisible={}, py3eval={args=1, base=1}, - pyeval={args=1, base=1}, - pyxeval={args=1, base=1}, + pyeval={args=1, base=1, func="f_py3eval"}, + pyxeval={args=1, base=1, func="f_py3eval"}, perleval={args=1, base=1}, range={args={1, 3}, base=1}, readdir={args={1, 2}, base=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index ae383dc981..29619f62e9 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6982,36 +6982,13 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "pyeval()" function - */ -static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - script_host_eval("python", argvars, rettv); -} - -/* - * "py3eval()" function - */ +/// "py3eval()" and "pyxeval()" functions (always python3) static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { script_host_eval("python3", argvars, rettv); } -// "pyxeval()" function -static void f_pyxeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - init_pyxversion(); - if (p_pyx == 2) { - f_pyeval(argvars, rettv, NULL); - } else { - f_py3eval(argvars, rettv, NULL); - } -} - -/// /// "perleval()" function -/// static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { script_host_eval("perl", argvars, rettv); diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index c391cf96aa..c2d40c8bb7 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2097,19 +2097,19 @@ module.cmds = { command='python', flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_python', + func='ex_python3', }, { command='pydo', flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pydo', + func='ex_pydo3', }, { command='pyfile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pyfile', + func='ex_py3file', }, { command='py3', @@ -2139,25 +2139,25 @@ module.cmds = { command='pyx', flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pyx', + func='ex_python3', }, { command='pyxdo', flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pyxdo', + func='ex_pydo3', }, { command='pythonx', flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pyx', + func='ex_python3', }, { command='pyxfile', flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN), addr_type='ADDR_LINES', - func='ex_pyxfile', + func='ex_py3file', }, { command='quit', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 846789233f..e5cec0e060 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -135,21 +135,6 @@ void ex_profile(exarg_T *eap) } } -void ex_python(exarg_T *eap) -{ - script_host_execute("python", eap); -} - -void ex_pyfile(exarg_T *eap) -{ - script_host_execute_file("python", eap); -} - -void ex_pydo(exarg_T *eap) -{ - script_host_do_range("python", eap); -} - void ex_ruby(exarg_T *eap) { script_host_execute("ruby", eap); @@ -1660,126 +1645,6 @@ void ex_options(exarg_T *eap) cmd_source((char_u *)SYS_OPTWIN_FILE, NULL); } -// Detect Python 3 or 2, and initialize 'pyxversion'. -void init_pyxversion(void) -{ - if (p_pyx == 0) { - if (eval_has_provider("python3")) { - p_pyx = 3; - } else if (eval_has_provider("python")) { - p_pyx = 2; - } - } -} - -// Does a file contain one of the following strings at the beginning of any -// line? -// "#!(any string)python2" => returns 2 -// "#!(any string)python3" => returns 3 -// "# requires python 2.x" => returns 2 -// "# requires python 3.x" => returns 3 -// otherwise return 0. -static int requires_py_version(char_u *filename) -{ - FILE *file; - int requires_py_version = 0; - int i, lines; - - lines = (int)p_mls; - if (lines < 0) { - lines = 5; - } - - file = os_fopen((char *)filename, "r"); - if (file != NULL) { - for (i = 0; i < lines; i++) { - if (vim_fgets(IObuff, IOSIZE, file)) { - break; - } - if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') { - // Check shebang. - if (strstr((char *)IObuff + 2, "python2") != NULL) { - requires_py_version = 2; - break; - } - if (strstr((char *)IObuff + 2, "python3") != NULL) { - requires_py_version = 3; - break; - } - } - IObuff[21] = '\0'; - if (STRCMP("# requires python 2.x", IObuff) == 0) { - requires_py_version = 2; - break; - } - if (STRCMP("# requires python 3.x", IObuff) == 0) { - requires_py_version = 3; - break; - } - } - fclose(file); - } - return requires_py_version; -} - - -// Source a python file using the requested python version. -static void source_pyx_file(exarg_T *eap, char_u *fname) -{ - exarg_T ex; - long int v = requires_py_version(fname); - - init_pyxversion(); - if (v == 0) { - // user didn't choose a preference, 'pyx' is used - v = p_pyx; - } - - // now source, if required python version is not supported show - // unobtrusive message. - if (eap == NULL) { - memset(&ex, 0, sizeof(ex)); - } else { - ex = *eap; - } - ex.arg = fname; - ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3"); - - if (v == 2) { - ex_pyfile(&ex); - } else { - ex_py3file(&ex); - } -} - -// ":pyxfile {fname}" -void ex_pyxfile(exarg_T *eap) -{ - source_pyx_file(eap, eap->arg); -} - -// ":pyx" -void ex_pyx(exarg_T *eap) -{ - init_pyxversion(); - if (p_pyx == 2) { - ex_python(eap); - } else { - ex_python3(eap); - } -} - -// ":pyxdo" -void ex_pyxdo(exarg_T *eap) -{ - init_pyxversion(); - if (p_pyx == 2) { - ex_pydo(eap); - } else { - ex_pydo3(eap); - } -} - /// ":source [{fname}]" void ex_source(exarg_T *eap) { diff --git a/src/nvim/option.c b/src/nvim/option.c index a4a6423ac7..2fb1966cda 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4349,6 +4349,12 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } else if (value > 10000) { errmsg = e_invarg; } + } else if (pp == &p_pyx) { + if (value == 0) { + value = 3; + } else if (value != 3) { + errmsg = e_invarg; + } } else if (pp == &p_re) { if (value < 0 || value > 2) { errmsg = e_invarg; @@ -4523,10 +4529,6 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, if (pum_drawn()) { pum_redraw(); } - } else if (pp == &p_pyx) { - if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) { - errmsg = e_invarg; - } } else if (pp == &p_ul || pp == &curbuf->b_p_ul) { // sync undo before 'undolevels' changes // use the old value, otherwise u_sync() may not work properly diff --git a/src/nvim/options.lua b/src/nvim/options.lua index aea2179a61..e665ffd346 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1846,7 +1846,7 @@ return { type='number', scope={'global'}, secure=true, varname='p_pyx', - defaults={if_true=0} + defaults={if_true=3} }, { full_name='quickfixtextfunc', abbreviation='qftf', diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim index 54da3d2eba..f6a1942e24 100644 --- a/src/nvim/testdir/test_python3.vim +++ b/src/nvim/testdir/test_python3.vim @@ -69,6 +69,7 @@ func Test_vim_function() endfunc func Test_skipped_python3_command_does_not_affect_pyxversion() + throw 'skipped: Nvim hardcodes pyxversion=3' set pyxversion=0 if 0 python3 import vim diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim index b6ed80f842..6a8ebf3da0 100644 --- a/src/nvim/testdir/test_pyx2.vim +++ b/src/nvim/testdir/test_pyx2.vim @@ -1,9 +1,9 @@ " Test for pyx* commands and functions with Python 2. -set pyx=2 if !has('python') finish endif +set pyx=2 let s:py2pattern = '^2\.[0-7]\.\d\+' let s:py3pattern = '^3\.\d\+\.\d\+' -- cgit From 4a96e7809f4d9f6ce21869817eb95ff6dcaa1693 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 30 Jan 2022 00:15:22 +0100 Subject: chore: typo fixes (#16921) Co-authored-by: zeertzjq --- src/nvim/testdir/test_help.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index 977dad6a45..e91dea1040 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -1,4 +1,3 @@ - " Tests for :help func Test_help_restore_snapshot() @@ -109,4 +108,5 @@ func Test_help_long_argument() endtry endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 63173f23c45064452f71fb1efc59dcc48b4a7b7f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 30 Jan 2022 13:25:29 +0800 Subject: vim-patch:8.2.0092: tags functionality insufficiently tested Problem: Tags functionality insufficiently tested. Solution: Add more tags tests. (Yegappan Lakshmanan, closes vim/vim#5446) https://github.com/vim/vim/commit/a1353b5352f0797fc651a0dd363876f1c2fa60c5 --- src/nvim/testdir/test_tagjump.vim | 178 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 2aa04df42a..a2f0e19891 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -641,7 +641,7 @@ func Test_tag_envvar() endfunc " Test for :ptag -func Test_ptag() +func Test_tag_preview() call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//", \ "second\tXfile1\t2", \ "third\tXfile1\t3",], @@ -655,11 +655,19 @@ func Test_ptag() call assert_equal(2, winnr('$')) call assert_equal(1, getwinvar(1, '&previewwindow')) call assert_equal(0, getwinvar(2, '&previewwindow')) - wincmd w + wincmd P call assert_equal(3, line('.')) " jump to the tag again + wincmd w ptag third + wincmd P + call assert_equal(3, line('.')) + + " jump to the newer tag + wincmd w + ptag + wincmd P call assert_equal(3, line('.')) " close the preview window @@ -829,8 +837,8 @@ func Test_tag_last_search_pat() %bwipe endfunc -" Test for jumping to a tag when the tag stack is full -func Test_tag_stack_full() +" Tag stack tests +func Test_tag_stack() let l = [] for i in range(10, 31) let l += ["var" .. i .. "\tXfoo\t/^int var" .. i .. ";$/"] @@ -844,6 +852,7 @@ func Test_tag_stack_full() endfor call writefile(l, 'Xfoo') + " Jump to a tag when the tag stack is full. Oldest entry should be removed. enew for i in range(10, 30) exe "tag var" .. i @@ -856,9 +865,15 @@ func Test_tag_stack_full() call assert_equal('var12', l.items[0].tagname) call assert_equal('var31', l.items[19].tagname) - " Jump from the top of the stack + " Use tnext with a single match + call assert_fails('tnext', 'E427:') + + " Jump to newest entry from the top of the stack call assert_fails('tag', 'E556:') + " Pop with zero count from the top of the stack + call assert_fails('0pop', 'E556:') + " Pop from an unsaved buffer enew! call append(1, "sample text") @@ -869,6 +884,13 @@ func Test_tag_stack_full() " Pop all the entries in the tag stack call assert_fails('30pop', 'E555:') + " Pop with a count when already at the bottom of the stack + call assert_fails('exe "normal 4\"', 'E555:') + call assert_equal(1, gettagstack().curidx) + + " Jump to newest entry from the bottom of the stack with zero count + call assert_fails('0tag', 'E555:') + " Pop the tag stack when it is empty call settagstack(1, {'items' : []}) call assert_fails('pop', 'E73:') @@ -895,6 +917,7 @@ func Test_tag_multimatch() [CODE] call writefile(code, 'Xfoo') + call settagstack(1, {'items' : []}) tag first tlast call assert_equal(3, line('.')) @@ -903,6 +926,151 @@ func Test_tag_multimatch() call assert_equal(1, line('.')) call assert_fails('tprev', 'E425:') + tlast + call feedkeys("5\", 't') + tselect first + call assert_equal(2, gettagstack().curidx) + + set ignorecase + tag FIRST + tnext + call assert_equal(2, line('.')) + set ignorecase& + + call delete('Xtags') + call delete('Xfoo') + set tags& + %bwipe +endfunc + +" Test for previewing multiple matching tags +func Test_preview_tag_multimatch() + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "first\tXfoo\t1", + \ "first\tXfoo\t2", + \ "first\tXfoo\t3"], + \ 'Xtags') + set tags=Xtags + let code =<< trim [CODE] + int first() {} + int first() {} + int first() {} + [CODE] + call writefile(code, 'Xfoo') + + enew | only + ptag first + ptlast + wincmd P + call assert_equal(3, line('.')) + wincmd w + call assert_fails('ptnext', 'E428:') + ptprev + wincmd P + call assert_equal(2, line('.')) + wincmd w + ptfirst + wincmd P + call assert_equal(1, line('.')) + wincmd w + call assert_fails('ptprev', 'E425:') + ptnext + wincmd P + call assert_equal(2, line('.')) + wincmd w + ptlast + call feedkeys("5\", 't') + ptselect first + wincmd P + call assert_equal(3, line('.')) + + pclose + + call delete('Xtags') + call delete('Xfoo') + set tags& + %bwipe +endfunc + +" Test for jumping to multiple matching tags across multiple :tags commands +func Test_tnext_multimatch() + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "first\tXfoo1\t1", + \ "first\tXfoo2\t1", + \ "first\tXfoo3\t1"], + \ 'Xtags') + set tags=Xtags + let code =<< trim [CODE] + int first() {} + [CODE] + call writefile(code, 'Xfoo1') + call writefile(code, 'Xfoo2') + call writefile(code, 'Xfoo3') + + tag first + tag first + pop + tnext + tnext + call assert_fails('tnext', 'E428:') + + call delete('Xtags') + call delete('Xfoo1') + call delete('Xfoo2') + call delete('Xfoo3') + set tags& + %bwipe +endfunc + +" Test for jumping to multiple matching tags in non-existing files +func Test_multimatch_non_existing_files() + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "first\tXfoo1\t1", + \ "first\tXfoo2\t1", + \ "first\tXfoo3\t1"], + \ 'Xtags') + set tags=Xtags + + call settagstack(1, {'items' : []}) + call assert_fails('tag first', 'E429:') + call assert_equal(3, gettagstack().items[0].matchnr) + + call delete('Xtags') + set tags& + %bwipe +endfunc + +func Test_tselect_listing() + call writefile([ + \ "!_TAG_FILE_ENCODING\tutf-8\t//", + \ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:", + \ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"], + \ 'Xtags') + set tags=Xtags + + let code =<< trim [CODE] + static int first; + static char first; + [CODE] + call writefile(code, 'Xfoo') + + call feedkeys("\", "t") + let l = split(execute("tselect first"), "\n") + let expected =<< [DATA] + # pri kind tag file + 1 FS v first Xfoo + typeref:typename:int + 1 + 2 FS v first Xfoo + typeref:typename:char + 2 +Type number and (empty cancels): +[DATA] + call assert_equal(expected, l) + call delete('Xtags') call delete('Xfoo') set tags& -- cgit From 8a9230db26f4cb8748d992ab4b545aca884884c5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 30 Jan 2022 13:25:29 +0800 Subject: vim-patch:8.2.0950: tagjump test fails Problem: Tagjump test fails. Solution: Adjust expected text of the prompt. https://github.com/vim/vim/commit/13b8205b442f52f34deac98e3312a27dec14d8e2 --- src/nvim/testdir/test_tagjump.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index a2f0e19891..e0b05edf15 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -1067,7 +1067,7 @@ func Test_tselect_listing() 2 FS v first Xfoo typeref:typename:char 2 -Type number and (empty cancels): +Type number and (q or empty cancels): [DATA] call assert_equal(expected, l) -- cgit From 2793fcae0ac2aef14c8f22636d579c68a97c0851 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Fri, 28 Jan 2022 22:54:03 +0100 Subject: vim-patch:8.2.4241: some type casts are redundant Problem: Some type casts are redundant. Solution: Remove the type casts. (closes vim/vim#9643) https://github.com/vim/vim/commit/420fabcd4ffeaf79082a6e43db91e1d363f88f27 This is not a literal port but an equivalent one. --- src/nvim/api/private/helpers.c | 9 ++++----- src/nvim/debugger.c | 4 ++-- src/nvim/ex_cmds.c | 4 ++-- src/nvim/ex_eval.c | 8 ++++---- src/nvim/ex_getln.c | 2 +- src/nvim/if_cscope.c | 6 ++---- src/nvim/lua/executor.c | 4 ++-- src/nvim/message.c | 9 ++++----- src/nvim/normal.c | 9 ++++----- src/nvim/ops.c | 6 ++---- src/nvim/option.c | 20 ++++++++++---------- src/nvim/popupmnu.c | 8 ++++---- src/nvim/screen.c | 4 ---- src/nvim/sign.c | 8 ++++---- src/nvim/tui/input.c | 1 - src/nvim/version.c | 2 +- 16 files changed, 46 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index f540f8f7ee..4ddd90f3c9 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -139,10 +139,10 @@ bool try_end(Error *err) got_int = false; } else if (msg_list != NULL && *msg_list != NULL) { int should_free; - char *msg = (char *)get_exception_string(*msg_list, - ET_ERROR, - NULL, - &should_free); + char *msg = get_exception_string(*msg_list, + ET_ERROR, + NULL, + &should_free); api_set_error(err, kErrorTypeException, "%s", msg); free_global_msglist(); @@ -720,7 +720,6 @@ fail_and_free: xfree(parsed_args.rhs); xfree(parsed_args.orig_rhs); XFREE_CLEAR(parsed_args.desc); - return; } /// Collects `n` buffer lines into array `l`, optionally replacing newlines diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index b6e35f3047..75ebf0084e 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -268,7 +268,7 @@ void do_debug(char_u *cmd) DOCMD_VERBOSE|DOCMD_EXCRESET); debug_break_level = n; } - lines_left = (int)(Rows - 1); + lines_left = Rows - 1; } xfree(cmdline); @@ -277,7 +277,7 @@ void do_debug(char_u *cmd) redraw_all_later(NOT_VALID); need_wait_return = false; msg_scroll = save_msg_scroll; - lines_left = (int)(Rows - 1); + lines_left = Rows - 1; State = save_State; debug_mode = false; did_emsg = save_did_emsg; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e6d63d08a7..1916fa7e44 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1366,7 +1366,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char_u *cmd, ui_cursor_goto(Rows - 1, 0); if (do_out) { - if (u_save((line2), (linenr_T)(line2 + 1)) == FAIL) { + if (u_save(line2, (linenr_T)(line2 + 1)) == FAIL) { xfree(cmd_buf); goto error; } @@ -3055,7 +3055,7 @@ void ex_append(exarg_T *eap) // it is the same as "start" -- Acevedo if (!cmdmod.lockmarks) { curbuf->b_op_start.lnum - = (eap->line2 < curbuf->b_ml.ml_line_count) ? eap->line2 + 1 : curbuf->b_ml.ml_line_count; + = (eap->line2 < curbuf->b_ml.ml_line_count) ? eap->line2 + 1 : curbuf->b_ml.ml_line_count; if (eap->cmdidx != CMD_append) { curbuf->b_op_start.lnum--; } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index b1c59a607c..851828afcf 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -599,7 +599,7 @@ static void catch_exception(except_T *excp) { excp->caught = caught_stack; caught_stack = excp; - set_vim_var_string(VV_EXCEPTION, (char *)excp->value, -1); + set_vim_var_string(VV_EXCEPTION, excp->value, -1); if (*excp->throw_name != NUL) { if (excp->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, _("%s, line %" PRId64), @@ -650,7 +650,7 @@ static void finish_exception(except_T *excp) } caught_stack = caught_stack->caught; if (caught_stack != NULL) { - set_vim_var_string(VV_EXCEPTION, (char *)caught_stack->value, -1); + set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1); if (*caught_stack->throw_name != NUL) { if (caught_stack->throw_lnum != 0) { vim_snprintf((char *)IObuff, IOSIZE, @@ -733,7 +733,7 @@ static void report_pending(int action, int pending, void *value) vim_snprintf((char *)IObuff, IOSIZE, mesg, _("Exception")); mesg = (char *)concat_str(IObuff, (char_u *)": %s"); - s = (char *)((except_T *)value)->value; + s = ((except_T *)value)->value; } else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) { s = _("Error and interrupt"); } else if (pending & CSTP_ERROR) { @@ -1620,7 +1620,7 @@ void ex_endtry(exarg_T *eap) // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. bool skip = did_emsg || got_int || current_exception - || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index fd75cfc7f8..0b13f73a7b 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -311,7 +311,7 @@ static char_u *get_healthcheck_names(expand_T *xp, int idx) healthchecks.last_gen = last_prompt_id; } return idx < - (int)healthchecks.names.ga_len ? ((char_u **)(healthchecks.names.ga_data))[idx] : NULL; + healthchecks.names.ga_len ? ((char_u **)(healthchecks.names.ga_data))[idx] : NULL; } /// Transform healthcheck file path into it's name. diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index daef8db267..9ca01137cf 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -919,8 +919,8 @@ static int cs_find(exarg_T *eap) /// Common code for cscope find, shared by cs_find() and ex_cstag(). -static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, - bool use_ll, char_u *cmdline) +static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, bool use_ll, + char_u *cmdline) { char *cmd; int *nummatches; @@ -1594,7 +1594,6 @@ static char *cs_pathcomponents(char *path) char *s = path + strlen(path) - 1; for (int i = 0; i < p_cspc; i++) { while (s > path && *--s != '/') { - continue; } } if ((s > path && *s == '/')) { @@ -1813,7 +1812,6 @@ static int cs_read_prompt(size_t i) static void sig_handler(int s) { // do nothing - return; } #endif diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 5c4d7e3c91..cbfb8364f6 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -682,7 +682,7 @@ int nlua_call(lua_State *lstate) typval_T vim_args[MAX_FUNC_ARGS + 1]; int i = 0; // also used for freeing the variables for (; i < nargs; i++) { - lua_pushvalue(lstate, (int)i+2); + lua_pushvalue(lstate, i+2); if (!nlua_pop_typval(lstate, &vim_args[i])) { api_set_error(&err, kErrorTypeException, "error converting argument %d", i+1); @@ -747,7 +747,7 @@ static int nlua_rpc(lua_State *lstate, bool request) Array args = ARRAY_DICT_INIT; for (int i = 0; i < nargs; i++) { - lua_pushvalue(lstate, (int)i+3); + lua_pushvalue(lstate, i+3); ADD(args, nlua_pop_Object(lstate, false, &err)); if (ERROR_SET(&err)) { api_free_array(args); diff --git a/src/nvim/message.c b/src/nvim/message.c index e1e253cd2e..d963cba6cb 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -262,7 +262,6 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea if (*s != NUL) { msg_outtrans_attr((char_u *)s, attr); } - return; } @@ -329,7 +328,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) } retval = msg_end(); - if (keep && retval && vim_strsize((char_u *)s) < (int)(Rows - cmdline_row - 1) + if (keep && retval && vim_strsize((char_u *)s) < (Rows - cmdline_row - 1) * Columns + sc_col) { set_keep_msg((char *)s, 0); } @@ -356,10 +355,10 @@ char_u *msg_strtrunc(char_u *s, int force) len = vim_strsize(s); if (msg_scrolled != 0) { // Use all the columns. - room = (int)(Rows - msg_row) * Columns - 1; + room = (Rows - msg_row) * Columns - 1; } else { // Use up to 'showcmd' column. - room = (int)(Rows - msg_row - 1) * Columns + sc_col - 1; + room = (Rows - msg_row - 1) * Columns + sc_col - 1; } if (len > room && room > 0) { // may have up to 18 bytes per cell (6 per char, up to two @@ -873,7 +872,7 @@ char_u *msg_may_trunc(bool force, char_u *s) { int room; - room = (int)(Rows - cmdline_row - 1) * Columns + sc_col - 1; + room = (Rows - cmdline_row - 1) * Columns + sc_col - 1; if ((force || (shortmess(SHM_TRUNC) && !exmode_active)) && (int)STRLEN(s) - room > 0) { int size = vim_strsize(s); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 28e5d47dbc..997d5ca396 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1030,7 +1030,7 @@ static int normal_execute(VimState *state, int key) s->need_flushbuf = add_to_showcmd(s->c); - while (normal_get_command_count(s)) { continue; } + while (normal_get_command_count(s)) { } if (s->c == K_EVENT) { // Save the count values so that ca.opcount and ca.count0 are exactly @@ -4093,7 +4093,7 @@ static void nv_colon(cmdarg_T *cap) if (is_lua) { cmd_result = map_execute_lua(); } else { - // get a command line and execute it + // get a command line and execute it cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0); } @@ -6330,7 +6330,7 @@ static void nv_g_cmd(cmdarg_T *cap) curwin->w_set_curswant = true; break; - case 'M': { + case 'M': oap->motion_type = kMTCharWise; oap->inclusive = false; i = linetabsize(get_cursor_line_ptr()); @@ -6340,8 +6340,7 @@ static void nv_g_cmd(cmdarg_T *cap) coladvance((colnr_T)(i / 2)); } curwin->w_set_curswant = true; - } - break; + break; case '_': /* "g_": to the last non-blank character in the line or lines diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 83a7c31991..0fbaf6431a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -381,8 +381,8 @@ static void shift_block(oparg_T *oap, int amount) } } for (; ascii_iswhite(*bd.textstart);) { - // TODO: is passing bd.textstart for start of the line OK? - incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, (bd.start_vcol)); + // TODO(fmoralesc): is passing bd.textstart for start of the line OK? + incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol); total += incr; bd.start_vcol += incr; } @@ -2789,8 +2789,6 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) curbuf->b_op_end.col = MAXCOL; } } - - return; } // Copy a block range into a register. diff --git a/src/nvim/option.c b/src/nvim/option.c index 2fb1966cda..43e1fb17c8 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1781,7 +1781,7 @@ static char *illegal_char(char *errbuf, size_t errbuflen, int c) if (errbuf == NULL) { return ""; } - vim_snprintf((char *)errbuf, errbuflen, _("E539: Illegal character <%s>"), + vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"), (char *)transchar(c)); return errbuf; } @@ -2284,12 +2284,12 @@ static char *set_string_option(const int opt_idx, const char *const value, const *varp = s; char *const saved_oldval = xstrdup(oldval); - char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup((char *)oldval_l) : 0; - char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup((char *)oldval_g) : 0; + char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0; + char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0; char *const saved_newval = xstrdup(s); int value_checked = false; - char *const r = did_set_string_option(opt_idx, (char_u **)varp, (int)true, + char *const r = did_set_string_option(opt_idx, (char_u **)varp, true, (char_u *)oldval, NULL, 0, opt_flags, &value_checked); if (r == NULL) { @@ -2777,7 +2777,7 @@ ambw_end: if (!ascii_isdigit(*(s - 1))) { if (errbuf != NULL) { - vim_snprintf((char *)errbuf, errbuflen, + vim_snprintf(errbuf, errbuflen, _("E526: Missing number after <%s>"), transchar_byte(*(s - 1))); errmsg = errbuf; @@ -2968,7 +2968,7 @@ ambw_end: } } else { if (errbuf != NULL) { - vim_snprintf((char *)errbuf, errbuflen, + vim_snprintf(errbuf, errbuflen, _("E535: Illegal character after <%c>"), *--s); errmsg = errbuf; @@ -4433,7 +4433,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Don't change the value and return early if validation failed. if (errmsg != NULL) { - return (char *)errmsg; + return errmsg; } *pp = value; @@ -4557,7 +4557,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // Check the (new) bounds for Rows and Columns here. if (p_lines < min_rows() && full_screen) { if (errbuf != NULL) { - vim_snprintf((char *)errbuf, errbuflen, + vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"), min_rows()); errmsg = errbuf; } @@ -4565,7 +4565,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } if (p_columns < MIN_COLUMNS && full_screen) { if (errbuf != NULL) { - vim_snprintf((char *)errbuf, errbuflen, + vim_snprintf(errbuf, errbuflen, _("E594: Need at least %d columns"), MIN_COLUMNS); errmsg = errbuf; } @@ -4681,7 +4681,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } check_redraw(options[opt_idx].flags); - return (char *)errmsg; + return errmsg; } /// Trigger the OptionSet autocommand. diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index da2ada791f..d7726409b5 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -291,7 +291,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } else { assert(Columns - pum_col - pum_scrollbar >= INT_MIN && Columns - pum_col - pum_scrollbar <= INT_MAX); - pum_width = (int)(Columns - pum_col - pum_scrollbar); + pum_width = Columns - pum_col - pum_scrollbar; } if ((pum_width > max_width + pum_kind_width + pum_extra_width + 1) @@ -352,12 +352,12 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i // not enough room, will use what we have if (pum_rl) { assert(Columns - 1 >= INT_MIN); - pum_col = (int)(Columns - 1); + pum_col = Columns - 1; } else { pum_col = 0; } assert(Columns - 1 >= INT_MIN); - pum_width = (int)(Columns - 1); + pum_width = Columns - 1; } else { if (max_width > p_pw) { // truncate @@ -369,7 +369,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } else { assert(Columns - max_width >= INT_MIN && Columns - max_width <= INT_MAX); - pum_col = (int)(Columns - max_width); + pum_col = Columns - max_width; } pum_width = max_width - pum_scrollbar; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index af023d6785..1654960148 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6919,8 +6919,6 @@ void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, if (!grid->throttled) { ui_call_grid_scroll(grid->handle, row, end, col, col+width, -line_count, 0); } - - return; } /// delete lines on the screen and move lines up. @@ -6971,8 +6969,6 @@ void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, if (!grid->throttled) { ui_call_grid_scroll(grid->handle, row, end, col, col+width, line_count, 0); } - - return; } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index a308df07d1..8b41781c98 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1501,28 +1501,28 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) if (p == NULL) { p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("linehl"), (char *)p); + tv_dict_add_str(retdict, S_LEN("linehl"), p); } if (sp->sn_text_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, false); if (p == NULL) { p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p); + tv_dict_add_str(retdict, S_LEN("texthl"), p); } if (sp->sn_cul_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false); if (p == NULL) { p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("culhl"), (char *)p); + tv_dict_add_str(retdict, S_LEN("culhl"), p); } if (sp->sn_num_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false); if (p == NULL) { p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("numhl"), (char *)p); + tv_dict_add_str(retdict, S_LEN("numhl"), p); } } diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 5fec41f9a5..6e48df3734 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -315,7 +315,6 @@ static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force) return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key); } -static void tinput_timer_cb(TimeWatcher *watcher, void *data); static void tk_getkeys(TermInput *input, bool force) { diff --git a/src/nvim/version.c b/src/nvim/version.c index 5e2a81795a..71cca52773 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2093,7 +2093,7 @@ void list_in_columns(char_u **items, int size, int current) // The rightmost column doesn't need a separator. // Sacrifice it to fit in one more column if possible. - int ncol = (int)(Columns + 1) / width; + int ncol = (Columns + 1) / width; int nrow = item_count / ncol + (item_count % ncol ? 1 : 0); int cur_row = 1; -- cgit From f8f0f14db2e8096e77ed60171d360ac2605b7c80 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 29 Jan 2022 17:00:37 +0000 Subject: vim-patch:8.2.3433: :delcommand does not take a -buffer option Problem: :delcommand does not take a -buffer option. Solution: Add the -buffer option. https://github.com/vim/vim/commit/bdcba24d8597abd5af509c2fb9206e64e713c711 --- src/nvim/ex_docmd.c | 26 ++++++++++++++++++++------ src/nvim/testdir/test_usercommands.vim | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c30d58a8eb..1f416d9f57 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -78,6 +78,10 @@ #include "nvim/vim.h" #include "nvim/window.h" +static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s"); +static char *e_no_such_user_defined_command_in_current_buffer_str + = N_("E1237: No such user-defined command in current buffer: %s"); + static int quitmore = 0; static bool ex_pressedreturn = false; @@ -5737,26 +5741,36 @@ static void ex_delcommand(exarg_T *eap) { int i = 0; ucmd_T *cmd = NULL; - int cmp = -1; + int res = -1; garray_T *gap; + const char_u *arg = eap->arg; + bool buffer_only = false; + + if (STRNCMP(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) { + buffer_only = true; + arg = skipwhite(arg + 7); + } gap = &curbuf->b_ucmds; for (;;) { for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); - cmp = STRCMP(eap->arg, cmd->uc_name); - if (cmp <= 0) { + res = STRCMP(arg, cmd->uc_name); + if (res <= 0) { break; } } - if (gap == &ucmds || cmp == 0) { + if (gap == &ucmds || res == 0 || buffer_only) { break; } gap = &ucmds; } - if (cmp != 0) { - semsg(_("E184: No such user-defined command: %s"), eap->arg); + if (res != 0) { + semsg(_(buffer_only + ? e_no_such_user_defined_command_in_current_buffer_str + : e_no_such_user_defined_command_str), + arg); return; } diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 481959d43d..2c4bf1bda5 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -551,3 +551,26 @@ func Test_command_list() call assert_equal("\nNo user-defined commands found", execute(':command Xxx')) call assert_equal("\nNo user-defined commands found", execute('command')) endfunc + +func Test_delcommand_buffer() + command Global echo 'global' + command -buffer OneBuffer echo 'one' + new + command -buffer TwoBuffer echo 'two' + call assert_equal(0, exists(':OneBuffer')) + call assert_equal(2, exists(':Global')) + call assert_equal(2, exists(':TwoBuffer')) + delcommand -buffer TwoBuffer + call assert_equal(0, exists(':TwoBuffer')) + call assert_fails('delcommand -buffer Global', 'E1237:') + call assert_fails('delcommand -buffer OneBuffer', 'E1237:') + bwipe! + call assert_equal(2, exists(':OneBuffer')) + delcommand -buffer OneBuffer + call assert_equal(0, exists(':OneBuffer')) + call assert_fails('delcommand -buffer Global', 'E1237:') + delcommand Global + call assert_equal(0, exists(':Global')) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 796224028bb8f67d91913f953edfe0693f444de9 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 29 Jan 2022 16:34:00 +0000 Subject: vim-patch:8.2.3629: command completion in cmdline window uses global commands Problem: Command completion in cmdline window uses global user commands, not local commands for the window where it was opened from. Solution: Use local commands. (closes vim/vim#9168) https://github.com/vim/vim/commit/a1198124370a366ff02811a43845a631b5c6e7f0 --- src/nvim/eval.c | 9 +++---- src/nvim/ex_docmd.c | 16 ++++------- src/nvim/ex_getln.c | 7 +++++ src/nvim/testdir/test_ins_complete.vim | 49 ++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 40fa05da4f..b87dad0d1f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3218,9 +3218,8 @@ char_u *get_user_var_name(expand_T *xp, int idx) // b: variables // In cmdwin, the alternative buffer should be used. - hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_buffer->b_vars->dv_hashtab - : &curbuf->b_vars->dv_hashtab; + hashtab_T *ht + = is_in_cmdwin() ? &prevwin->w_buffer->b_vars->dv_hashtab : &curbuf->b_vars->dv_hashtab; if (bdone < ht->ht_used) { if (bdone++ == 0) { hi = ht->ht_array; @@ -3235,9 +3234,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) // w: variables // In cmdwin, the alternative window should be used. - ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_vars->dv_hashtab - : &curwin->w_vars->dv_hashtab; + ht = is_in_cmdwin() ? &prevwin->w_vars->dv_hashtab : &curwin->w_vars->dv_hashtab; if (wdone < ht->ht_used) { if (wdone++ == 0) { hi = ht->ht_array; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1f416d9f57..bf41e45af9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2701,10 +2701,8 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * bool amb_local = false; // Found ambiguous buffer-local command, // only full match global is accepted. - /* - * Look for buffer-local user commands first, then global ones. - */ - gap = &curbuf->b_ucmds; + // Look for buffer-local user commands first, then global ones. + gap = is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; for (;;) { for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); @@ -5351,9 +5349,7 @@ static void uc_list(char_u *name, size_t name_len) uint32_t a; // In cmdwin, the alternative buffer should be used. - garray_T *gap = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_buffer->b_ucmds - : &curbuf->b_ucmds; + garray_T *gap = is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; for (;;) { for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); @@ -6307,9 +6303,7 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // In cmdwin, the alternative buffer should be used. - const buf_T *const buf = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? prevwin->w_buffer - : curbuf; + const buf_T *const buf = is_in_cmdwin() ? prevwin->w_buffer : curbuf; if (idx < buf->b_ucmds.ga_len) { return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; @@ -6331,7 +6325,7 @@ static char_u *get_user_command_name(int idx, int cmdidx) } if (cmdidx == CMD_USER_BUF) { // In cmdwin, the alternative buffer should be used. - buf_T *buf = (cmdwin_type != 0 && get_cmdline_type() == NUL) ? prevwin->w_buffer : curbuf; + buf_T *buf = is_in_cmdwin() ? prevwin->w_buffer : curbuf; if (idx < buf->b_ucmds.ga_len) { return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index fd75cfc7f8..3bb5d37212 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6587,6 +6587,13 @@ static int open_cmdwin(void) return cmdwin_result; } +/// @return true if in the cmdwin, not editing the command line. +bool is_in_cmdwin(void) + FUNC_ATTR_PURE +{ + return cmdwin_type != 0 && get_cmdline_type() == NUL; +} + /// Get script string /// /// Used for commands which accept either `:command script` or diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 6803271c03..f066d842b4 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -341,6 +341,14 @@ func Test_compl_feedkeys() set completeopt& endfunc +func s:ComplInCmdwin_GlobalCompletion(a, l, p) + return 'global' +endfunc + +func s:ComplInCmdwin_LocalCompletion(a, l, p) + return 'local' +endfunc + func Test_compl_in_cmdwin() set wildmenu wildchar= com! -nargs=1 -complete=command GetInput let input = @@ -376,6 +384,47 @@ func Test_compl_in_cmdwin() call feedkeys("q::GetInput b:test_\\:q\", 'tx!') call assert_equal('b:test_', input) + + " Argument completion of buffer-local command + func s:ComplInCmdwin_GlobalCompletionList(a, l, p) + return ['global'] + endfunc + + func s:ComplInCmdwin_LocalCompletionList(a, l, p) + return ['local'] + endfunc + + func s:ComplInCmdwin_CheckCompletion(arg) + call assert_equal('local', a:arg) + endfunc + + com! -nargs=1 -complete=custom,ComplInCmdwin_GlobalCompletion + \ TestCommand call s:ComplInCmdwin_CheckCompletion() + com! -buffer -nargs=1 -complete=custom,ComplInCmdwin_LocalCompletion + \ TestCommand call s:ComplInCmdwin_CheckCompletion() + call feedkeys("q:iTestCommand \\", 'tx!') + + com! -nargs=1 -complete=customlist,ComplInCmdwin_GlobalCompletionList + \ TestCommand call s:ComplInCmdwin_CheckCompletion() + com! -buffer -nargs=1 -complete=customlist,ComplInCmdwin_LocalCompletionList + \ TestCommand call s:ComplInCmdwin_CheckCompletion() + + call feedkeys("q:iTestCommand \\", 'tx!') + + func! s:ComplInCmdwin_CheckCompletion(arg) + call assert_equal('global', a:arg) + endfunc + new + call feedkeys("q:iTestCommand \\", 'tx!') + quit + + delfunc s:ComplInCmdwin_GlobalCompletion + delfunc s:ComplInCmdwin_LocalCompletion + delfunc s:ComplInCmdwin_GlobalCompletionList + delfunc s:ComplInCmdwin_LocalCompletionList + delfunc s:ComplInCmdwin_CheckCompletion + + delcom -buffer TestCommand delcom TestCommand delcom GetInput unlet w:test_winvar -- cgit From c91fbc1b9e8b56e260555b14cd3bc4b9b4874037 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 29 Jan 2022 17:12:58 +0000 Subject: test(oldtest): unskip Test_addr_all v8.1.0341 has since been ported --- src/nvim/testdir/test_usercommands.vim | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 2c4bf1bda5..967ad85a64 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -350,7 +350,6 @@ func Test_use_execute_in_completion() endfunc func Test_addr_all() - throw 'skipped: requires patch v8.1.0341 to pass' command! -addr=lines DoSomething let g:a1 = | let g:a2 = %DoSomething call assert_equal(1, g:a1) -- cgit From f19921be0cba1a38bd15f2b3a2d41992fcb5553b Mon Sep 17 00:00:00 2001 From: VVKot Date: Thu, 13 Jan 2022 05:51:16 +0000 Subject: vim-patch:8.2.3933: after ":cd" fails ":cd -" is incorrect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: After ":cd" fails ":cd -" is incorrect. Solution: Set the previous directory only after successfully changing directory. (Richard Doty, closes vim/vim#9419, closes vim/vim#8983) https://github.com/vim/vim/commit/3d0abad5bf4fe125e219f1b56c4e8200cb900e2a Adjust the test's error message check due to missing patch vim-patch:8.2.3973: tiny build fails Problem: Tiny build fails. Solution: Adjust #ifdefs https://github.com/vim/vim/commit/0f7a5e758c5d8be2d8f1ab4a145d1636a36d18b2 vim-patch:8.2.3978: build error when using dynamycally loaded Python 3 Problem: Build error when using dynamycally loaded Python 3. Solution: Adjust #ifdef. https://github.com/vim/vim/commit/6b1a99dfe33cf5a1d7f82febd81face85ac1b8a6 vim-patch:8.2.4013: build failure without the spell feature Problem: Build failure without the spell feature. Solution: Adjust #ifdefs. https://github.com/vim/vim/commit/e60b3c47d701e73ecbadb1b9a12bf82010cadae8 vim-patch:8.2.4032: ATTRIBUTE_NORETURN is not needed Problem: ATTRIBUTE_NORETURN is not needed. Solution: Use NORETURN(). (Ozaki Kiichi, closes vim/vim#9487) https://github.com/vim/vim/commit/e12406526a24768e6121450112eb2f9f92445ac5 vim-patch:8.2.4048: gcc complains about use of "%p" in printf Problem: gcc complains about use of "%p" in printf. Solution: Add (void *) typecast. (Dominique Pellé, closes vim/vim#9494) https://github.com/vim/vim/commit/c14f667626ba677a767d474324306e39096dc43e --- src/nvim/ex_docmd.c | 32 ++++++++++++++++---------------- src/nvim/testdir/test_cd.vim | 9 +++++++++ 2 files changed, 25 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c30d58a8eb..4325ef7229 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7825,7 +7825,6 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) /// @return true if the directory is successfully changed. bool changedir_func(char_u *new_dir, CdScope scope) { - char_u *tofree; char_u *pdir = NULL; bool retval = false; @@ -7843,26 +7842,12 @@ bool changedir_func(char_u *new_dir, CdScope scope) new_dir = pdir; } - // Free the previous directory - tofree = get_prevdir(scope); - if (os_dirname(NameBuff, MAXPATHL) == OK) { pdir = vim_strsave(NameBuff); } else { pdir = NULL; } - switch (scope) { - case kCdScopeTabpage: - curtab->tp_prevdir = pdir; - break; - case kCdScopeWindow: - curwin->w_prevdir = pdir; - break; - default: - prev_dir = pdir; - } - // For UNIX ":cd" means: go to home directory. // On other systems too if 'cdhome' is set. #if defined(UNIX) @@ -7878,12 +7863,27 @@ bool changedir_func(char_u *new_dir, CdScope scope) bool dir_differs = new_dir == NULL || pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) { + char_u **pp; + + switch (scope) { + case kCdScopeTabpage: + pp = &curtab->tp_prevdir; + break; + case kCdScopeWindow: + pp = &curwin->w_prevdir; + break; + default: + pp = &prev_dir; + } + xfree(*pp); + *pp = pdir; + post_chdir(scope, dir_differs); retval = true; } else { emsg(_(e_failed)); + xfree(pdir); } - xfree(tofree); return retval; } diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim index 76a2620be0..c364babd65 100644 --- a/src/nvim/testdir/test_cd.vim +++ b/src/nvim/testdir/test_cd.vim @@ -44,6 +44,15 @@ func Test_cd_minus() cd - call assert_equal(path, getcwd()) + " Test for :cd - after a failed :cd + " v8.2.1183 is not ported yet + " call assert_fails('cd /nonexistent', 'E344:') + call assert_fails('cd /nonexistent', 'E472:') + call assert_equal(path, getcwd()) + cd - + call assert_equal(path_dotdot, getcwd()) + cd - + " Test for :cd - without a previous directory let lines =<< trim [SCRIPT] call assert_fails('cd -', 'E186:') -- cgit From 58d01d3403735a5b291015cf3abbdadfface74a1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 08:11:26 +0800 Subject: vim-patch:8.2.3475: expression register set by not executed put command (#17211) --- src/nvim/ex_docmd.c | 4 +++- src/nvim/testdir/test_excmd.vim | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c30d58a8eb..3c2a63ff1a 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1758,7 +1758,9 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe ea.regname = *ea.arg++; // for '=' register: accept the rest of the line as an expression if (ea.arg[-1] == '=' && ea.arg[0] != NUL) { - set_expr_line(vim_strsave(ea.arg)); + if (!ea.skip) { + set_expr_line(vim_strsave(ea.arg)); + } ea.arg += STRLEN(ea.arg); } ea.arg = skipwhite(ea.arg); diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 1c053c824f..bbf8b4dfc8 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -400,3 +400,13 @@ func Test_winsize_cmd() call assert_fails('win_getid(1)', 'E475: Invalid argument: _getid(1)') " Actually changing the window size would be flaky. endfunc + +func Test_not_break_expression_register() + call setreg('=', '1+1') + if 0 + put =1 + endif + call assert_equal('1+1', getreg('=', 1)) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 4dcc7bcbedbb2a9e9166b94900d4c3e363468578 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 08:12:29 +0800 Subject: vim-patch:8.2.3532: the previous '' mark is restored after moving the cursor (#17246) --- src/nvim/mark.c | 2 +- src/nvim/testdir/test_marks.vim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 39f18b333d..8b29aa3676 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -217,8 +217,8 @@ void checkpcmark(void) && (equalpos(curwin->w_pcmark, curwin->w_cursor) || curwin->w_pcmark.lnum == 0)) { curwin->w_pcmark = curwin->w_prev_pcmark; - curwin->w_prev_pcmark.lnum = 0; // Show it has been checked } + curwin->w_prev_pcmark.lnum = 0; // it has been checked } /* diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 4ef42946cb..6b9904ec0a 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -25,6 +25,16 @@ function! Test_Incr_Marks() enew! endfunction +func Test_previous_jump_mark() + new + call setline(1, ['']->repeat(6)) + normal Ggg + call assert_equal(6, getpos("''")[1]) + normal jjjjj + call assert_equal(6, getpos("''")[1]) + bwipe! +endfunc + func Test_setpos() new Xone let onebuf = bufnr('%') -- cgit From 62c8715ee9be95f9e0f5164dc9b39a04f4d60e8a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 08:12:44 +0800 Subject: vim-patch:8.1.2412: crash when evaluating expression with error (#17109) --- src/nvim/eval.c | 2 +- src/nvim/testdir/test_lambda.vim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 40fa05da4f..3325628a8e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4402,7 +4402,7 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva rettv->v_type = VAR_UNKNOWN; int ret = get_lambda_tv(arg, rettv, evaluate); - if (ret == NOTDONE) { + if (ret != OK) { return FAIL; } else if (**arg != '(') { if (verbose) { diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 63bb4ae1ef..72ddbcf6dc 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -303,3 +303,8 @@ func Test_lambda_with_index() let Extract = {-> function(List, ['foobar'])()[0]} call assert_equal('foobar', Extract()) endfunc + +func Test_lambda_error() + " This was causing a crash + call assert_fails('ec{@{->{d->()()', 'E15') +endfunc -- cgit From eda957db10e97b28a2734e0391d986676927d963 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: vim-patch:8.2.3787: no proper formatting of a C line comment after a statement Problem: No proper formatting of a C line comment after a statement. Solution: Find the start of the line comment, insert the comment leader and indent the comment properly. https://github.com/vim/vim/commit/6e371ecb27227ff8fedd8561d0f3880a17576848 --- src/nvim/change.c | 39 ++++++++++++++++++++++++++++-------- src/nvim/edit.c | 12 +++++++++-- src/nvim/indent_c.c | 25 +++++++++++++++++------ src/nvim/normal.c | 5 ++--- src/nvim/search.c | 2 +- src/nvim/testdir/test_cindent.vim | 4 ++-- src/nvim/testdir/test_textformat.vim | 30 +++++++++++++++++++++++++++ 7 files changed, 95 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/change.c b/src/nvim/change.c index 0c16b204e3..02893e53b0 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -952,11 +952,13 @@ int copy_indent(int size, char_u *src) /// /// "second_line_indent": indent for after ^^D in Insert mode or if flag /// OPENLINE_COM_LIST +/// "did_do_comment" is set to true when intentionally putting the comment +/// leader in fromt of the new line. /// /// @param dir FORWARD or BACKWARD /// /// @return true on success, false on failure -int open_line(int dir, int flags, int second_line_indent) +int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) { char_u *next_line = NULL; // copy of the next line char_u *p_extra = NULL; // what goes to next line @@ -969,6 +971,7 @@ int open_line(int dir, int flags, int second_line_indent) bool retval = false; // return value int extra_len = 0; // length of p_extra string int lead_len; // length of comment leader + int comment_start = 0; // start index of the comment leader char_u *lead_flags; // position in 'comments' for comment leader char_u *leader = NULL; // copy of comment leader char_u *allocated = NULL; // allocated memory @@ -977,6 +980,7 @@ int open_line(int dir, int flags, int second_line_indent) pos_T *pos; bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL); + bool do_cindent; bool no_si = false; // reset did_si afterwards int first_char = NUL; // init for GCC int vreplace_mode; @@ -1189,11 +1193,29 @@ int open_line(int dir, int flags, int second_line_indent) did_ai = true; } + // May do indenting after opening a new line. + do_cindent = !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL) + && in_cinkeys(dir == FORWARD ? KEY_OPEN_FORW : KEY_OPEN_BACK, + ' ', linewhite(curwin->w_cursor.lnum)); + // Find out if the current line starts with a comment leader. // This may then be inserted in front of the new line. end_comment_pending = NUL; if (flags & OPENLINE_DO_COM) { lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); + if (lead_len == 0 && do_cindent) { + comment_start = check_linecomment(saved_line); + if (comment_start != MAXCOL) { + lead_len = get_leader_len(saved_line + comment_start, + &lead_flags, dir == BACKWARD, true); + if (lead_len != 0) { + lead_len += comment_start; + if (did_do_comment != NULL) { + *did_do_comment = true; + } + } + } + } } else { lead_len = 0; } @@ -1349,6 +1371,13 @@ int open_line(int dir, int flags, int second_line_indent) STRLCPY(leader, saved_line, lead_len + 1); + // TODO(vim): handle multi-byte and double width chars + for (int li = 0; li < comment_start; li++) { + if (!ascii_iswhite(leader[li])) { + leader[li] = ' '; + } + } + // Replace leader with lead_repl, right or left adjusted if (lead_repl != NULL) { int c = 0; @@ -1758,13 +1787,7 @@ int open_line(int dir, int flags, int second_line_indent) ai_col = (colnr_T)getwhitecols_curline(); } // May do indenting after opening a new line. - if (!p_paste - && (curbuf->b_p_cin - || *curbuf->b_p_inde != NUL - ) - && in_cinkeys(dir == FORWARD - ? KEY_OPEN_FORW - : KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum))) { + if (do_cindent) { do_c_expr_indent(); ai_col = (colnr_T)getwhitecols_curline(); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 095fa14752..47e1cc3351 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6021,6 +6021,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for char_u *saved_text = NULL; colnr_T col; colnr_T end_col; + bool did_do_comment = false; virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor()); @@ -6294,11 +6295,18 @@ static void internal_format(int textwidth, int second_indent, int flags, int for + (fo_white_par ? OPENLINE_KEEPTRAIL : 0) + (do_comments ? OPENLINE_DO_COM : 0) + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0), - ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent)); + ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent), + &did_do_comment); if (!(flags & INSCHAR_COM_LIST)) { old_indent = 0; } + // If a comment leader was inserted, may also do this on a following + // line. + if (did_do_comment) { + no_leader = false; + } + replace_offset = 0; if (first_line) { if (!(flags & INSCHAR_COM_LIST)) { @@ -9183,7 +9191,7 @@ static bool ins_eol(int c) AppendToRedobuff(NL_STR); bool i = open_line(FORWARD, has_format_option(FO_RET_COMS) ? OPENLINE_DO_COM : 0, - old_indent); + old_indent, NULL); old_indent = 0; can_cindent = true; // When inserting a line the cursor line must never be in a closed fold. diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index eb24e13d24..5ccf322b76 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -1906,12 +1906,25 @@ int get_c_indent(void) * If we're inside a "//" comment and there is a "//" comment in a * previous line, lineup with that one. */ - if (cin_islinecomment(theline) - && (trypos = find_line_comment()) != NULL) { // XXX - // find how indented the line beginning the comment is - getvcol(curwin, trypos, &col, NULL, NULL); - amount = col; - goto theend; + if (cin_islinecomment(theline)) { + pos_T linecomment_pos; + + trypos = find_line_comment(); // XXX + if (trypos == NULL && curwin->w_cursor.lnum > 1) { + // There may be a statement before the comment, search from the end + // of the line for a comment start. + linecomment_pos.col = check_linecomment(ml_get(curwin->w_cursor.lnum - 1)); + if (linecomment_pos.col != MAXCOL) { + trypos = &linecomment_pos; + trypos->lnum = curwin->w_cursor.lnum - 1; + } + } + if (trypos != NULL) { + // find how indented the line beginning the comment is + getvcol(curwin, trypos, &col, NULL, NULL); + amount = col; + goto theend; + } } /* * If we're inside a comment and not looking at the start of the diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 28e5d47dbc..6e36226914 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6685,9 +6685,8 @@ static void n_opencmd(cmdarg_T *cap) (cap->cmdchar == 'o' ? 1 : 0)) ) && open_line(cap->cmdchar == 'O' ? BACKWARD : FORWARD, - has_format_option(FO_OPEN_COMS) - ? OPENLINE_DO_COM : 0, - 0)) { + has_format_option(FO_OPEN_COMS) ? OPENLINE_DO_COM : 0, + 0, NULL)) { if (win_cursorline_standout(curwin)) { // force redraw of cursorline curwin->w_valid &= ~VALID_CROW; diff --git a/src/nvim/search.c b/src/nvim/search.c index 906c9a6f47..6195be42ba 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2318,7 +2318,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) * Return MAXCOL if not, otherwise return the column. * TODO: skip strings. */ -static int check_linecomment(const char_u *line) +int check_linecomment(const char_u *line) { const char_u *p = line; // scan from start // skip Lispish one-line comments diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 6554d034d3..5dc54111e7 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -1707,9 +1707,9 @@ func Test_cindent_1() #endif int y; // comment - // comment + // comment - // comment + // comment { Constructor(int a, diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index bf0706a0c2..5ca2554218 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -196,6 +196,36 @@ func Test_text_format() enew! endfunc +func Test_format_c_comment() + new + setl ai cindent tw=40 et fo=croql + let text =<< trim END + var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf + END + call setline(1, text) + normal gql + let expected =<< trim END + var = 2345; // asdf asdf asdf asdf asdf + // asdf asdf asdf asdf asdf + END + call assert_equal(expected, getline(1, '$')) + + %del + let text =<< trim END + var = 2345; // asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf + END + call setline(1, text) + normal gql + let expected =<< trim END + var = 2345; // asdf asdf asdf asdf asdf + // asdf asdf asdf asdf asdf + // asdf asdf + END + call assert_equal(expected, getline(1, '$')) + + bwipe! +endfunc + " Tests for :right, :center and :left on text with embedded TAB. func Test_format_align() enew! -- cgit From 88ba0774e202126a432521d5cb14bd2187ef65a2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: vim-patch:8.2.3932: C line comment not formatted properly Problem: C line comment not formatted properly. Solution: If a line comment follows after "#if" the next line is not the end of a paragraph. https://github.com/vim/vim/commit/264d3ddac0f9474816c20a0e92014d6f7f4b08ac --- src/nvim/edit.c | 3 +-- src/nvim/ops.c | 11 +++++++++-- src/nvim/testdir/test_textformat.vim | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 47e1cc3351..3d2cfa5c2a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6137,8 +6137,7 @@ static void internal_format(int textwidth, int second_indent, int flags, int for if (curwin->w_cursor.col <= (colnr_T)wantcol) { break; } - } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) - && fo_multibyte) { + } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) { int ncc; bool allow_break; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 83a7c31991..11484eee57 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -4350,7 +4350,7 @@ void format_lines(linenr_T line_count, int avoid_fex) int leader_len = 0; // leader len of current line int next_leader_len; // leader len of next line char_u *leader_flags = NULL; // flags for leader of current line - char_u *next_leader_flags; // flags for leader of next line + char_u *next_leader_flags = NULL; // flags for leader of next line bool advance = true; int second_indent = -1; // indent for second line (comment aware) bool first_par_line = true; @@ -4467,7 +4467,14 @@ void format_lines(linenr_T line_count, int avoid_fex) leader_len, leader_flags, next_leader_len, next_leader_flags)) { - is_end_par = true; + // Special case: If the next line starts with a line comment + // and this line has a line comment after some text, the + // paragraph doesn't really end. + if (next_leader_flags == NULL + || STRNCMP(next_leader_flags, "://", 3) != 0 + || check_linecomment(get_cursor_line_ptr()) == MAXCOL) { + is_end_par = true; + } } /* diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 5ca2554218..25ae208f03 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -223,6 +223,21 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) + %del + let text =<< trim END + #if 0 // This is another long end of + // line comment that + // wraps. + END + call setline(1, text) + normal gq2j + let expected =<< trim END + #if 0 // This is another long + // end of line comment + // that wraps. + END + call assert_equal(expected, getline(1, '$')) + bwipe! endfunc -- cgit From da3b04a9e07635bab9b0d67e9b16c62c1e59e004 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: vim-patch:8.2.3934: repeating line comment is undesired for "O" command Problem: Repeating line comment is undesired for "O" command. Solution: Do not copy line comment leader for "O". (closes vim/vim#9426) https://github.com/vim/vim/commit/5ea5f373729589acb38ce3f3ca338e8a6d398bdc --- src/nvim/change.c | 5 +++-- src/nvim/testdir/test_textformat.vim | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/change.c b/src/nvim/change.c index 02893e53b0..54c4ba5319 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1201,13 +1201,14 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Find out if the current line starts with a comment leader. // This may then be inserted in front of the new line. end_comment_pending = NUL; - if (flags & OPENLINE_DO_COM) { + if (flags & OPENLINE_DO_COM && dir == FORWARD) { + // Check for a line comment after code. lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); if (lead_len == 0 && do_cindent) { comment_start = check_linecomment(saved_line); if (comment_start != MAXCOL) { lead_len = get_leader_len(saved_line + comment_start, - &lead_flags, dir == BACKWARD, true); + &lead_flags, false, true); if (lead_len != 0) { lead_len += comment_start; if (did_do_comment != NULL) { diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 25ae208f03..5cebdef7f1 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -238,6 +238,29 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) + " Using "o" repeates the line comment, "O" does not. + %del + let text =<< trim END + nop; + val = val; // This is a comment + END + call setline(1, text) + normal 2Go + let expected =<< trim END + nop; + val = val; // This is a comment + // + END + call assert_equal(expected, getline(1, '$')) + normal 2GO + let expected =<< trim END + nop; + + val = val; // This is a comment + // + END + call assert_equal(expected, getline(1, '$')) + bwipe! endfunc -- cgit From f7801fe138d9677c9333650b4d5581489d4b0613 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: vim-patch:8.2.3935: CTRL-U in Insert mode does not fix the indent Problem: CTRL-U in Insert mode does not fix the indent. Solution: Fix the indent when 'cindent' is set. https://github.com/vim/vim/commit/5d20fbf2e79b96a39abbdadc486b656cdcc67ed6 --- src/nvim/edit.c | 8 ++++++++ src/nvim/testdir/test_textformat.vim | 17 ++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 3d2cfa5c2a..f96e7261ca 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -8299,6 +8299,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) int in_indent; int oldState; int cpc[MAX_MCO]; // composing characters + bool call_fix_indent = false; // can't delete anything in an empty file // can't backup past first character in buffer @@ -8442,6 +8443,8 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) beginline(BL_WHITE); if (curwin->w_cursor.col < save_col) { mincol = curwin->w_cursor.col; + // should now fix the indent to match with the previous line + call_fix_indent = true; } curwin->w_cursor.col = save_col; } @@ -8576,6 +8579,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) if (curwin->w_cursor.col <= 1) { did_ai = false; } + + if (call_fix_indent) { + fix_indent(); + } + // It's a little strange to put backspaces into the redo // buffer, but it makes auto-indent a lot easier to deal // with. diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 5cebdef7f1..e1d155eba7 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -238,7 +238,7 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) - " Using "o" repeates the line comment, "O" does not. + " Using "o" repeats the line comment, "O" does not. %del let text =<< trim END nop; @@ -261,6 +261,21 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) + " Using CTRL-U after "o" fixes the indent + %del + let text =<< trim END + { + val = val; // This is a comment + END + call setline(1, text) + exe "normal! 2Go\x\" + let expected =<< trim END + { + val = val; // This is a comment + x + END + call assert_equal(expected, getline(1, '$')) + bwipe! endfunc -- cgit From ae649650de3509e22ee6fad5cfa72998d40f2a92 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: vim-patch:8.2.3938: line comment start is also found in a string Problem: Line comment start is also found in a string. Solution: Skip line comments in a string. https://github.com/vim/vim/commit/ba26367fea3b63df49d274f3d5cca0af38402add --- src/nvim/indent_c.c | 30 ++++++++++++++++-------------- src/nvim/search.c | 19 ++++++++++--------- src/nvim/testdir/test_textformat.vim | 15 +++++++++++++++ 3 files changed, 41 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 5ccf322b76..bfc2ad4dd2 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -41,9 +41,7 @@ static pos_T *ind_find_start_comment(void) // XXX pos_T *find_start_comment(int ind_maxcomment) // XXX { - pos_T *pos; - char_u *line; - char_u *p; + pos_T *pos; int64_t cur_maxcomment = ind_maxcomment; for (;; ) { @@ -55,11 +53,9 @@ pos_T *find_start_comment(int ind_maxcomment) // XXX * Check if the comment start we found is inside a string. * If it is then restrict the search to below this line and try again. */ - line = ml_get(pos->lnum); - for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) - p = skip_string(p); - if ((colnr_T)(p - line) <= pos->col) + if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) { break; + } cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; if (cur_maxcomment <= 0) { pos = NULL; @@ -110,8 +106,6 @@ static pos_T *ind_find_start_CORS(linenr_T *is_raw) static pos_T *find_start_rawstring(int ind_maxcomment) // XXX { pos_T *pos; - char_u *line; - char_u *p; long cur_maxcomment = ind_maxcomment; for (;;) @@ -124,11 +118,9 @@ static pos_T *find_start_rawstring(int ind_maxcomment) // XXX * Check if the raw string start we found is inside a string. * If it is then restrict the search to below this line and try again. */ - line = ml_get(pos->lnum); - for (p = line; *p && (colnr_T)(p - line) < pos->col; ++p) - p = skip_string(p); - if ((colnr_T)(p - line) <= pos->col) - break; + if (!is_pos_in_string(ml_get(pos->lnum), pos->col)) { + break; + } cur_maxcomment = curwin->w_cursor.lnum - pos->lnum - 1; if (cur_maxcomment <= 0) { @@ -205,6 +197,16 @@ static char_u *skip_string(char_u *p) return p; } +/// @returns true if "line[col]" is inside a C string. +int is_pos_in_string(char_u *line, colnr_T col) +{ + char_u *p; + + for (p = line; *p && (colnr_T)(p - line) < col; p++) { + p = skip_string(p); + } + return !((colnr_T)(p - line) <= col); +} /* * Functions for C-indenting. diff --git a/src/nvim/search.c b/src/nvim/search.c index 6195be42ba..93180f97fe 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -26,6 +26,7 @@ #include "nvim/func_attr.h" #include "nvim/getchar.h" #include "nvim/indent.h" +#include "nvim/indent_c.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" @@ -2313,11 +2314,8 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel) return (pos_T *)NULL; // never found it } -/* - * Check if line[] contains a / / comment. - * Return MAXCOL if not, otherwise return the column. - * TODO: skip strings. - */ +/// Check if line[] contains a / / comment. +/// @returns MAXCOL if not, otherwise return the column. int check_linecomment(const char_u *line) { const char_u *p = line; // scan from start @@ -2338,7 +2336,8 @@ int check_linecomment(const char_u *line) in_str = true; } } else if (!in_str && ((p - line) < 2 - || (*(p - 1) != '\\' && *(p - 2) != '#'))) { + || (*(p - 1) != '\\' && *(p - 2) != '#')) + && !is_pos_in_string(line, (colnr_T)(p - line))) { break; // found! } p++; @@ -2348,9 +2347,11 @@ int check_linecomment(const char_u *line) } } else { while ((p = vim_strchr(p, '/')) != NULL) { - // accept a double /, unless it's preceded with * and followed by *, - // because * / / * is an end and start of a C comment - if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')) { + // Accept a double /, unless it's preceded with * and followed by *, + // because * / / * is an end and start of a C comment. + // Only accept the position if it is not inside a string. + if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*') + && !is_pos_in_string(line, (colnr_T)(p - line))) { break; } ++p; diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index e1d155eba7..052c32214d 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -261,6 +261,21 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) + " Using "o" does not repeat a comment in a string + %del + let text =<< trim END + nop; + val = " // This is not a comment"; + END + call setline(1, text) + normal 2Gox + let expected =<< trim END + nop; + val = " // This is not a comment"; + x + END + call assert_equal(expected, getline(1, '$')) + " Using CTRL-U after "o" fixes the indent %del let text =<< trim END -- cgit From ba2bb6a81b0b25cc1670a5b9524d17c9cba27296 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 15:44:54 +0800 Subject: refactor(indent_c.c): add const qualifiers --- src/nvim/indent_c.c | 162 ++++++++++++++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index bfc2ad4dd2..7f483d02ab 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -135,7 +135,7 @@ static pos_T *find_start_rawstring(int ind_maxcomment) // XXX * Skip to the end of a "string" and a 'c' character. * If there is no string or character, return argument unmodified. */ -static char_u *skip_string(char_u *p) +static const char_u *skip_string(const char_u *p) { int i; @@ -170,24 +170,24 @@ static char_u *skip_string(char_u *p) continue; // continue for another string } } else if (p[0] == 'R' && p[1] == '"') { - // Raw string: R"[delim](...)[delim]" - char_u *delim = p + 2; - char_u *paren = vim_strchr(delim, '('); - - if (paren != NULL) { - const ptrdiff_t delim_len = paren - delim; - - for (p += 3; *p; ++p) - if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 - && p[delim_len + 1] == '"') - { - p += delim_len + 1; - break; - } - if (p[0] == '"') { - continue; // continue for another string - } + // Raw string: R"[delim](...)[delim]" + const char_u *delim = p + 2; + const char_u *paren = vim_strchr(delim, '('); + + if (paren != NULL) { + const ptrdiff_t delim_len = paren - delim; + + for (p += 3; *p; p++) { + if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 + && p[delim_len + 1] == '"') { + p += delim_len + 1; + break; + } } + if (p[0] == '"') { + continue; // continue for another string + } + } } break; // no string found } @@ -198,9 +198,9 @@ static char_u *skip_string(char_u *p) } /// @returns true if "line[col]" is inside a C string. -int is_pos_in_string(char_u *line, colnr_T col) +int is_pos_in_string(const char_u *line, colnr_T col) { - char_u *p; + const char_u *p; for (p = line; *p && (colnr_T)(p - line) < col; p++) { p = skip_string(p); @@ -220,7 +220,7 @@ int is_pos_in_string(char_u *line, colnr_T col) /* * Return true if the string "line" starts with a word from 'cinwords'. */ -bool cin_is_cinword(char_u *line) +bool cin_is_cinword(const char_u *line) { bool retval = false; @@ -248,10 +248,10 @@ bool cin_is_cinword(char_u *line) * Skip over white space and C comments within the line. * Also skip over Perl/shell comments if desired. */ -static char_u *cin_skipcomment(char_u *s) +static const char_u *cin_skipcomment(const char_u *s) { while (*s) { - char_u *prev_s = s; + const char_u *prev_s = s; s = skipwhite(s); @@ -285,7 +285,7 @@ static char_u *cin_skipcomment(char_u *s) * Return TRUE if there is no code at *s. White space and comments are * not considered code. */ -static int cin_nocode(char_u *s) +static int cin_nocode(const char_u *s) { return *cin_skipcomment(s) == NUL; } @@ -314,9 +314,9 @@ static pos_T *find_line_comment(void) // XXX } /// Checks if `text` starts with "key:". -static bool cin_has_js_key(char_u *text) +static bool cin_has_js_key(const char_u *text) { - char_u *s = skipwhite(text); + const char_u *s = skipwhite(text); char_u quote = 0; if (*s == '\'' || *s == '"') { @@ -343,7 +343,7 @@ static bool cin_has_js_key(char_u *text) /// Checks if string matches "label:"; move to character after ':' if true. /// "*s" must point to the start of the label, if there is one. -static bool cin_islabel_skip(char_u **s) +static bool cin_islabel_skip(const char_u **s) FUNC_ATTR_NONNULL_ALL { if (!vim_isIDc(**s)) { // need at least one ID character @@ -363,7 +363,7 @@ static bool cin_islabel_skip(char_u **s) // Note: curwin->w_cursor must be where we are looking for the label. bool cin_islabel(void) // XXX { - char_u *s = cin_skipcomment(get_cursor_line_ptr()); + const char_u *s = cin_skipcomment(get_cursor_line_ptr()); // Exclude "default" from labels, since it should be indented // like a switch label. Same for C++ scope declarations. @@ -382,8 +382,8 @@ bool cin_islabel(void) // XXX * label. */ pos_T cursor_save; - pos_T *trypos; - char_u *line; + pos_T *trypos; + const char_u *line; cursor_save = curwin->w_cursor; while (curwin->w_cursor.lnum > 1) { @@ -426,8 +426,8 @@ bool cin_islabel(void) // XXX */ static int cin_isinit(void) { - char_u *s; - static char *skip[] = {"static", "public", "protected", "private"}; + const char_u *s; + static char *skip[] = { "static", "public", "protected", "private" }; s = cin_skipcomment(get_cursor_line_ptr()); @@ -462,7 +462,7 @@ static int cin_isinit(void) * Recognize a switch label: "case .*:" or "default:". */ bool cin_iscase( - char_u *s, + const char_u *s, bool strict // Allow relaxed check of case statement for JS ) { @@ -505,7 +505,7 @@ bool cin_iscase( /* * Recognize a "default" switch label. */ -static int cin_isdefault(char_u *s) +static int cin_isdefault(const char_u *s) { return STRNCMP(s, "default", 7) == 0 && *(s = cin_skipcomment(s + 7)) == ':' @@ -515,7 +515,7 @@ static int cin_isdefault(char_u *s) /* * Recognize a "public/private/protected" scope declaration label. */ -bool cin_isscopedecl(char_u *s) +bool cin_isscopedecl(const char_u *s) { int i; @@ -536,9 +536,9 @@ bool cin_isscopedecl(char_u *s) #define FIND_NAMESPACE_LIM 20 // Recognize a "namespace" scope declaration. -static bool cin_is_cpp_namespace(char_u *s) +static bool cin_is_cpp_namespace(const char_u *s) { - char_u *p; + const char_u *p; bool has_name = false; bool has_name_start = false; @@ -583,7 +583,7 @@ static bool cin_is_cpp_namespace(char_u *s) * case 234: a = b; * ^ */ -static char_u *after_label(char_u *l) +static const char_u *after_label(const char_u *l) { for (; *l; ++l) { if (*l == ':') { @@ -610,10 +610,10 @@ static char_u *after_label(char_u *l) */ static int get_indent_nolabel(linenr_T lnum) // XXX { - char_u *l; + const char_u *l; pos_T fp; colnr_T col; - char_u *p; + const char_u *p; l = ml_get(lnum); p = after_label(l); @@ -632,9 +632,9 @@ static int get_indent_nolabel(linenr_T lnum) // XXX * label: if (asdf && asdfasdf) * ^ */ -static int skip_label(linenr_T lnum, char_u **pp) +static int skip_label(linenr_T lnum, const char_u **pp) { - char_u *l; + const char_u *l; int amount; pos_T cursor_save; @@ -715,8 +715,8 @@ static int cin_first_id_amount(void) */ static int cin_get_equal_amount(linenr_T lnum) { - char_u *line; - char_u *s; + const char_u *line; + const char_u *s; colnr_T col; pos_T fp; @@ -754,7 +754,7 @@ static int cin_get_equal_amount(linenr_T lnum) /* * Recognize a preprocessor statement: Any line that starts with '#'. */ -static int cin_ispreproc(char_u *s) +static int cin_ispreproc(const char_u *s) { if (*skipwhite(s) == '#') return TRUE; @@ -765,9 +765,9 @@ static int cin_ispreproc(char_u *s) /// continuation line of a preprocessor statement. Decrease "*lnump" to the /// start and return the line in "*pp". /// Put the amount of indent in "*amount". -static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) +static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount) { - char_u *line = *pp; + const char_u *line = *pp; linenr_T lnum = *lnump; int retval = false; int candidate_amount = *amount; @@ -801,7 +801,7 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) /* * Recognize the start of a C or C++ comment. */ -static int cin_iscomment(char_u *p) +static int cin_iscomment(const char_u *p) { return p[0] == '/' && (p[1] == '*' || p[1] == '/'); } @@ -809,7 +809,7 @@ static int cin_iscomment(char_u *p) /* * Recognize the start of a "//" comment. */ -static int cin_islinecomment(char_u *p) +static int cin_islinecomment(const char_u *p) { return p[0] == '/' && p[1] == '/'; } @@ -824,8 +824,8 @@ static int cin_islinecomment(char_u *p) * both apply in order to determine initializations). */ static char_u -cin_isterminated ( - char_u *s, +cin_isterminated( + const char_u *s, int incl_open, // include '{' at the end as terminator int incl_comma // recognize a trailing comma ) @@ -874,9 +874,9 @@ cin_isterminated ( /// lines here. /// @param[in] first_lnum Where to start looking. /// @param[in] min_lnum The line before which we will not be looking. -static int cin_isfuncdecl(char_u **sp, linenr_T first_lnum, linenr_T min_lnum) +static int cin_isfuncdecl(const char_u **sp, linenr_T first_lnum, linenr_T min_lnum) { - char_u *s; + const char_u *s; linenr_T lnum = first_lnum; linenr_T save_lnum = curwin->w_cursor.lnum; int retval = false; @@ -977,12 +977,12 @@ done: return retval; } -static int cin_isif(char_u *p) +static int cin_isif(const char_u *p) { return STRNCMP(p, "if", 2) == 0 && !vim_isIDc(p[2]); } -static int cin_iselse(char_u *p) +static int cin_iselse(const char_u *p) { if (*p == '}') { // accept "} else" p = cin_skipcomment(p + 1); @@ -990,7 +990,7 @@ static int cin_iselse(char_u *p) return STRNCMP(p, "else", 4) == 0 && !vim_isIDc(p[4]); } -static int cin_isdo(char_u *p) +static int cin_isdo(const char_u *p) { return STRNCMP(p, "do", 2) == 0 && !vim_isIDc(p[2]); } @@ -1000,7 +1000,7 @@ static int cin_isdo(char_u *p) * We only accept a "while (condition) ;", with only white space between the * ')' and ';'. The condition may be spread over several lines. */ -static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX +static int cin_iswhileofdo(const char_u *p, linenr_T lnum) // XXX { pos_T cursor_save; pos_T *trypos; @@ -1034,7 +1034,7 @@ static int cin_iswhileofdo(char_u *p, linenr_T lnum) // XXX * Otherwise return !0 and update "*poffset" to point to the place where the * string was found. */ -static int cin_is_if_for_while_before_offset(char_u *line, int *poffset) +static int cin_is_if_for_while_before_offset(const char_u *line, int *poffset) { int offset = *poffset; @@ -1078,10 +1078,10 @@ probablyFound: */ static int cin_iswhileofdo_end(int terminated) { - char_u *line; - char_u *p; - char_u *s; - pos_T *trypos; + const char_u *line; + const char_u *p; + const char_u *s; + pos_T *trypos; int i; if (terminated != ';') { // there must be a ';' at the end @@ -1121,7 +1121,7 @@ static int cin_iswhileofdo_end(int terminated) return FALSE; } -static int cin_isbreak(char_u *p) +static int cin_isbreak(const char_u *p) { return STRNCMP(p, "break", 5) == 0 && !vim_isIDc(p[5]); } @@ -1141,10 +1141,10 @@ static int cin_isbreak(char_u *p) */ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached) { lpos_T *pos = &cached->lpos; // find position - char_u *s; + const char_u *s; int class_or_struct, lookfor_ctor_init, cpp_base_class; linenr_T lnum = curwin->w_cursor.lnum; - char_u *line = get_cursor_line_ptr(); + const char_u *line = get_cursor_line_ptr(); if (pos->lnum <= lnum) { return cached->found; // Use the cached result @@ -1312,10 +1312,10 @@ static int get_baseclass_amount(int col) * white space and comments. Skip strings and comments. * Ignore "ignore" after "find" if it's not NULL. */ -static int cin_ends_in(char_u *s, char_u *find, char_u *ignore) +static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore) { - char_u *p = s; - char_u *r; + const char_u *p = s; + const char_u *r; int len = (int)STRLEN(find); while (*p != NUL) { @@ -1336,7 +1336,7 @@ static int cin_ends_in(char_u *s, char_u *find, char_u *ignore) /* * Return TRUE when "s" starts with "word" and then a non-ID character. */ -static int cin_starts_with(char_u *s, char *word) +static int cin_starts_with(const char_u *s, const char *word) { int l = (int)STRLEN(word); @@ -1344,10 +1344,10 @@ static int cin_starts_with(char_u *s, char *word) } /// Recognize a `extern "C"` or `extern "C++"` linkage specifications. -static int cin_is_cpp_extern_c(char_u *s) +static int cin_is_cpp_extern_c(const char_u *s) { - char_u *p; - int has_string_literal = false; + const char_u *p; + int has_string_literal = false; s = cin_skipcomment(s); if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) { @@ -1386,9 +1386,9 @@ static int cin_is_cpp_extern_c(char_u *s) */ static int cin_skip2pos(pos_T *trypos) { - char_u *line; - char_u *p; - char_u *new_p; + const char_u *line; + const char_u *p; + const char_u *new_p; p = line = ml_get(trypos->lnum); while (*p && (colnr_T)(p - line) < trypos->col) { @@ -1531,7 +1531,7 @@ static int corr_ind_maxparen(pos_T *startpos) * Set w_cursor.col to the column number of the last unmatched ')' or '{' in * line "l". "l" must point to the start of the line. */ -static int find_last_paren(char_u *l, int start, int end) +static int find_last_paren(const char_u *l, int start, int end) { int i; int retval = FALSE; @@ -1803,8 +1803,8 @@ int get_c_indent(void) #define BRACE_AT_START 2 // '{' is at start of line #define BRACE_AT_END 3 // '{' is at end of line linenr_T ourscope; - char_u *l; - char_u *look; + const char_u *l; + const char_u *look; char_u terminated; int lookfor; #define LOOKFOR_INITIAL 0 @@ -3617,9 +3617,9 @@ laterend: static int find_match(int lookfor, linenr_T ourscope) { - char_u *look; - pos_T *theirscope; - char_u *mightbeif; + const char_u *look; + pos_T *theirscope; + const char_u *mightbeif; int elselevel; int whilelevel; -- cgit From d746f5aa418f86828aef689a2c4f8d5b53c9f7de Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 6 Dec 2021 20:50:29 +0000 Subject: feat(eval): partially port v8.2.0878 Problem: No reduce() function. Solution: Add a reduce() function. (closes vim/vim#5481) https://github.com/vim/vim/commit/85629985b71035608a37ba3bde86968481490d46 Needs CHECK_LIST_MATERIALIZE from v8.2.0751 (and range_list_materialize from 8.2.0149). Move e_reduceempty to funcs.c, as it's only used there. Make it static. Use tv_blob_len, tv_list_len == 0 for empty checks. Replace vim_memset(&funcexe, 0, ...) with FUNCEXE_INIT. Leave li initially undefined (tv_list_first returns NULL if list is NULL). This patch has a memory leak fixed by v8.2.0882. --- src/nvim/eval.lua | 1 + src/nvim/eval/funcs.c | 85 ++++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_listdict.vim | 31 ++++++++++++++ 3 files changed, 117 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 18967b80f2..e00a14fca7 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -276,6 +276,7 @@ return { range={args={1, 3}, base=1}, readdir={args={1, 2}, base=1}, readfile={args={1, 3}, base=1}, + reduce={args={2, 3}, base=1}, reg_executing={}, reg_recording={}, reg_recorded={}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 29619f62e9..1a5f6e08bc 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -100,6 +100,7 @@ PRAGMA_DIAG_POP static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); +static char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); /// Dummy va_list for passing to vim_snprintf /// @@ -7855,6 +7856,90 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "reduce(list, { accumlator, element -> value } [, initial])" function +static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { + emsg(_(e_listblobreq)); + return; + } + + const char_u *func_name; + partial_T *partial = NULL; + if (argvars[1].v_type == VAR_FUNC) { + func_name = argvars[1].vval.v_string; + } else if (argvars[1].v_type == VAR_PARTIAL) { + partial = argvars[1].vval.v_partial; + func_name = partial_name(partial); + } else { + func_name = (const char_u *)tv_get_string(&argvars[1]); + } + if (*func_name == NUL) { + return; // type error or empty name + } + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + + typval_T accum; + typval_T argv[3]; + if (argvars[0].v_type == VAR_LIST) { + const list_T *const l = argvars[0].vval.v_list; + const listitem_T *li; + + if (argvars[2].v_type == VAR_UNKNOWN) { + if (tv_list_len(l) == 0) { + semsg(_(e_reduceempty), "List"); + return; + } + const listitem_T *const first = tv_list_first(l); + accum = *TV_LIST_ITEM_TV(first); + li = TV_LIST_ITEM_NEXT(l, first); + } else { + accum = argvars[2]; + li = tv_list_first(l); + } + + tv_copy(&accum, rettv); + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + argv[0] = accum; + argv[1] = *TV_LIST_ITEM_TV(li); + if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) { + return; + } + accum = *rettv; + } + } else { + const blob_T *const b = argvars[0].vval.v_blob; + int i; + + if (argvars[2].v_type == VAR_UNKNOWN) { + if (tv_blob_len(b) == 0) { + semsg(_(e_reduceempty), "Blob"); + return; + } + accum.v_type = VAR_NUMBER; + accum.vval.v_number = tv_blob_get(b, 0); + i = 1; + } else { + accum = argvars[2]; + i = 0; + } + + tv_copy(&accum, rettv); + for (; i < tv_blob_len(b); i++) { + argv[0] = accum; + argv[1].v_type = VAR_NUMBER; + argv[1].vval.v_number = tv_blob_get(b, i); + if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) { + return; + } + accum = *rettv; + } + } +} + #define SP_NOMOVE 0x01 ///< don't move cursor #define SP_REPEAT 0x02 ///< repeat to find outer pair #define SP_RETCOUNT 0x04 ///< return matchcount diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index f6c404d390..42b46fc76c 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -620,6 +620,37 @@ func Test_reverse_sort_uniq() call assert_fails('call reverse("")', 'E899:') endfunc +" reduce a list or a blob +func Test_reduce() + call assert_equal(1, reduce([], { acc, val -> acc + val }, 1)) + call assert_equal(10, reduce([1, 3, 5], { acc, val -> acc + val }, 1)) + call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], { acc, val -> 2 * acc + val }, 1)) + call assert_equal('a x y z', ['x', 'y', 'z']->reduce({ acc, val -> acc .. ' ' .. val}, 'a')) + call assert_equal(#{ x: 1, y: 1, z: 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {})) + call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0])) + + let l = ['x', 'y', 'z'] + call assert_equal(42, reduce(l, function('get'), #{ x: #{ y: #{ z: 42 } } })) + call assert_equal(['x', 'y', 'z'], l) + + call assert_equal(1, reduce([1], { acc, val -> acc + val })) + call assert_equal('x y z', reduce(['x', 'y', 'z'], { acc, val -> acc .. ' ' .. val })) + call assert_equal(120, range(1, 5)->reduce({ acc, val -> acc * val })) + call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') + + call assert_equal(1, reduce(0z, { acc, val -> acc + val }, 1)) + call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, { acc, val -> acc + val }, 1)) + call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce({ acc, val -> 2 * acc + val }, 1)) + + call assert_equal(0xff, reduce(0zff, { acc, val -> acc + val })) + call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, { acc, val -> 2 * acc + val })) + call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') + + call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') + call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') + call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') +endfunc + " splitting a string to a List func Test_str_split() call assert_equal(['aa', 'bb'], split(' aa bb ')) -- cgit From af0bae38e286c7139f56307e318fa9818218c3d2 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 6 Dec 2021 22:34:02 +0000 Subject: vim-patch:8.2.0882: leaking memory when using reduce() Problem: Leaking memory when using reduce(). Solution: Free the intermediate value. https://github.com/vim/vim/commit/48b1c21809553d3463b5ed6c2b3bc6d335663bb6 --- src/nvim/eval/funcs.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 1a5f6e08bc..997096aad4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7882,7 +7882,7 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) funcexe.evaluate = true; funcexe.partial = partial; - typval_T accum; + typval_T initial; typval_T argv[3]; if (argvars[0].v_type == VAR_LIST) { const list_T *const l = argvars[0].vval.v_list; @@ -7894,21 +7894,23 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } const listitem_T *const first = tv_list_first(l); - accum = *TV_LIST_ITEM_TV(first); + initial = *TV_LIST_ITEM_TV(first); li = TV_LIST_ITEM_NEXT(l, first); } else { - accum = argvars[2]; + initial = argvars[2]; li = tv_list_first(l); } - tv_copy(&accum, rettv); + tv_copy(&initial, rettv); for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - argv[0] = accum; + argv[0] = *rettv; argv[1] = *TV_LIST_ITEM_TV(li); - if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) { + rettv->v_type = VAR_UNKNOWN; + const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + if (r == FAIL) { return; } - accum = *rettv; } } else { const blob_T *const b = argvars[0].vval.v_blob; @@ -7919,23 +7921,25 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) semsg(_(e_reduceempty), "Blob"); return; } - accum.v_type = VAR_NUMBER; - accum.vval.v_number = tv_blob_get(b, 0); + initial.v_type = VAR_NUMBER; + initial.vval.v_number = tv_blob_get(b, 0); i = 1; + } else if (argvars[2].v_type != VAR_NUMBER) { + emsg(_(e_number_exp)); + return; } else { - accum = argvars[2]; + initial = argvars[2]; i = 0; } - tv_copy(&accum, rettv); + tv_copy(&initial, rettv); for (; i < tv_blob_len(b); i++) { - argv[0] = accum; + argv[0] = *rettv; argv[1].v_type = VAR_NUMBER; argv[1].vval.v_number = tv_blob_get(b, i); if (call_func(func_name, -1, rettv, 2, argv, &funcexe) == FAIL) { return; } - accum = *rettv; } } } -- cgit From 44a5875b24f033c472af50aa4eec4468c554b7c9 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 6 Dec 2021 22:43:59 +0000 Subject: vim-patch:8.2.1051: crash when changing a list while using reduce() on it Problem: Crash when changing a list while using reduce() on it. Solution: Lock the list. (closes vim/vim#6330) https://github.com/vim/vim/commit/ca275a05d8b79f6a9101604fdede2373d0dea44e --- src/nvim/eval/funcs.c | 11 ++++++++--- src/nvim/testdir/test_listdict.vim | 9 +++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 997096aad4..c80ff8f36a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7885,7 +7885,7 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) typval_T initial; typval_T argv[3]; if (argvars[0].v_type == VAR_LIST) { - const list_T *const l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; const listitem_T *li; if (argvars[2].v_type == VAR_UNKNOWN) { @@ -7901,6 +7901,10 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_first(l); } + const VarLockStatus prev_locked = tv_list_locked(l); + const int called_emsg_start = called_emsg; + + tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here tv_copy(&initial, rettv); for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { argv[0] = *rettv; @@ -7908,10 +7912,11 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_UNKNOWN; const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); tv_clear(&argv[0]); - if (r == FAIL) { - return; + if (r == FAIL || called_emsg != called_emsg_start) { + break; } } + tv_list_set_lock(l, prev_locked); } else { const blob_T *const b = argvars[0].vval.v_blob; int i; diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 42b46fc76c..fdf9123d82 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -649,6 +649,15 @@ func Test_reduce() call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:') call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:') + + let g:lut = [1, 2, 3, 4] + func EvilRemove() + call remove(g:lut, 1) + return 1 + endfunc + call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') + unlet g:lut + delfunc EvilRemove endfunc " splitting a string to a List -- cgit From 8d99f53f3dc0815d5515551473367d06669836e0 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 6 Dec 2021 22:51:08 +0000 Subject: vim-patch:8.2.1083: crash when using reduce() on a NULL list Problem: Crash when using reduce() on a NULL list. Solution: Only access the list when not NULL. https://github.com/vim/vim/commit/fda20c4cc59008264676a6deb6a3095ed0c248e0 CHECK_LIST_MATERIALIZE hasn't been ported yet, but presumably if it is ported it'll use tv_list_first to check for range_list_item, which already checks for NULL, so this should need no extra changes and can be a full port. We didn't actually crash here due to the use of Nvim's tv_list functions checking for NULL, but apply these changes to match Vim better anyway. --- src/nvim/eval/funcs.c | 29 ++++++++++++++++------------- src/nvim/testdir/test_listdict.vim | 3 +++ 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c80ff8f36a..bd790bfdd3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7901,22 +7901,25 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_first(l); } - const VarLockStatus prev_locked = tv_list_locked(l); - const int called_emsg_start = called_emsg; - - tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here tv_copy(&initial, rettv); - for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - argv[0] = *rettv; - argv[1] = *TV_LIST_ITEM_TV(li); - rettv->v_type = VAR_UNKNOWN; - const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); - tv_clear(&argv[0]); - if (r == FAIL || called_emsg != called_emsg_start) { - break; + + if (l != NULL) { + const VarLockStatus prev_locked = tv_list_locked(l); + const int called_emsg_start = called_emsg; + + tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + argv[0] = *rettv; + argv[1] = *TV_LIST_ITEM_TV(li); + rettv->v_type = VAR_UNKNOWN; + const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe); + tv_clear(&argv[0]); + if (r == FAIL || called_emsg != called_emsg_start) { + break; + } } + tv_list_set_lock(l, prev_locked); } - tv_list_set_lock(l, prev_locked); } else { const blob_T *const b = argvars[0].vval.v_blob; int i; diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index fdf9123d82..114cca7ec0 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -658,6 +658,9 @@ func Test_reduce() call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') unlet g:lut delfunc EvilRemove + + call assert_equal(42, reduce(v:_null_list, function('add'), 42)) + call assert_equal(42, reduce(v:_null_blob, function('add'), 42)) endfunc " splitting a string to a List -- cgit From 2a58e62145d64245e23825ca717aecf8eaaaddad Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 31 Jan 2022 21:01:04 +0800 Subject: vim-patch:8.2.4267: unused entry in keymap enum (#17255) Problem: Unused entry in keymap enum. Solution: Remove the entry. https://github.com/vim/vim/commit/4c93aff20f228d0dfca11f4793eb2c8895d4984c --- src/nvim/keymap.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 42cae0c35e..ae2ec7835e 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -122,8 +122,6 @@ // // Entries must be in the range 0x02-0x7f (see comment at K_SPECIAL). enum key_extra { - KE_NAME = 3, // name of this terminal entry - KE_S_UP = 4, // shift-up KE_S_DOWN = 5, // shift-down -- cgit From f195345c936eaecf423ab79473003e1ab337a17d Mon Sep 17 00:00:00 2001 From: Shougo Date: Mon, 31 Jan 2022 23:06:46 +0900 Subject: [RFC] fix: has('python') error (#17252) * fix: has('python') error * fix: functional tests --- src/nvim/eval.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cf3322df1b..b8e9f41551 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11004,10 +11004,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo bool eval_has_provider(const char *feat) { if (!strequal(feat, "clipboard") - && !strequal(feat, "python") && !strequal(feat, "python3") - && !strequal(feat, "python_compiled") - && !strequal(feat, "python_dynamic") && !strequal(feat, "python3_compiled") && !strequal(feat, "python3_dynamic") && !strequal(feat, "perl") -- cgit From bba5003bdb628764362d8c4869a1e1999584d716 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 1 Feb 2022 09:31:25 +0800 Subject: vim-patch:8.2.4273: the EBCDIC support is outdated Problem: The EBCDIC support is outdated. Solution: Remove the EBCDIC support. https://github.com/vim/vim/commit/424bcae1fb0f69e0aef5e0cf84fd771cf34a0fb7 Also remove a comment in buf_init_chartab() as it is for enc_dbcs only. Skip test_expr.vim: the check was already removed when patch 7.4.2265 was first ported. --- src/nvim/charset.c | 6 ++---- src/nvim/edit.c | 4 +--- src/nvim/normal.c | 4 +--- src/nvim/regexp.c | 20 ++++++++------------ src/nvim/testdir/test_edit.vim | 8 +------- src/nvim/testdir/test_exec_while_if.vim | 18 +++--------------- src/nvim/testdir/test_gf.vim | 12 ++---------- src/nvim/testdir/test_regexp_utf8.vim | 5 ----- 8 files changed, 18 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 599d662993..583a040ed1 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -217,9 +217,7 @@ int buf_init_chartab(buf_T *buf, int global) } } else if (i == 1) { // (re)set printable - // For double-byte we keep the cell width, so - // that we can detect it from the first byte. - if (((c < ' ') || (c > '~'))) { + if (c < ' ' || c > '~') { if (tilde) { g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + ((dy_flags & DY_UHEX) ? 4 : 2)); @@ -539,7 +537,7 @@ char_u *transchar_buf(const buf_T *buf, int c) c = K_SECOND(c); } - if ((!chartab_initialized && (((c >= ' ') && (c <= '~')))) + if ((!chartab_initialized && (c >= ' ' && c <= '~')) || ((c <= 0xFF) && vim_isprintc_strict(c))) { // printable character transchar_charbuf[i] = (char_u)c; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index f96e7261ca..00ffa7cba1 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -7131,9 +7131,7 @@ int stuff_inserted(int c, long count, int no_esc) stuffReadbuff((const char *)ptr); // A trailing "0" is inserted as "048", "^" as "^". if (last) { - stuffReadbuff((last == '0' - ? "\026\060\064\070" - : "\026^")); + stuffReadbuff(last == '0' ? "\026\060\064\070" : "\026^"); } } while (--count > 0); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6e36226914..c597aed545 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5027,9 +5027,7 @@ static void nv_brackets(cmdarg_T *cap) * identifier "]i" "[i" "]I" "[I" "]^I" "[^I" * define "]d" "[d" "]D" "[D" "]^D" "[^D" */ - if (vim_strchr((char_u *) - "iI\011dD\004", - cap->nchar) != NULL) { + if (vim_strchr((char_u *)"iI\011dD\004", cap->nchar) != NULL) { char_u *ptr; size_t len; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index c8508179a1..ba234bc841 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2901,18 +2901,14 @@ static int peekchr(void) { int c = regparse[1]; - if (c == NUL) - curchr = '\\'; /* trailing '\' */ - else if ( - c <= '~' && META_flags[c] - ) { - /* - * META contains everything that may be magic sometimes, - * except ^ and $ ("\^" and "\$" are only magic after - * "\V"). We now fetch the next character and toggle its - * magicness. Therefore, \ is so meta-magic that it is - * not in META. - */ + if (c == NUL) { + curchr = '\\'; // trailing '\' + } else if (c <= '~' && META_flags[c]) { + // META contains everything that may be magic sometimes, + // except ^ and $ ("\^" and "\$" are only magic after + // "\V"). We now fetch the next character and toggle its + // magicness. Therefore, \ is so meta-magic that it is + // not in META. curchr = -1; prev_at_start = at_start; at_start = false; // be able to say "/\*ptr" diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index c1f74e7675..17393d6edb 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -1006,8 +1006,6 @@ func Test_edit_DROP() endfunc func Test_edit_CTRL_V() - CheckNotFeature ebcdic - new call setline(1, ['abc']) call cursor(2, 1) @@ -1561,11 +1559,7 @@ endfunc func Test_edit_special_chars() new - if has("ebcdic") - let t = "o\193\xc2\o303 \90a\xfg\o578\" - else - let t = "o\65\x42\o103 \33a\xfg\o78\" - endif + let t = "o\65\x42\o103 \33a\xfg\o78\" exe "normal " . t call assert_equal("ABC !a\g\8", getline(2)) diff --git a/src/nvim/testdir/test_exec_while_if.vim b/src/nvim/testdir/test_exec_while_if.vim index 3da2784d77..3f13b09945 100644 --- a/src/nvim/testdir/test_exec_while_if.vim +++ b/src/nvim/testdir/test_exec_while_if.vim @@ -6,11 +6,7 @@ func Test_exec_while_if() let i = 0 while i < 12 let i = i + 1 - if has("ebcdic") - execute "normal o" . i . "\047" - else - execute "normal o" . i . "\033" - endif + execute "normal o" . i . "\033" if i % 2 normal Ax if i == 9 @@ -21,21 +17,13 @@ func Test_exec_while_if() else let j = 9 while j > 0 - if has("ebcdic") - execute "normal" j . "a" . j . "\x27" - else - execute "normal" j . "a" . j . "\x1b" - endif + execute "normal" j . "a" . j . "\x1b" let j = j - 1 endwhile endif endif if i == 9 - if has("ebcdic") - execute "normal Az\047" - else - execute "normal Az\033" - endif + execute "normal Az\033" endif endwhile unlet i j diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index 43efd6248e..589899f532 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -19,11 +19,7 @@ func Test_gf_url() call search("^second") call search("URL") call assert_equal("URL://machine.name/tmp/vimtest2b", expand("")) - if has("ebcdic") - set isf=@,240-249,/,.,-,_,+,,,$,:,~,\ - else - set isf=@,48-57,/,.,-,_,+,,,$,~,\ - endif + set isf=@,48-57,/,.,-,_,+,,,$,~,\ call search("^third") call search("name") call assert_equal("URL:\\\\machine.name\\vimtest2c", expand("")) @@ -76,11 +72,7 @@ endfunc " Test for invoking 'gf' on a ${VAR} variable func Test_gf() - if has("ebcdic") - set isfname=@,240-249,/,.,-,_,+,,,$,:,~,{,} - else - set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,} - endif + set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,} call writefile(["Test for gf command"], "Xtest1") if has("unix") diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index c568805f87..b640a6d043 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -152,9 +152,6 @@ func s:classes_test() if has('win32') let identchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€Â‚ƒ„…†‡ˆ‰Š‹ŒÂŽ‘’“”•–—˜™š›œÂžŸ ¡¢£¤¥¦§µÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' let kwordchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' - elseif has('ebcdic') - let identchars_ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€ŒÂŽœž¬®µº¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' - let kwordchars_ok = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz€ŒÂŽœž¬®µº¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' else let identchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' let kwordchars_ok = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzµÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' @@ -166,8 +163,6 @@ func s:classes_test() let fnamechars_ok = '$+,-./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' elseif has('vms') let fnamechars_ok = '#$%+,-./0123456789:;<>ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' - elseif has('ebcdic') - let fnamechars_ok = '#$%+,-./=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' else let fnamechars_ok = '#$%+,-./0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖרÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ' endif -- cgit From a562b5771ea91becd0a469378ec852feaf50d2d0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 1 Feb 2022 08:35:28 +0100 Subject: vim-patch:8.2.4274: Basic and form filetype detection is incomplete (#17259) Problem: Basic and form filetype detection is incomplete. Solution: Add a separate function for .frm files. (Doug Kearns, closes vim/vim#9675) https://github.com/vim/vim/commit/c570e9cf68c0fe30366e82c96be460047dd659b9 --- src/nvim/testdir/test_filetype.vim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index dcf20e2c1b..d4e5563865 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -183,6 +183,7 @@ let s:filename_checks = { \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], \ 'fish': ['file.fish'], \ 'focexec': ['file.fex', 'file.focexec'], + \ 'form': ['file.frm'], \ 'forth': ['file.ft', 'file.fth'], \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'], \ 'fpcmake': ['file.fpc'], @@ -1249,4 +1250,31 @@ func Test_bas_file() filetype off endfunc +func Test_frm_file() + filetype on + + call writefile(['looks like FORM'], 'Xfile.frm') + split Xfile.frm + call assert_equal('form', &filetype) + bwipe! + + " Test dist#ft#FTfrm() + + let g:filetype_frm = 'form' + split Xfile.frm + call assert_equal('form', &filetype) + bwipe! + unlet g:filetype_frm + + " Visual Basic + + call writefile(['Begin VB.Form Form1'], 'Xfile.frm') + split Xfile.frm + call assert_equal('vb', &filetype) + bwipe! + + call delete('Xfile.frm') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 7d72076a6f3bd3cb85100c94bfeb7a70de345c3b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 30 Jan 2022 14:53:56 +0800 Subject: vim-patch:8.2.3219: :find searches non-existing directories Problem: :find searches non-existing directories. Solution: Check the path is not "..". Update help. (Christian Brabandt, closes vim/vim#8612, closes vim/vim#8533) https://github.com/vim/vim/commit/7a4ca32175bef0f9a177052796bd9addd10dc218 Change STRNCAT to STRLCAT as clint doesn't like the former. Include a typo fix from https://github.com/vim/vim/commit/2f0936cb9a2eb026acac03e6a8fd0b2a5d97508b#diff-7e9292cae1f2ba70dd5b17d2d162693a91044ada6ac99e9c3e8917f32878c097 --- src/nvim/file_search.c | 8 +++++++- src/nvim/testdir/test_findfile.vim | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b4becb3066..25dbf680de 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -483,8 +483,14 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le int len = 0; if (p > search_ctx->ffsc_fix_path) { + // do not add '..' to the path and start upwards searching len = (int)(p - search_ctx->ffsc_fix_path) - 1; - STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len); + if ((len >= 2 && STRNCMP(search_ctx->ffsc_fix_path, "..", 2) == 0) + && (len == 2 || search_ctx->ffsc_fix_path[2] == PATHSEP)) { + xfree(buf); + goto error_return; + } + STRLCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, eb_len + (size_t)len + 1); add_pathsep((char *)ff_expand_buffer); } else { len = (int)STRLEN(search_ctx->ffsc_fix_path); diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim index 5a20475d3d..1684c5d30a 100644 --- a/src/nvim/testdir/test_findfile.vim +++ b/src/nvim/testdir/test_findfile.vim @@ -226,4 +226,26 @@ func Test_find_cmd() call assert_fails('tabfind', 'E471:') endfunc +func Test_find_non_existing_path() + new + let save_path = &path + let save_dir = getcwd() + call mkdir('dir1/dir2', 'p') + call writefile([], 'dir1/file.txt') + call writefile([], 'dir1/dir2/base.txt') + call chdir('dir1/dir2') + e base.txt + set path=../include + + call assert_fails(':find file.txt', 'E345:') + + call chdir(save_dir) + bw! + call delete('dir1/dir2/base.txt', 'rf') + call delete('dir1/dir2', 'rf') + call delete('dir1/file.txt', 'rf') + call delete('dir1', 'rf') + let &path = save_path +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 4aa0cdd3aa117e032325edeb755107acd4ecbf84 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 24 Jan 2022 09:36:15 +0000 Subject: feat(highlight): ns=0 to set :highlight namespace Passing ns=0 to nvim_set_hl will alter the `:highlight` namespace. --- src/nvim/api/vim.c | 18 +++++++--- src/nvim/highlight.c | 32 +++++++++++------- src/nvim/highlight_defs.h | 12 +++++++ src/nvim/syntax.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7c194935ce..af743eb63c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -31,6 +31,7 @@ #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_defs.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" @@ -121,7 +122,9 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// Set a highlight group. /// -/// @param ns_id number of namespace for this highlight +/// @param ns_id number of namespace for this highlight. Use value 0 +/// to set a highlight group in the global (`:highlight`) +/// namespace. /// @param name highlight group name, like ErrorMsg /// @param val highlight definition map, like |nvim_get_hl_by_name|. /// in addition the following keys are also recognized: @@ -135,18 +138,23 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// same as attributes of gui color /// @param[out] err Error details, if any /// -/// TODO: ns_id = 0, should modify :highlight namespace -/// TODO val should take update vs reset flag +// TODO(bfredl): val should take update vs reset flag void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) FUNC_API_SINCE(7) { int hl_id = syn_check_group(name.data, (int)name.size); int link_id = -1; - HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); + HlAttrNames *names = NULL; // Only used when setting global namespace + if (ns_id == 0) { + names = xmalloc(sizeof(*names)); + *names = HLATTRNAMES_INIT; + } + HlAttrs attrs = dict2hlattrs(val, true, &link_id, names, err); if (!ERROR_SET(err)) { - ns_hl_def((NS)ns_id, hl_id, attrs, link_id); + ns_hl_def((NS)ns_id, hl_id, attrs, link_id, names); } + xfree(names); } /// Set active namespace for highlights. diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 3050ca02de..83a6ccf38f 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -144,13 +144,19 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en) } } -void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id) +void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, HlAttrNames *names) { - DecorProvider *p = get_decor_provider(ns_id, true); if ((attrs.rgb_ae_attr & HL_DEFAULT) && map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) { return; } + if (ns_id == 0) { + assert(names); + // set in global (':highlight') namespace + set_hl_group(hl_id, attrs, names, link_id); + return; + } + DecorProvider *p = get_decor_provider(ns_id, true); int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs); ColorItem it = { .attr_id = attr_id, .link_id = link_id, @@ -194,7 +200,7 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) if (ret.type == kObjectTypeDictionary) { Dictionary dict = ret.data.dictionary; fallback = false; - attrs = dict2hlattrs(dict, true, &it.link_id, &err); + attrs = dict2hlattrs(dict, true, &it.link_id, NULL, &err); for (size_t i = 0; i < dict.size; i++) { char *key = dict.items[i].key.data; Object val = dict.items[i].value; @@ -796,7 +802,7 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) return hl; } -HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) +HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, HlAttrNames *names, Error *err) { HlAttrs hlattrs = HLATTRS_INIT; @@ -820,7 +826,7 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) { "italic", HL_ITALIC }, { "reverse", HL_INVERSE }, { "default", HL_DEFAULT }, - { "global", HL_GLOBAL }, + // { "global", HL_GLOBAL }, { NULL, 0 }, }; @@ -857,13 +863,14 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) const char *name; const char *shortname; int *dest; + char **dest_name; } colors[] = { - { "foreground", "fg", &fg }, - { "background", "bg", &bg }, - { "ctermfg", NULL, &ctermfg }, - { "ctermbg", NULL, &ctermbg }, - { "special", "sp", &sp }, - { NULL, NULL, NULL }, + { "foreground", "fg", &fg, names ? &names->fg_name : NULL }, + { "background", "bg", &bg, names ? &names->bg_name : NULL }, + { "ctermfg", NULL, &ctermfg, NULL }, + { "ctermbg", NULL, &ctermbg, NULL }, + { "special", "sp", &sp, names ? &names->sp_name : NULL }, + { NULL, NULL, NULL, NULL }, }; int k; @@ -876,6 +883,9 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, Error *err) // TODO(bfredl): be more fancy with "bg", "fg" etc if (str.size) { *colors[k].dest = name_to_color(str.data); + if (colors[k].dest_name) { + *colors[k].dest_name = str.data; + } } } else { api_set_error(err, kErrorTypeValidation, diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 50a03e0c02..65374782b6 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -46,6 +46,18 @@ typedef struct attr_entry { .hl_blend = -1, \ } +typedef struct { + char *bg_name; + char *fg_name; + char *sp_name; +} HlAttrNames; + +#define HLATTRNAMES_INIT (HlAttrNames) { \ + .bg_name = NULL, \ + .fg_name = NULL, \ + .sp_name = NULL, \ +} + /// Values for index in highlight_attr[]. /// When making changes, also update hlf_names below! typedef enum { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a9447165c2..b383b290ba 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6714,6 +6714,90 @@ int lookup_color(const int idx, const bool foreground, TriState *const boldp) return color; } +void set_hl_group(int id, HlAttrs attrs, HlAttrNames *names, int link_id) +{ + int idx = id - 1; // Index is ID minus one. + + bool is_default = attrs.rgb_ae_attr & HL_DEFAULT; + + // Return if "default" was used and the group already has settings + if (is_default && hl_has_settings(idx, true)) { + return; + } + + HlGroup *g = &HL_TABLE()[idx]; + + if (link_id > 0) { + g->sg_cleared = false; + g->sg_link = link_id; + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + g->sg_set |= SG_LINK; + if (is_default) { + g->sg_deflink = link_id; + g->sg_deflink_sctx = current_sctx; + g->sg_deflink_sctx.sc_lnum += sourcing_lnum; + } + return; + } + + g->sg_cleared = false; + g->sg_link = 0; + g->sg_gui = attrs.rgb_ae_attr; + + g->sg_rgb_fg = attrs.rgb_fg_color; + g->sg_rgb_bg = attrs.rgb_bg_color; + g->sg_rgb_sp = attrs.rgb_sp_color; + + struct { + char **dest; RgbValue val; char *name; + } cattrs[] = { + { &g->sg_rgb_fg_name, g->sg_rgb_fg, names->fg_name }, + { &g->sg_rgb_bg_name, g->sg_rgb_bg, names->bg_name }, + { &g->sg_rgb_sp_name, g->sg_rgb_sp, names->sp_name }, + { NULL, -1, NULL }, + }; + + for (int j = 0; cattrs[j].dest; j++) { + if (cattrs[j].val != -1) { + xfree(*cattrs[j].dest); + if (cattrs[j].name) { + *cattrs[j].dest = xstrdup(cattrs[j].name); + } else { + char hex_name[8]; + snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); + *cattrs[j].dest = xstrdup(hex_name); + } + } + } + + g->sg_cterm = attrs.cterm_ae_attr; + g->sg_cterm_bg = attrs.cterm_bg_color; + g->sg_cterm_fg = attrs.cterm_fg_color; + g->sg_cterm_bold = g->sg_cterm & HL_BOLD; + g->sg_blend = attrs.hl_blend; + + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + + // 'Normal' is special + if (STRCMP(g->sg_name_u, "NORMAL") == 0) { + cterm_normal_fg_color = g->sg_cterm_fg; + cterm_normal_bg_color = g->sg_cterm_bg; + normal_fg = g->sg_rgb_fg; + normal_bg = g->sg_rgb_bg; + normal_sp = g->sg_rgb_sp; + ui_default_colors_set(); + } else { + g->sg_attr = hl_get_syn_attr(0, id, attrs); + + // a cursor style uses this syn_id, make sure its attribute is updated. + if (cursor_mode_uses_syn_id(id)) { + ui_mode_info_set(); + } + } +} + /// Handle ":highlight" command /// -- cgit From 3e689737557ac968e1e62a92a22d33c887bc51bd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Feb 2022 09:46:25 +0800 Subject: vim-patch:8.2.3966: when using feedkeys() abbreviations may be blocked Problem: When using feedkeys() abbreviations may be blocked. Solution: Reset tb_no_abbr_cnt when running out of characters. (closes vim/vim#9448) https://github.com/vim/vim/commit/b37a65e4bf08c4eec4fa5b81a5efc3945fca44de --- src/nvim/getchar.c | 4 ++++ src/nvim/testdir/test_feedkeys.vim | 12 ++++++++++++ 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index d3641032ab..5d8a8ddbfe 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2304,6 +2304,10 @@ static int vgetorpeek(bool advance) c = ESC; } tc = c; + + // no chars to block abbreviations for + typebuf.tb_no_abbr_cnt = 0; + break; } diff --git a/src/nvim/testdir/test_feedkeys.vim b/src/nvim/testdir/test_feedkeys.vim index 70500f2bb5..f343b0174c 100644 --- a/src/nvim/testdir/test_feedkeys.vim +++ b/src/nvim/testdir/test_feedkeys.vim @@ -12,3 +12,15 @@ func Test_feedkeys_x_with_empty_string() call assert_equal('foo', getline('.')) quit! endfunc + +func Test_feedkeys_with_abbreviation() + new + inoreabbrev trigger value + call feedkeys("atrigger ", 'x') + call feedkeys("atrigger ", 'x') + call assert_equal('value value ', getline(1)) + bwipe! + iunabbrev trigger +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 1bce6d6e16283a5de46e4c0b9d80742f226bea66 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Feb 2022 21:33:46 +0800 Subject: vim-patch:8.2.3947: unnecessary check for NULL pointer Problem: Unnecessary check for NULL pointer. Solution: Remove the check. (closes vim/vim#9434) https://github.com/vim/vim/commit/f38aad85cf8e4e930c96cb843bc136949c8dbd29 Reorder the two if branches to match upstream. --- src/nvim/ex_docmd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 07fbf1c61a..b859d29a86 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7870,9 +7870,11 @@ bool changedir_func(char_u *new_dir, CdScope scope) new_dir = NameBuff; } - bool dir_differs = new_dir == NULL || pdir == NULL - || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; - if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) { + bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; + if (dir_differs && vim_chdir(new_dir) != 0) { + emsg(_(e_failed)); + xfree(pdir); + } else { char_u **pp; switch (scope) { @@ -7890,9 +7892,6 @@ bool changedir_func(char_u *new_dir, CdScope scope) post_chdir(scope, dir_differs); retval = true; - } else { - emsg(_(e_failed)); - xfree(pdir); } return retval; -- cgit From cd44f0a401dce1cdf2efad80a3477e6dc8b9d061 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Feb 2022 21:33:46 +0800 Subject: vim-patch:8.2.4283: using a variable for the return value is not needed Problem: Using a variable for the return value is not needed. Solution: Return the value directly. (closes vim/vim#9687) https://github.com/vim/vim/commit/73257149d759a8e6ddbe555d2b5aa37b6cb8db8b Also move down variable declarations in changedir_func(). vim_chdirfile() doesn't need change. --- src/nvim/ex_docmd.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index b859d29a86..b94fe47c17 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7835,13 +7835,11 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) /// @return true if the directory is successfully changed. bool changedir_func(char_u *new_dir, CdScope scope) { - char_u *pdir = NULL; - bool retval = false; - if (new_dir == NULL || allbuf_locked()) { return false; } + char_u *pdir = NULL; // ":cd -": Change to previous directory if (STRCMP(new_dir, "-") == 0) { pdir = get_prevdir(scope); @@ -7874,27 +7872,26 @@ bool changedir_func(char_u *new_dir, CdScope scope) if (dir_differs && vim_chdir(new_dir) != 0) { emsg(_(e_failed)); xfree(pdir); - } else { - char_u **pp; - - switch (scope) { - case kCdScopeTabpage: - pp = &curtab->tp_prevdir; - break; - case kCdScopeWindow: - pp = &curwin->w_prevdir; - break; - default: - pp = &prev_dir; - } - xfree(*pp); - *pp = pdir; + return false; + } - post_chdir(scope, dir_differs); - retval = true; + char_u **pp; + switch (scope) { + case kCdScopeTabpage: + pp = &curtab->tp_prevdir; + break; + case kCdScopeWindow: + pp = &curwin->w_prevdir; + break; + default: + pp = &prev_dir; } + xfree(*pp); + *pp = pdir; - return retval; + post_chdir(scope, dir_differs); + + return true; } /// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir". -- cgit From 0bafa44f8b9c4ad2a1cf5233bc207cba522a5dfe Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 2 Feb 2022 22:01:52 +0100 Subject: refactor(api): use a keyset for highlight dicts --- src/nvim/api/keysets.lua | 27 +++++ src/nvim/api/vim.c | 12 +- src/nvim/generators/gen_keysets.lua | 3 +- src/nvim/highlight.c | 211 +++++++++++++++++------------------- src/nvim/highlight_defs.h | 12 -- src/nvim/syntax.c | 16 +-- 6 files changed, 141 insertions(+), 140 deletions(-) (limited to 'src') diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 7d521bbf25..075e2c48d2 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -78,5 +78,32 @@ return { option = { "scope"; }; + highlight = { + "bold"; + "standout"; + "underline"; + "undercurl"; + "italic"; + "reverse"; + "default"; + "global"; + "cterm"; + "foreground"; "fg"; + "background"; "bg"; + "ctermfg"; + "ctermbg"; + "special"; "sp"; + "link"; + "fallback"; + "temp"; + }; + highlight_cterm = { + "bold"; + "standout"; + "underline"; + "undercurl"; + "italic"; + "reverse"; + }; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index e9182fde7f..ada041bab2 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -140,22 +140,16 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// @param[out] err Error details, if any /// // TODO(bfredl): val should take update vs reset flag -void nvim_set_hl(Integer ns_id, String name, Dictionary val, Error *err) +void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) FUNC_API_SINCE(7) { int hl_id = syn_check_group(name.data, (int)name.size); int link_id = -1; - HlAttrNames *names = NULL; // Only used when setting global namespace - if (ns_id == 0) { - names = xmalloc(sizeof(*names)); - *names = HLATTRNAMES_INIT; - } - HlAttrs attrs = dict2hlattrs(val, true, &link_id, names, err); + HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); if (!ERROR_SET(err)) { - ns_hl_def((NS)ns_id, hl_id, attrs, link_id, names); + ns_hl_def((NS)ns_id, hl_id, attrs, link_id, val); } - xfree(names); } /// Set active namespace for highlights. diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua index 01d8c1d357..633c5da184 100644 --- a/src/nvim/generators/gen_keysets.lua +++ b/src/nvim/generators/gen_keysets.lua @@ -27,7 +27,8 @@ local defspipe = io.open(defs_file, 'wb') local keysets = require'api.keysets' local keywords = { - register = true, + register = true; + default = true; } local function sanitize(key) diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 83a6ccf38f..87c090e594 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -144,16 +144,16 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en) } } -void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, HlAttrNames *names) +void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict) { if ((attrs.rgb_ae_attr & HL_DEFAULT) && map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) { return; } if (ns_id == 0) { - assert(names); + assert(dict); // set in global (':highlight') namespace - set_hl_group(hl_id, attrs, names, link_id); + set_hl_group(hl_id, attrs, dict, link_id); return; } DecorProvider *p = get_decor_provider(ns_id, true); @@ -198,23 +198,17 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault) int tmp = false; HlAttrs attrs = HLATTRS_INIT; if (ret.type == kObjectTypeDictionary) { - Dictionary dict = ret.data.dictionary; fallback = false; - attrs = dict2hlattrs(dict, true, &it.link_id, NULL, &err); - for (size_t i = 0; i < dict.size; i++) { - char *key = dict.items[i].key.data; - Object val = dict.items[i].value; - bool truthy = api_object_to_bool(val, key, false, &err); - - if (strequal(key, "fallback")) { - fallback = truthy; - } else if (strequal(key, "temp")) { - tmp = truthy; + Dict(highlight) dict = { 0 }; + if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field, + ret.data.dictionary, &err)) { + attrs = dict2hlattrs(&dict, true, &it.link_id, &err); + fallback = api_object_to_bool(dict.fallback, "fallback", true, &err); + tmp = api_object_to_bool(dict.fallback, "tmp", false, &err); + if (it.link_id >= 0) { + fallback = true; } } - if (it.link_id >= 0) { - fallback = true; - } } it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs); @@ -802,116 +796,98 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) return hl; } -HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, HlAttrNames *names, Error *err) +HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *err) { HlAttrs hlattrs = HLATTRS_INIT; - int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1; int16_t mask = 0; int16_t cterm_mask = 0; bool cterm_mask_provided = false; - for (size_t i = 0; i < dict.size; i++) { - char *key = dict.items[i].key.data; - Object val = dict.items[i].value; - - struct { - const char *name; - int16_t flag; - } flags[] = { - { "bold", HL_BOLD }, - { "standout", HL_STANDOUT }, - { "underline", HL_UNDERLINE }, - { "undercurl", HL_UNDERCURL }, - { "italic", HL_ITALIC }, - { "reverse", HL_INVERSE }, - { "default", HL_DEFAULT }, - // { "global", HL_GLOBAL }, - { NULL, 0 }, - }; - - int j; - for (j = 0; flags[j].name; j++) { - if (strequal(flags[j].name, key)) { - if (api_object_to_bool(val, key, false, err)) { - mask = mask | flags[j].flag; - } - break; - } +#define CHECK_FLAG(d, m, name, extra, flag) \ + if (api_object_to_bool(d->name ## extra, #name, false, err)) { \ + m = m | flag; \ } - // Handle cterm attrs - if (strequal(key, "cterm") && val.type == kObjectTypeDictionary) { - cterm_mask_provided = true; - Dictionary cterm_dict = val.data.dictionary; - for (size_t l = 0; l < cterm_dict.size; l++) { - char *cterm_dict_key = cterm_dict.items[l].key.data; - Object cterm_dict_val = cterm_dict.items[l].value; - for (int m = 0; flags[m].name; m++) { - if (strequal(flags[m].name, cterm_dict_key)) { - if (api_object_to_bool(cterm_dict_val, cterm_dict_key, false, - err)) { - cterm_mask |= flags[m].flag; - } - break; - } - } + CHECK_FLAG(dict, mask, bold, , HL_BOLD); + CHECK_FLAG(dict, mask, standout, , HL_STANDOUT); + CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE); + CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL); + CHECK_FLAG(dict, mask, italic, , HL_ITALIC); + CHECK_FLAG(dict, mask, reverse, , HL_INVERSE); + CHECK_FLAG(dict, mask, default, _, HL_DEFAULT); + CHECK_FLAG(dict, mask, global, , HL_GLOBAL); + + if (HAS_KEY(dict->fg)) { + fg = object_to_color(dict->fg, "fg", err); + } else if (HAS_KEY(dict->foreground)) { + fg = object_to_color(dict->foreground, "foreground", err); + } + if (ERROR_SET(err)) { + return hlattrs; + } + + if (HAS_KEY(dict->bg)) { + bg = object_to_color(dict->bg, "bg", err); + } else if (HAS_KEY(dict->background)) { + bg = object_to_color(dict->background, "background", err); + } + if (ERROR_SET(err)) { + return hlattrs; + } + + if (HAS_KEY(dict->sp)) { + sp = object_to_color(dict->sp, "sp", err); + } else if (HAS_KEY(dict->special)) { + sp = object_to_color(dict->special, "special", err); + } + if (ERROR_SET(err)) { + return hlattrs; + } + + if (HAS_KEY(dict->link)) { + if (link_id) { + *link_id = object_to_hl_id(dict->link, "link", err); + if (ERROR_SET(err)) { + return hlattrs; } + } else { + api_set_error(err, kErrorTypeValidation, "Invalid Key: 'link'"); } + } - struct { - const char *name; - const char *shortname; - int *dest; - char **dest_name; - } colors[] = { - { "foreground", "fg", &fg, names ? &names->fg_name : NULL }, - { "background", "bg", &bg, names ? &names->bg_name : NULL }, - { "ctermfg", NULL, &ctermfg, NULL }, - { "ctermbg", NULL, &ctermbg, NULL }, - { "special", "sp", &sp, names ? &names->sp_name : NULL }, - { NULL, NULL, NULL, NULL }, - }; - - int k; - for (k = 0; (!flags[j].name) && colors[k].name; k++) { - if (strequal(colors[k].name, key) || strequal(colors[k].shortname, key)) { - if (val.type == kObjectTypeInteger) { - *colors[k].dest = (int)val.data.integer; - } else if (val.type == kObjectTypeString) { - String str = val.data.string; - // TODO(bfredl): be more fancy with "bg", "fg" etc - if (str.size) { - *colors[k].dest = name_to_color(str.data); - if (colors[k].dest_name) { - *colors[k].dest_name = str.data; - } - } - } else { - api_set_error(err, kErrorTypeValidation, - "'%s' must be string or integer", key); - } - break; - } + // Handle cterm attrs + if (dict->cterm.type == kObjectTypeDictionary) { + Dict(highlight_cterm) cterm[1] = { 0 }; + if (!api_dict_to_keydict(cterm, KeyDict_highlight_cterm_get_field, + dict->cterm.data.dictionary, err)) { + return hlattrs; } - if (flags[j].name || colors[k].name) { - // handled above - } else if (link_id && strequal(key, "link")) { - if (val.type == kObjectTypeString) { - String str = val.data.string; - *link_id = syn_check_group(str.data, (int)str.size); - } else if (val.type == kObjectTypeInteger) { - // TODO(bfredl): validate range? - *link_id = (int)val.data.integer; - } else { - api_set_error(err, kErrorTypeValidation, - "'link' must be string or integer"); - } + cterm_mask_provided = true; + CHECK_FLAG(cterm, cterm_mask, bold, , HL_BOLD); + CHECK_FLAG(cterm, cterm_mask, standout, , HL_STANDOUT); + CHECK_FLAG(cterm, cterm_mask, underline, , HL_UNDERLINE); + CHECK_FLAG(cterm, cterm_mask, undercurl, , HL_UNDERCURL); + CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC); + CHECK_FLAG(cterm, cterm_mask, reverse, , HL_INVERSE); + + } else if (HAS_KEY(dict->cterm)) { + api_set_error(err, kErrorTypeValidation, "'cterm' must be a Dictionary."); + } +#undef CHECK_FLAG + + if (HAS_KEY(dict->ctermfg)) { + ctermfg = object_to_color(dict->ctermfg, "ctermfg", err); + if (ERROR_SET(err)) { + return hlattrs; } + } + if (HAS_KEY(dict->ctermbg)) { + ctermbg = object_to_color(dict->ctermbg, "ctermbg", err); if (ERROR_SET(err)) { - return hlattrs; // error set, caller should not use retval + return hlattrs; } } @@ -937,6 +913,21 @@ HlAttrs dict2hlattrs(Dictionary dict, bool use_rgb, int *link_id, HlAttrNames *n return hlattrs; } + +int object_to_color(Object val, char *key, Error *err) +{ + if (val.type == kObjectTypeInteger) { + return (int)val.data.integer; + } else if (val.type == kObjectTypeString) { + String str = val.data.string; + // TODO(bfredl): be more fancy with "bg", "fg" etc + return str.size ? name_to_color(str.data) : 0; + } else { + api_set_error(err, kErrorTypeValidation, "'%s' must be string or integer", key); + return 0; + } +} + Array hl_inspect(int attr) { Array ret = ARRAY_DICT_INIT; diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 65374782b6..50a03e0c02 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -46,18 +46,6 @@ typedef struct attr_entry { .hl_blend = -1, \ } -typedef struct { - char *bg_name; - char *fg_name; - char *sp_name; -} HlAttrNames; - -#define HLATTRNAMES_INIT (HlAttrNames) { \ - .bg_name = NULL, \ - .fg_name = NULL, \ - .sp_name = NULL, \ -} - /// Values for index in highlight_attr[]. /// When making changes, also update hlf_names below! typedef enum { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index b383b290ba..3aef654a8e 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6714,7 +6714,7 @@ int lookup_color(const int idx, const bool foreground, TriState *const boldp) return color; } -void set_hl_group(int id, HlAttrs attrs, HlAttrNames *names, int link_id) +void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) { int idx = id - 1; // Index is ID minus one. @@ -6750,19 +6750,19 @@ void set_hl_group(int id, HlAttrs attrs, HlAttrNames *names, int link_id) g->sg_rgb_sp = attrs.rgb_sp_color; struct { - char **dest; RgbValue val; char *name; + char **dest; RgbValue val; Object name; } cattrs[] = { - { &g->sg_rgb_fg_name, g->sg_rgb_fg, names->fg_name }, - { &g->sg_rgb_bg_name, g->sg_rgb_bg, names->bg_name }, - { &g->sg_rgb_sp_name, g->sg_rgb_sp, names->sp_name }, - { NULL, -1, NULL }, + { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { NULL, -1, NIL }, }; for (int j = 0; cattrs[j].dest; j++) { if (cattrs[j].val != -1) { xfree(*cattrs[j].dest); - if (cattrs[j].name) { - *cattrs[j].dest = xstrdup(cattrs[j].name); + if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + *cattrs[j].dest = xstrdup(cattrs[j].name.data.string.data); } else { char hex_name[8]; snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); -- cgit From f70d1442bedcc5d2dfba903055769bedb444bdb3 Mon Sep 17 00:00:00 2001 From: quentin Date: Thu, 3 Feb 2022 01:36:48 +0100 Subject: build(win): package the debug symbols (PDB file) along with nvim.exe --- src/nvim/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 94572b57cd..cc4f9bcdcf 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -493,6 +493,7 @@ add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) +install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) set_property(TARGET nvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) -- cgit From 74998b0449c4df0494c3bfe5d4034c575d972406 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 3 Feb 2022 13:43:48 +0800 Subject: fix(event-loop): call vpeekc() directly first to check for character Expand mappings first by calling `vpeekc()` directly. --- src/nvim/eval/funcs.c | 2 +- src/nvim/state.c | 22 +++++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 29619f62e9..115f3a6cb0 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3180,7 +3180,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type == VAR_UNKNOWN) { // getchar(): blocking wait. // TODO(bfredl): deduplicate shared logic with state_enter ? - if (!(char_avail() || using_script() || input_available())) { + if (!char_avail()) { (void)os_inchar(NULL, 0, -1, 0, main_loop.events); if (!multiqueue_empty(main_loop.events)) { state_handle_k_event(); diff --git a/src/nvim/state.c b/src/nvim/state.c index 9e4c9b2bad..f9a3aaab7f 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -39,10 +39,16 @@ void state_enter(VimState *s) int key; getkey: - if (char_avail() || using_script() || input_available()) { - // Don't block for events if there's a character already available for - // processing. Characters can come from mappings, scripts and other - // sources, so this scenario is very common. + // Expand mappings first by calling vpeekc() directly. + // - If vpeekc() returns non-NUL, there is a character already available for processing, so + // don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence. + // - If vpeekc() returns NUL, vgetc() will block, and there are three cases: + // - There is no input available. + // - All of available input maps to an empty string. + // - There is an incomplete mapping. + // A blocking wait for a character should only be done in the third case, which is the only + // case of the three where typebuf.tb_len > 0 after vpeekc() returns NUL. + if (vpeekc() != NUL || typebuf.tb_len > 0) { key = safe_vgetc(); } else if (!multiqueue_empty(main_loop.events)) { // Event was made available after the last multiqueue_process_events call @@ -55,9 +61,11 @@ getkey: // mapping engine. (void)os_inchar(NULL, 0, -1, 0, main_loop.events); // If an event was put into the queue, we send K_EVENT directly. - key = !multiqueue_empty(main_loop.events) - ? K_EVENT - : safe_vgetc(); + if (!multiqueue_empty(main_loop.events)) { + key = K_EVENT; + } else { + goto getkey; + } } if (key == K_EVENT) { -- cgit From 5fe6bbae2da82b41caae3742df02287e1da1477a Mon Sep 17 00:00:00 2001 From: Quentin Minster Date: Thu, 3 Feb 2022 07:52:27 +0100 Subject: build(win): don't try to package a PDB if not building with MSVC… MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: zeertzjq --- src/nvim/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index cc4f9bcdcf..dcc20194f0 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -493,7 +493,9 @@ add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} ${EXTERNAL_SOURCES} ${EXTERNAL_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) -install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) +if(MSVC) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) +endif() set_property(TARGET nvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) -- cgit From f326c9a77da3aef052ce6af0f851344f2479c844 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 6 Jan 2022 13:48:37 +0000 Subject: vim-patch:8.2.4018: ml_get error when win_execute redraws with Visual selection Problem: ml_get error when win_execute redraws with Visual selection. Solution: Disable Visual area temporarily. (closes vim/vim#9479) https://github.com/vim/vim/commit/18f4740f043b353abe47b7a00131317052457686 {switch_to/restore}_win_for_buf is N/A (marked as such in v8.0.0860; currently only used in Vim's if_py). Add a modeline to test_execute_func.vim. --- src/nvim/api/private/helpers.c | 10 +++---- src/nvim/api/window.c | 9 ++---- src/nvim/eval.c | 17 +++++------ src/nvim/eval/funcs.c | 23 ++++++--------- src/nvim/move.c | 6 ++-- src/nvim/testdir/test_execute_func.vim | 12 ++++++++ src/nvim/window.c | 53 +++++++++++++++++++++------------- src/nvim/window.h | 8 +++++ 8 files changed, 79 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 4ddd90f3c9..ddcfff0097 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -989,18 +989,16 @@ Object copy_object(Object obj) static void set_option_value_for(char *key, int numval, char *stringval, int opt_flags, int opt_type, void *from, Error *err) { - win_T *save_curwin = NULL; - tabpage_T *save_curtab = NULL; + switchwin_T switchwin; aco_save_T aco; try_start(); switch (opt_type) { case SREQ_WIN: - if (switch_win_noblock(&save_curwin, &save_curtab, (win_T *)from, - win_find_tabpage((win_T *)from), true) + if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) == FAIL) { - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); if (try_end(err)) { return; } @@ -1010,7 +1008,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt return; } set_option_value_err(key, numval, stringval, opt_flags, err); - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); break; case SREQ_BUF: aucmd_prepbuf(&aco, (buf_T *)from); diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 907306da7b..a762673fbf 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -455,17 +455,14 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) } tabpage_T *tabpage = win_find_tabpage(win); - win_T *save_curwin; - tabpage_T *save_curtab; - try_start(); Object res = OBJECT_INIT; - if (switch_win_noblock(&save_curwin, &save_curtab, win, tabpage, true) == - OK) { + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, win, tabpage, true) == OK) { Array args = ARRAY_DICT_INIT; res = nlua_call_ref(fun, NULL, args, true, err); } - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); try_end(err); return res; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8e9f41551..5d9326eb24 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6963,10 +6963,9 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp) /// @param off 1 for gettabwinvar() void getwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win, *oldcurwin; + win_T *win; dictitem_T *v; tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; bool done = false; if (off == 1) { @@ -6986,8 +6985,8 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) // otherwise the window is not valid. Only do this when needed, // autocommands get blocked. bool need_switch_win = tp != curtab || win != curwin; - if (!need_switch_win - || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { if (varname[1] == NUL) { // get all window-local options in a dict @@ -7015,7 +7014,7 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) if (need_switch_win) { // restore previous notion of curwin - restore_win(oldcurwin, oldtabpage, true); + restore_win(&switchwin, true); } } emsg_off--; @@ -7517,11 +7516,9 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off) typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { - win_T *save_curwin; - tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; - if (!need_switch_win - || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { long numval; bool error = false; @@ -7543,7 +7540,7 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off) } } if (need_switch_win) { - restore_win(save_curwin, save_curtab, true); + restore_win(&switchwin, true); } } } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 29619f62e9..3cfc00fea2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2176,20 +2176,18 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tp; win_T *wp = win_id2wp_tp(argvars, &tp); - win_T *save_curwin; - tabpage_T *save_curtab; // Return an empty string if something fails. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (wp != NULL && tp != NULL) { pos_T curpos = wp->w_cursor; - if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) == - OK) { + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { check_cursor(); execute_common(argvars, rettv, fptr, 1); } - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); // Update the status line if the cursor moved. if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) { @@ -4031,8 +4029,6 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *oldcurwin; - tabpage_T *oldtabpage; bool done = false; rettv->v_type = VAR_STRING; @@ -4046,7 +4042,8 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) win_T *const window = tp == curtab || tp->tp_firstwin == NULL ? firstwin : tp->tp_firstwin; - if (switch_win(&oldcurwin, &oldtabpage, window, tp, true) == OK) { + switchwin_T switchwin; + if (switch_win(&switchwin, window, tp, true) == OK) { // look up the variable // Let gettabvar({nr}, "") return the "t:" dictionary. const dictitem_T *const v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', @@ -4059,7 +4056,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // restore previous notion of curwin - restore_win(oldcurwin, oldtabpage, true); + restore_win(&switchwin, true); } if (!done && argvars[2].v_type != VAR_UNKNOWN) { @@ -5881,18 +5878,16 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { tabpage_T *tp; - win_T *save_curwin; - tabpage_T *save_curtab; // use window specified in the second argument win_T *wp = win_id2wp_tp(&argvars[1], &tp); if (wp != NULL && tp != NULL) { - if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, true) - == OK) { + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { check_cursor(); fp = var2fpos(&argvars[0], true, &fnum); } - restore_win_noblock(save_curwin, save_curtab, true); + restore_win_noblock(&switchwin, true); } } else { // use current window diff --git a/src/nvim/move.c b/src/nvim/move.c index 67ec19903f..27cc2b341c 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -346,10 +346,10 @@ void update_topline(win_T *wp) */ void update_topline_win(win_T *win) { - win_T *save_curwin; - switch_win(&save_curwin, NULL, win, NULL, true); + switchwin_T switchwin; + switch_win(&switchwin, win, NULL, true); update_topline(curwin); - restore_win(save_curwin, NULL, true); + restore_win(&switchwin, true); } /* diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim index 2cb6d73407..bb601e19ac 100644 --- a/src/nvim/testdir/test_execute_func.vim +++ b/src/nvim/testdir/test_execute_func.vim @@ -147,3 +147,15 @@ func Test_win_execute_other_tab() tabclose unlet xyz endfunc + +func Test_win_execute_visual_redraw() + call setline(1, ['a', 'b', 'c']) + new + wincmd p + call feedkeys("G\", 'txn') + call win_execute(winnr('#')->win_getid(), 'redraw') + bwipe! + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index 8e44fd5014..6996a93928 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -577,18 +577,19 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Pren void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) { - win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; + win_T *win = find_window_by_handle(window, err); buf_T *buf = find_buffer_by_handle(buffer, err); - tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab; + tabpage_T *tab = win_find_tabpage(win); if (!win || !buf) { return; } - if (noautocmd) { block_autocmds(); } - if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) { + + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to switch to window %d", @@ -608,7 +609,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) // So do it now. validate_cursor(); - restore_win_noblock(save_curwin, save_curtab, false); + restore_win_noblock(&switchwin, false); if (noautocmd) { unblock_autocmds(); } @@ -6631,20 +6632,27 @@ static win_T *get_snapshot_focus(int idx) /// triggered, another tabpage access is limited. /// /// @return FAIL if switching to "win" failed. -int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, - bool no_display) +int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) { block_autocmds(); - return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display); + return switch_win_noblock(switchwin, win, tp, no_display); } // As switch_win() but without blocking autocommands. -int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, - bool no_display) +int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) { - *save_curwin = curwin; + memset(switchwin, 0, sizeof(switchwin_T)); + switchwin->sw_curwin = curwin; + if (win == curwin) { + switchwin->sw_same_win = true; + } else { + // Disable Visual selection, because redrawing may fail. + switchwin->sw_visual_active = VIsual_active; + VIsual_active = false; + } + if (tp != NULL) { - *save_curtab = curtab; + switchwin->sw_curtab = curtab; if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; @@ -6666,28 +6674,33 @@ int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, // Restore current tabpage and window saved by switch_win(), if still valid. // When "no_display" is true the display won't be affected, no redraw is // triggered. -void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) +void restore_win(switchwin_T *switchwin, bool no_display) { - restore_win_noblock(save_curwin, save_curtab, no_display); + restore_win_noblock(switchwin, no_display); unblock_autocmds(); } // As restore_win() but without unblocking autocommands. -void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) +void restore_win_noblock(switchwin_T *switchwin, bool no_display) { - if (save_curtab != NULL && valid_tabpage(save_curtab)) { + if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; - curtab = save_curtab; + curtab = switchwin->sw_curtab; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; } else { - goto_tabpage_tp(save_curtab, false, false); + goto_tabpage_tp(switchwin->sw_curtab, false, false); } } - if (win_valid(save_curwin)) { - curwin = save_curwin; + + if (!switchwin->sw_same_win) { + VIsual_active = switchwin->sw_visual_active; + } + + if (win_valid(switchwin->sw_curwin)) { + curwin = switchwin->sw_curwin; curbuf = curwin->w_buffer; } // If called by win_execute() and executing the command changed the diff --git a/src/nvim/window.h b/src/nvim/window.h index 7e465a9f08..925cc68b60 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -32,6 +32,14 @@ #define MIN_COLUMNS 12 // minimal columns for screen #define MIN_LINES 2 // minimal lines for screen +/// Structure used by switch_win() to pass values to restore_win() +typedef struct { + win_T *sw_curwin; + tabpage_T *sw_curtab; + bool sw_same_win; ///< VIsual_active was not reset + bool sw_visual_active; +} switchwin_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "window.h.generated.h" #endif -- cgit From d984a8d130c2063dcd33ae52f98c22fe3567cd18 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 7 Jan 2022 17:27:47 +0000 Subject: vim-patch:8.2.4026: ml_get error with specific win_execute() command Problem: ml_get error with specific win_execute() command. (Sean Dewar) Solution: Check cursor and Visual area are OK. https://github.com/vim/vim/commit/e664a327014f4aa8baf8549a34a4caab2f3116a3 --- src/nvim/eval/funcs.c | 7 +++++++ src/nvim/testdir/test_execute_func.vim | 15 +++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3cfc00fea2..b16041b832 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2193,6 +2193,13 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) { wp->w_redr_status = true; } + + // In case the command moved the cursor or changed the Visual area, + // check it is valid. + check_cursor(); + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } } } diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim index bb601e19ac..16cc20e9a7 100644 --- a/src/nvim/testdir/test_execute_func.vim +++ b/src/nvim/testdir/test_execute_func.vim @@ -152,8 +152,23 @@ func Test_win_execute_visual_redraw() call setline(1, ['a', 'b', 'c']) new wincmd p + " start Visual in current window, redraw in other window with fewer lines call feedkeys("G\", 'txn') call win_execute(winnr('#')->win_getid(), 'redraw') + call feedkeys("\", 'txn') + bwipe! + bwipe! + + enew + new + call setline(1, ['a', 'b', 'c']) + let winid = win_getid() + wincmd p + " start Visual in current window, extend it in other window with more lines + call feedkeys("\", 'txn') + call win_execute(winid, 'call feedkeys("G\", ''txn'')') + redraw + bwipe! bwipe! endfunc -- cgit From 6820420d3e7c153868e21d917596b3d1a19fa937 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 7 Jan 2022 17:33:01 +0000 Subject: vim-patch:8.2.4028: ml_get error with :doautoall and Visual area Problem: ml_get error with :doautoall and Visual area. (Sean Dewar) Solution: Disable Visual mode while executing autocommands. https://github.com/vim/vim/commit/cb1956d6f2aece8ad93e19e5d4c7e0b5e405f056 This should also fix #16937 for nvim_buf_call, so test for it. --- src/nvim/autocmd.c | 12 ++++++++++-- src/nvim/autocmd.h | 1 + src/nvim/testdir/test_autocmd.vim | 10 ++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 94ac389139..2e7f9c5136 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1069,8 +1069,6 @@ void ex_doautoall(exarg_T *eap) do_modelines(0); } } - - check_cursor(); // just in case lines got deleted } /// Check *argp for . When it is present return false, otherwise @@ -1171,6 +1169,10 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) curbuf = buf; aco->new_curwin_handle = curwin->handle; set_bufref(&aco->new_curbuf, curbuf); + + // disable the Visual area, the position may be invalid in another buffer + aco->save_VIsual_active = VIsual_active; + VIsual_active = false; } /// Cleanup after executing autocommands for a (hidden) buffer. @@ -1267,6 +1269,12 @@ win_found: check_cursor(); } } + + check_cursor(); // just in case lines got deleted + VIsual_active = aco->save_VIsual_active; + if (VIsual_active) { + check_pos(curbuf, &VIsual); + } } /// Execute autocommands for "event" and file name "fname". diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index ac12e2acf3..63c5abd4f8 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -14,6 +14,7 @@ typedef struct { handle_T save_prevwin_handle; ///< ID of saved prevwin bufref_T new_curbuf; ///< new curbuf char_u *globaldir; ///< saved value of globaldir + bool save_VIsual_active; ///< saved VIsual_active } aco_save_T; typedef struct AutoCmd { diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 231ab2acf1..433410248b 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2543,6 +2543,16 @@ func Test_close_autocmd_tab() %bwipe! endfunc +func Test_Visual_doautoall_redraw() + call setline(1, ['a', 'b']) + new + wincmd p + call feedkeys("G\", 'txn') + autocmd User Explode ++once redraw + doautoall User Explode + %bwipe! +endfunc + func Test_autocmd_closes_window() au BufNew,BufWinLeave * e %e file yyy -- cgit From 452b46fcf79de52317e2c41adb083d461a93ace5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 8 Jan 2022 12:27:35 +0000 Subject: fix(api/nvim_win_call): share common win_execute logic We have to be sure that the bugs fixed in the previous patches also apply to nvim_win_call. Checking v8.1.2124 and v8.2.4026 is especially important as these patches were only applied to win_execute, but nvim_win_call is also affected by the same bugs. A lot of win_execute's logic can be shared with nvim_win_call, so factor it out into a common macro to reduce the possibility of this happening again. --- src/nvim/api/window.c | 6 ++---- src/nvim/eval/funcs.c | 20 +------------------- src/nvim/window.h | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index a762673fbf..9fd4de4bb2 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -457,12 +457,10 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err) try_start(); Object res = OBJECT_INIT; - switchwin_T switchwin; - if (switch_win_noblock(&switchwin, win, tabpage, true) == OK) { + WIN_EXECUTE(win, tabpage, { Array args = ARRAY_DICT_INIT; res = nlua_call_ref(fun, NULL, args, true, err); - } - restore_win_noblock(&switchwin, true); + }); try_end(err); return res; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b16041b832..b1b308a393 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2181,25 +2181,7 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; if (wp != NULL && tp != NULL) { - pos_T curpos = wp->w_cursor; - switchwin_T switchwin; - if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { - check_cursor(); - execute_common(argvars, rettv, fptr, 1); - } - restore_win_noblock(&switchwin, true); - - // Update the status line if the cursor moved. - if (win_valid(wp) && !equalpos(curpos, wp->w_cursor)) { - wp->w_redr_status = true; - } - - // In case the command moved the cursor or changed the Visual area, - // check it is valid. - check_cursor(); - if (VIsual_active) { - check_pos(curbuf, &VIsual); - } + WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1)); } } diff --git a/src/nvim/window.h b/src/nvim/window.h index 925cc68b60..e2fd2c515d 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -4,6 +4,7 @@ #include #include "nvim/buffer_defs.h" +#include "nvim/mark.h" // Values for file_name_in_line() #define FNAME_MESS 1 // give error message @@ -40,6 +41,30 @@ typedef struct { bool sw_visual_active; } switchwin_T; +/// Execute a block of code in the context of window `wp` in tabpage `tp`. +/// Ensures the status line is redrawn and cursor position is valid if it is moved. +#define WIN_EXECUTE(wp, tp, block) \ + do { \ + win_T *const wp_ = (wp); \ + const pos_T curpos_ = wp_->w_cursor; \ + switchwin_T switchwin_; \ + if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ + check_cursor(); \ + block; \ + } \ + restore_win_noblock(&switchwin_, true); \ + /* Update the status line if the cursor moved. */ \ + if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ + wp_->w_redr_status = true; \ + } \ + /* In case the command moved the cursor or changed the Visual area, */ \ + /* check it is valid. */ \ + check_cursor(); \ + if (VIsual_active) { \ + check_pos(curbuf, &VIsual); \ + } \ + } while (false) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "window.h.generated.h" #endif -- cgit From f25ab39faaf9e5e161d3c285a4af645383c5498b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.1.0846: not easy to recognize the system Vim runs on Problem: Not easy to recognize the system Vim runs on. Solution: Add more items to the features list. (Ozaki Kiichi, closes vim/vim#3855) https://github.com/vim/vim/commit/39536dd557e847e80572044c2be319db5886abe3 Some doc changes have already been applied. Some others are N/A. "moon" was removed in patch 8.2.0427 so I did not add it. --- src/nvim/eval/funcs.c | 6 ++++ src/nvim/testdir/test_functions.vim | 68 ++++++++++++++++++++++++++++--------- src/nvim/testdir/test_writefile.vim | 2 +- 3 files changed, 59 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 29619f62e9..04a1f08f04 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4433,6 +4433,12 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) #if defined(BSD) && !defined(__APPLE__) "bsd", #endif +#ifdef __linux__ + "linux", +#endif +#ifdef SUN_SYSTEM + "sun", +#endif #ifdef UNIX "unix", #endif diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 0edbeb420a..e4c6c6a3b2 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1377,6 +1377,35 @@ func Test_func_exists_on_reload() delfunc ExistingFunction endfunc +func Test_platform_name() + " The system matches at most only one name. + let names = ['amiga', 'beos', 'bsd', 'hpux', 'linux', 'mac', 'qnx', 'sun', 'vms', 'win32', 'win32unix'] + call assert_inrange(0, 1, len(filter(copy(names), 'has(v:val)'))) + + " Is Unix? + call assert_equal(has('beos'), has('beos') && has('unix')) + call assert_equal(has('bsd'), has('bsd') && has('unix')) + call assert_equal(has('hpux'), has('hpux') && has('unix')) + call assert_equal(has('linux'), has('linux') && has('unix')) + call assert_equal(has('mac'), has('mac') && has('unix')) + call assert_equal(has('qnx'), has('qnx') && has('unix')) + call assert_equal(has('sun'), has('sun') && has('unix')) + call assert_equal(has('win32'), has('win32') && !has('unix')) + call assert_equal(has('win32unix'), has('win32unix') && has('unix')) + + if has('unix') && executable('uname') + let uname = system('uname') + call assert_equal(uname =~? 'BeOS', has('beos')) + call assert_equal(uname =~? 'BSD\|DragonFly', has('bsd')) + call assert_equal(uname =~? 'HP-UX', has('hpux')) + call assert_equal(uname =~? 'Linux', has('linux')) + call assert_equal(uname =~? 'Darwin', has('mac')) + call assert_equal(uname =~? 'QNX', has('qnx')) + call assert_equal(uname =~? 'SunOS', has('sun')) + call assert_equal(uname =~? 'CYGWIN\|MSYS', has('win32unix')) + endif +endfunc + sandbox function Fsandbox() normal ix endfunc @@ -1519,24 +1548,31 @@ func Test_libcall_libcallnr() let libc = 'msvcrt.dll' elseif has('mac') let libc = 'libSystem.B.dylib' - elseif system('uname -s') =~ 'SunOS' - " Set the path to libc.so according to the architecture. - let test_bits = system('file ' . GetVimProg()) - let test_arch = system('uname -p') - if test_bits =~ '64-bit' && test_arch =~ 'sparc' - let libc = '/usr/lib/sparcv9/libc.so' - elseif test_bits =~ '64-bit' && test_arch =~ 'i386' - let libc = '/usr/lib/amd64/libc.so' + elseif executable('ldd') + let libc = matchstr(split(system('ldd ' . GetVimProg())), '/libc\.so\>') + endif + if get(l:, 'libc', '') ==# '' + " On Unix, libc.so can be in various places. + if has('linux') + " There is not documented but regarding the 1st argument of glibc's + " dlopen an empty string and nullptr are equivalent, so using an empty + " string for the 1st argument of libcall allows to call functions. + let libc = '' + elseif has('sun') + " Set the path to libc.so according to the architecture. + let test_bits = system('file ' . GetVimProg()) + let test_arch = system('uname -p') + if test_bits =~ '64-bit' && test_arch =~ 'sparc' + let libc = '/usr/lib/sparcv9/libc.so' + elseif test_bits =~ '64-bit' && test_arch =~ 'i386' + let libc = '/usr/lib/amd64/libc.so' + else + let libc = '/usr/lib/libc.so' + endif else - let libc = '/usr/lib/libc.so' + " Unfortunately skip this test until a good way is found. + return endif - elseif system('uname -s') =~ 'OpenBSD' - let libc = 'libc.so' - else - " On Unix, libc.so can be in various places. - " Interestingly, using an empty string for the 1st argument of libcall - " allows to call functions from libc which is not documented. - let libc = '' endif if has('win32') diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index aa7882d129..5ffbe82082 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -43,7 +43,7 @@ func Test_writefile_fails_gently() endfunc func Test_writefile_fails_conversion() - if !has('iconv') || system('uname -s') =~ 'SunOS' + if !has('iconv') || has('sun') return endif " Without a backup file the write won't happen if there is a conversion -- cgit From 22a7693915c15cb4822f85323961687c5e86675b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.1.0878: test for has('bsd') fails on some BSD systems Problem: Test for has('bsd') fails on some BSD systems. Solution: Adjust the uname match. (James McCoy, closes vim/vim#3909) https://github.com/vim/vim/commit/a02e3f65c52a2c8c987e7dcac5df1f8db9a7b0de --- src/nvim/testdir/test_functions.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index e4c6c6a3b2..67b94d5ef8 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1397,6 +1397,8 @@ func Test_platform_name() let uname = system('uname') call assert_equal(uname =~? 'BeOS', has('beos')) call assert_equal(uname =~? 'BSD\|DragonFly', has('bsd')) + " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined + call assert_equal(uname =~? '\%(GNU/k\w\+\)\@ Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.1.0884: double check for bsd systems Problem: Double check for bsd systems. Solution: Delete the old line. https://github.com/vim/vim/commit/af630d4f7f8daa7edbda0b607d32d39a5feae9d9 --- src/nvim/testdir/test_functions.vim | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 67b94d5ef8..42facdd491 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1396,7 +1396,6 @@ func Test_platform_name() if has('unix') && executable('uname') let uname = system('uname') call assert_equal(uname =~? 'BeOS', has('beos')) - call assert_equal(uname =~? 'BSD\|DragonFly', has('bsd')) " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined call assert_equal(uname =~? '\%(GNU/k\w\+\)\@ Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.1.2358: tests fail on Cirrus CI for FreeBSD Problem: Tests fail on Cirrus CI for FreeBSD. Solution: Fix a test and skip some. (Christian Brabandt, closes vim/vim#5281) https://github.com/vim/vim/commit/9134f1ecd41207045db3cb47f0269497980395ad Skip test_normal.vim: already applied in #11483. --- src/nvim/testdir/check.vim | 9 +++++++++ src/nvim/testdir/test_quickfix.vim | 1 + src/nvim/testdir/test_source_utf8.vim | 4 ++++ src/nvim/testdir/test_utf8_comparisons.vim | 5 ++++- 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index ab26dddbe0..76c0f7d422 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -63,6 +63,15 @@ func CheckUnix() endif endfunc +" Command to check for not running on a BSD system. +" TODO: using this checks should not be needed +command CheckNotBSD call CheckNotBSD() +func CheckNotBSD() + if has('bsd') + throw 'Skipped: does not work on BSD' + endif +endfunc + " Command to check that making screendumps is supported. " Caller must source screendump.vim command CheckScreendump call CheckScreendump() diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 6db679c5f9..5cb0354dce 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1789,6 +1789,7 @@ func s:create_test_file(filename) endfunc func Test_switchbuf() + CheckNotBSD call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') call s:create_test_file('Xqftestfile3') diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim index e93ea29dff..99cb09c50e 100644 --- a/src/nvim/testdir/test_source_utf8.vim +++ b/src/nvim/testdir/test_source_utf8.vim @@ -1,7 +1,10 @@ " Test the :source! command +source check.vim func Test_source_utf8() " check that sourcing a script with 0x80 as second byte works + " does not work correctly on BSD + CheckNotBSD new call setline(1, [':%s/àx/--à1234--/g', ':%s/Àx/--À1234--/g']) write! Xscript @@ -31,6 +34,7 @@ endfunc " Test for sourcing a file with CTRL-V's at the end of the line func Test_source_ctrl_v() + CheckNotBSD call writefile(['map __1 afirst', \ 'map __2 asecond', \ 'map __3 athird', diff --git a/src/nvim/testdir/test_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim index fdf9d80802..62947c6e6e 100644 --- a/src/nvim/testdir/test_utf8_comparisons.vim +++ b/src/nvim/testdir/test_utf8_comparisons.vim @@ -86,6 +86,9 @@ endfunc " test that g~ap changes one paragraph only. func Test_gap() new - call feedkeys("iabcd\n\ndefggg0g~ap", "tx") + " setup text + call feedkeys("iabcd\\defg", "tx") + " modify only first line + call feedkeys("gg0g~ap", "tx") call assert_equal(["ABCD", "", "defg"], getline(1,3)) endfunc -- cgit From 94eaf3cb2fa548c0fa3132824baf6284f7e6bfee Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.2.0305: relativenumber test fails on some systems Problem: Relativenumber test fails on some systems. (James McCoy) Solution: Clear the command line. https://github.com/vim/vim/commit/8040a7147f5b896a702d1684e7831df107490f45 --- src/nvim/testdir/test_number.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim index d737ebe9f0..dfbdc0bffd 100644 --- a/src/nvim/testdir/test_number.vim +++ b/src/nvim/testdir/test_number.vim @@ -284,10 +284,10 @@ func Test_relativenumber_colors() " Default colors call VerifyScreenDump(buf, 'Test_relnr_colors_1', {}) - call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\") + call term_sendkeys(buf, ":hi LineNrAbove ctermfg=blue\:\") call VerifyScreenDump(buf, 'Test_relnr_colors_2', {}) - call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\") + call term_sendkeys(buf, ":hi LineNrBelow ctermfg=green\:\") call VerifyScreenDump(buf, 'Test_relnr_colors_3', {}) call term_sendkeys(buf, ":hi clear LineNrAbove\") -- cgit From a998b24c568048f0e61052380605e15bc1fbec75 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.2.0352: FreeBSD: test for sourcing utf-8 is skipped Problem: FreeBSD: test for sourcing utf-8 is skipped. Solution: Run the matchadd_conceal test separately to avoid that setting 'term' to "ansi" causes problems for other tests. (Ozaki Kiichi, closes vim/vim#5721) https://github.com/vim/vim/commit/36ddf9383181f93b080eb26121bdff37e394d2db --- src/nvim/testdir/test_alot_utf8.vim | 1 - src/nvim/testdir/test_source_utf8.vim | 29 +++++++++++++---------------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_alot_utf8.vim b/src/nvim/testdir/test_alot_utf8.vim index 70f14320a6..77f5ede4c8 100644 --- a/src/nvim/testdir/test_alot_utf8.vim +++ b/src/nvim/testdir/test_alot_utf8.vim @@ -6,7 +6,6 @@ source test_charsearch_utf8.vim source test_expr_utf8.vim -source test_matchadd_conceal_utf8.vim source test_mksession_utf8.vim source test_regexp_utf8.vim source test_source_utf8.vim diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim index 99cb09c50e..66fabe0442 100644 --- a/src/nvim/testdir/test_source_utf8.vim +++ b/src/nvim/testdir/test_source_utf8.vim @@ -3,8 +3,6 @@ source check.vim func Test_source_utf8() " check that sourcing a script with 0x80 as second byte works - " does not work correctly on BSD - CheckNotBSD new call setline(1, [':%s/àx/--à1234--/g', ':%s/Àx/--À1234--/g']) write! Xscript @@ -34,25 +32,24 @@ endfunc " Test for sourcing a file with CTRL-V's at the end of the line func Test_source_ctrl_v() - CheckNotBSD - call writefile(['map __1 afirst', - \ 'map __2 asecond', - \ 'map __3 athird', - \ 'map __4 afourth', - \ 'map __5 afifth', - \ "map __1 asd\", - \ "map __2 asd\\", - \ "map __3 asd\\", - \ "map __4 asd\\\", - \ "map __5 asd\\\", - \ ], 'Xtestfile') + call writefile(['map __1 afirst', + \ 'map __2 asecond', + \ 'map __3 athird', + \ 'map __4 afourth', + \ 'map __5 afifth', + \ "map __1 asd\", + \ "map __2 asd\\", + \ "map __3 asd\\", + \ "map __4 asd\\\", + \ "map __5 asd\\\", + \ ], 'Xtestfile') source Xtestfile enew! exe "normal __1\\__2\__3\\__4\__5\" exe "%s/\/0/g" call assert_equal(['sd', - \ "map __2 asd\secondsd\sd0map __5 asd0fifth"], - \ getline(1, 2)) + \ "map __2 asd\secondsd\sd0map __5 asd0fifth"], + \ getline(1, 2)) enew! call delete('Xtestfile') -- cgit From 7c3064d46e9e525a3632e7622e192cb5402b05df Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 09:23:54 +0800 Subject: vim-patch:8.2.0687: some tests do not work on FreeBSD Problem: Some tests do not work on FreeBSD. Solution: Enable modeline. Use WaitFor() in more cases. (Ozaki Kiichi, closes vim/vim#6036) https://github.com/vim/vim/commit/41d4299f26cc98e253f9c63f8adc9dbb9d49ed5c --- src/nvim/testdir/test_quickfix.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 5cb0354dce..f137ed5346 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1789,7 +1789,6 @@ func s:create_test_file(filename) endfunc func Test_switchbuf() - CheckNotBSD call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') call s:create_test_file('Xqftestfile3') @@ -1915,6 +1914,7 @@ func Test_switchbuf() " If opening a file changes 'switchbuf', then the new value should be " retained. + set modeline&vim call writefile(["vim: switchbuf=split"], 'Xqftestfile1') enew | only set switchbuf&vim -- cgit From b9def4ae10dc77d16786d99e2814033c02178e63 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 18:47:20 +0800 Subject: test(old): fix test order in test_search_stat.vim Also avoid unnecessary divergences from upstream and add a modeline. --- src/nvim/testdir/test_search_stat.vim | 62 ++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim index f7f7467e91..015771a3bb 100644 --- a/src/nvim/testdir/test_search_stat.vim +++ b/src/nvim/testdir/test_search_stat.vim @@ -1,14 +1,15 @@ " Tests for search_stats, when "S" is not in 'shortmess' -source screendump.vim source check.vim +source screendump.vim func Test_search_stat() new set shortmess-=S " Append 50 lines with text to search for, "foobar" appears 20 times call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 10)) - call nvim_win_set_cursor(0, [1, 0]) + + call cursor(1, 1) " searchcount() returns an empty dictionary when previous pattern was not set call assert_equal({}, searchcount(#{pattern: ''})) @@ -45,7 +46,6 @@ func Test_search_stat() \ searchcount(#{pattern: 'fooooobar', maxcount: 1})) " match at second line - call cursor(1, 1) let messages_before = execute('messages') let @/ = 'fo*\(bar\?\)\?' let g:a = execute(':unsilent :norm! n') @@ -262,6 +262,34 @@ func Test_searchcount_fails() call assert_fails('echo searchcount("boo!")', 'E715:') endfunc +func Test_searchcount_in_statusline() + CheckScreendump + + let lines =<< trim END + set shortmess-=S + call append(0, 'this is something') + function TestSearchCount() abort + let search_count = searchcount() + if !empty(search_count) + return '[' . search_count.current . '/' . search_count.total . ']' + else + return '' + endif + endfunction + set hlsearch + set laststatus=2 statusline+=%{TestSearchCount()} + END + call writefile(lines, 'Xsearchstatusline') + let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10}) + call TermWait(buf) + call term_sendkeys(buf, "/something") + call VerifyScreenDump(buf, 'Test_searchstat_4', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) + call delete('Xsearchstatusline') +endfunc + func Test_search_stat_foldopen() CheckScreendump @@ -319,30 +347,4 @@ func! Test_search_stat_screendump() call delete('Xsearchstat') endfunc -func Test_searchcount_in_statusline() - CheckScreendump - - let lines =<< trim END - set shortmess-=S - call append(0, 'this is something') - function TestSearchCount() abort - let search_count = searchcount() - if !empty(search_count) - return '[' . search_count.current . '/' . search_count.total . ']' - else - return '' - endif - endfunction - set hlsearch - set laststatus=2 statusline+=%{TestSearchCount()} - END - call writefile(lines, 'Xsearchstatusline') - let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10}) - call TermWait(buf) - call term_sendkeys(buf, "/something") - call VerifyScreenDump(buf, 'Test_searchstat_4', {}) - - call term_sendkeys(buf, "\") - call StopVimInTerminal(buf) - call delete('Xsearchstatusline') -endfunc +" vim: shiftwidth=2 sts=2 expandtab -- cgit From ab7ae8806e907c7238e5379f6d0eeb4d62277759 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 18:47:20 +0800 Subject: vim-patch:8.2.3583: the "gd" and "gD" commands do not update search stats Problem: The "gd" and "gD" commands do not update search stats. (Gary Johnson) Solution: Clear search stats. https://github.com/vim/vim/commit/0c71114aede81a209b7efc126b4bf19f11d58955 --- src/nvim/normal.c | 10 ++++++++-- src/nvim/testdir/test_search_stat.vim | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 96f321cf9b..225c66aae1 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3098,8 +3098,14 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock) if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0 || !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) { clearopbeep(oap); - } else if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) { - foldOpenCursor(); + } else { + if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) { + foldOpenCursor(); + } + // clear any search statistics + if (messaging() && !msg_silent && !shortmess(SHM_SEARCHCOUNT)) { + clear_cmdline = true; + } } } diff --git a/src/nvim/testdir/test_search_stat.vim b/src/nvim/testdir/test_search_stat.vim index 015771a3bb..d950626615 100644 --- a/src/nvim/testdir/test_search_stat.vim +++ b/src/nvim/testdir/test_search_stat.vim @@ -347,4 +347,29 @@ func! Test_search_stat_screendump() call delete('Xsearchstat') endfunc +func Test_search_stat_then_gd() + CheckScreendump + + let lines =<< trim END + call setline(1, ['int cat;', 'int dog;', 'cat = dog;']) + set shortmess-=S + set hlsearch + END + call writefile(lines, 'Xsearchstatgd') + + let buf = RunVimInTerminal('-S Xsearchstatgd', #{rows: 10}) + call term_sendkeys(buf, "/dog\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_searchstatgd_1', {}) + + call term_sendkeys(buf, "G0gD") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_searchstatgd_2', {}) + + call StopVimInTerminal(buf) + call delete('Xsearchstatgd') +endfunc + + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 041b2d6f1ea4b683b6ac258abb24ed6bbe72208d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 21:52:44 +0800 Subject: vim-patch:8.1.2133: some tests fail when run as root Problem: Some tests fail when run as root. Solution: Add CheckNotRoot and use it. (James McCoy, closes vim/vim#5020) https://github.com/vim/vim/commit/07282f01da06c158bab4787adc89ec15d7eeb202 Skip test_terminal.vim and test_viminfo.vim: N/A --- src/nvim/testdir/check.vim | 8 ++++++++ src/nvim/testdir/shared.vim | 9 +++++++++ src/nvim/testdir/test_rename.vim | 4 +++- src/nvim/testdir/test_swap.vim | 20 ++++++++++++-------- 4 files changed, 32 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 76c0f7d422..883f036fe1 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -113,6 +113,14 @@ func CheckNotGui() endif endfunc +" Command to check that test is not running as root +command CheckNotRoot call CheckNotRoot() +func CheckNotRoot() + if IsRoot() + throw 'Skipped: cannot run test as root' + endif +endfunc + " Command to check that the current language is English command CheckEnglish call CheckEnglish() func CheckEnglish() diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index f456ff4250..c2809844ac 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -343,6 +343,15 @@ func RunVimPiped(before, after, arguments, pipecmd) return 1 endfunc +func IsRoot() + if !has('unix') + return v:false + elseif $USER == 'root' || system('id -un') =~ '\' + return v:true + endif + return v:false +endfunc + " Get all messages but drop the maintainer entry. func GetMessages() redir => result diff --git a/src/nvim/testdir/test_rename.vim b/src/nvim/testdir/test_rename.vim index 3887fcfabf..5359b84923 100644 --- a/src/nvim/testdir/test_rename.vim +++ b/src/nvim/testdir/test_rename.vim @@ -1,5 +1,7 @@ " Test rename() +source shared.vim + func Test_rename_file_to_file() call writefile(['foo'], 'Xrename1') @@ -81,7 +83,7 @@ func Test_rename_copy() call assert_equal(0, rename('Xrenamedir/Xrenamefile', 'Xrenamefile')) - if !has('win32') + if !has('win32') && !IsRoot() " On Windows, the source file is removed despite " its directory being made not writable. call assert_equal(['foo'], readfile('Xrenamedir/Xrenamefile')) diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index b3018b2b0d..b180f27685 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -1,6 +1,7 @@ " Tests for the swap feature source check.vim +source shared.vim func s:swapname() return trim(execute('swapname')) @@ -198,14 +199,17 @@ func Test_swapfile_delete() quit call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) - " Write the swapfile with a modified PID, now it will be automatically - " deleted. Process one should never be Vim. - let swapfile_bytes[24:27] = 0z01000000 - call writefile(swapfile_bytes, swapfile_name) - let s:swapname = '' - split XswapfileText - quit - call assert_equal('', s:swapname) + " This test won't work as root because root can successfully run kill(1, 0) + if !IsRoot() + " Write the swapfile with a modified PID, now it will be automatically + " deleted. Process one should never be Vim. + let swapfile_bytes[24:27] = 0z01000000 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal('', s:swapname) + endif " Now set the modified flag, the swap file will not be deleted let swapfile_bytes[28 + 80 + 899] = 0x55 -- cgit From 9b0363d365f92b1a31bcf0fc32969a4667c11e95 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Feb 2022 22:17:25 +0800 Subject: vim-patch:8.2.1128: the write message mentions characters, but it's bytes Problem: The write message mentions characters, but it's actually bytes. Solution: Change "C" to "B" and "characters" to "bytes". https://github.com/vim/vim/commit/3f40ce78f5c178d15871bd784ed878c78f0b8a44 --- src/nvim/fileio.c | 4 ++-- src/nvim/testdir/test_cscope.vim | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f6d37adf89..f28ee1bfcb 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3863,7 +3863,7 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars) *p++ = ' '; } if (shortmess(SHM_LINES)) { - vim_snprintf((char *)p, IOSIZE - (p - IObuff), "%" PRId64 "L, %" PRId64 "C", + vim_snprintf((char *)p, IOSIZE - (p - IObuff), "%" PRId64 "L, %" PRId64 "B", (int64_t)lnum, (int64_t)nchars); } else { vim_snprintf((char *)p, IOSIZE - (p - IObuff), @@ -3871,7 +3871,7 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars) (int64_t)lnum); p += STRLEN(p); vim_snprintf((char *)p, IOSIZE - (p - IObuff), - NGETTEXT("%" PRId64 " character", "%" PRId64 " characters", nchars), + NGETTEXT("%" PRId64 " byte", "%" PRId64 " bytes", nchars), (int64_t)nchars); } } diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim index cc6154af69..faf37485cd 100644 --- a/src/nvim/testdir/test_cscope.vim +++ b/src/nvim/testdir/test_cscope.vim @@ -102,7 +102,7 @@ func Test_cscopeWithCscopeConnections() for cmd in ['cs find f Xmemfile_test.c', 'cs find 7 Xmemfile_test.c'] enew let a = execute(cmd) - call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+C') + call assert_true(a =~ '"Xmemfile_test.c" \d\+L, \d\+B') call assert_equal('Xmemfile_test.c', @%) endfor @@ -112,7 +112,7 @@ func Test_cscopeWithCscopeConnections() let a = execute(cmd) let alines = split(a, '\n', 1) call assert_equal('', alines[0]) - call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+C') + call assert_true(alines[1] =~ '"Xmemfile_test.c" \d\+L, \d\+B') call assert_equal('(1 of 1): <> #include ', alines[2]) call assert_equal('#include ', getline('.')) endfor -- cgit From a4078fa57e44fb66a3c498d6d81c1eb1a30fdc13 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 5 Feb 2022 16:39:53 +0800 Subject: test(old): add test_expand.vim This removes expand_spec.lua and copies test_expand.vim from Vim at version v8.1.2278. The rest of patch 8.1.2278 were already applied in #15952, so this marks that patch as fully ported. vim-patch:8.1.2278: using "cd" with "exe" may fail Problem: Using "cd" with "exe" may fail. Solution: Use chdir() instead. https://github.com/vim/vim/commit/3503d7c94a6c8c2a5ca1665d648d0cb81afcc863 --- src/nvim/testdir/test_expand.vim | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/nvim/testdir/test_expand.vim (limited to 'src') diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim new file mode 100644 index 0000000000..48dce25bb3 --- /dev/null +++ b/src/nvim/testdir/test_expand.vim @@ -0,0 +1,83 @@ +" Test for expanding file names + +func Test_with_directories() + call mkdir('Xdir1') + call mkdir('Xdir2') + call mkdir('Xdir3') + cd Xdir3 + call mkdir('Xdir4') + cd .. + + split Xdir1/file + call setline(1, ['a', 'b']) + w + w Xdir3/Xdir4/file + close + + next Xdir?/*/file + call assert_equal('Xdir3/Xdir4/file', expand('%')) + if has('unix') + next! Xdir?/*/nofile + call assert_equal('Xdir?/*/nofile', expand('%')) + endif + " Edit another file, on MS-Windows the swap file would be in use and can't + " be deleted. + edit foo + + call assert_equal(0, delete('Xdir1', 'rf')) + call assert_equal(0, delete('Xdir2', 'rf')) + call assert_equal(0, delete('Xdir3', 'rf')) +endfunc + +func Test_with_tilde() + let dir = getcwd() + call mkdir('Xdir ~ dir') + call assert_true(isdirectory('Xdir ~ dir')) + cd Xdir\ ~\ dir + call assert_true(getcwd() =~ 'Xdir \~ dir') + call chdir(dir) + call delete('Xdir ~ dir', 'd') + call assert_false(isdirectory('Xdir ~ dir')) +endfunc + +func Test_expand_tilde_filename() + split ~ + call assert_equal('~', expand('%')) + call assert_notequal(expand('%:p'), expand('~/')) + call assert_match('\~', expand('%:p')) + bwipe! +endfunc + +func Test_expandcmd() + let $FOO = 'Test' + call assert_equal('e x/Test/y', expandcmd('e x/$FOO/y')) + unlet $FOO + + new + edit Xfile1 + call assert_equal('e Xfile1', expandcmd('e %')) + edit Xfile2 + edit Xfile1 + call assert_equal('e Xfile2', 'e #'->expandcmd()) + edit Xfile2 + edit Xfile3 + edit Xfile4 + let bnum = bufnr('Xfile2') + call assert_equal('e Xfile2', expandcmd('e #' . bnum)) + call setline('.', 'Vim!@#') + call assert_equal('e Vim', expandcmd('e ')) + call assert_equal('e Vim!@#', expandcmd('e ')) + enew! + edit Xfile.java + call assert_equal('e Xfile.py', expandcmd('e %:r.py')) + call assert_equal('make abc.java', expandcmd('make abc.%:e')) + call assert_equal('make Xabc.java', expandcmd('make %:s?file?abc?')) + edit a1a2a3.rb + call assert_equal('make b1b2b3.rb a1a2a3 Xfile.o', expandcmd('make %:gs?a?b? %< #<.o')) + + call assert_fails('call expandcmd("make ")', 'E495:') + call assert_fails('call expandcmd("make ")', 'E495:') + enew + call assert_fails('call expandcmd("make %")', 'E499:') + close +endfunc -- cgit From 5b34c2ab73d62431f391d454e68a84332148d609 Mon Sep 17 00:00:00 2001 From: glacambre Date: Tue, 25 Jan 2022 07:59:17 +0100 Subject: fix(--headless): do not block on press-enter prompts when no UI This commit fixes #9358, where emitting multiple messages with 'echo' or a single one with 'echom' or 'echoerr' would result in a press-enter prompt that couldn't be dismissed by pressing enter. This requires adapting a few tests to spawn a UI before testing whether press-enter prompts are blocking. It also fixes #11718, as when combined with #15910 it enables making sure that neovim never blocks and emits messages on startup. --- src/nvim/message.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index d963cba6cb..93742ccbdb 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1094,6 +1094,10 @@ void wait_return(int redraw) return; } + if (headless_mode && !ui_active()) { + return; + } + /* * When inside vgetc(), we can't wait for a typed character at all. * With the global command (and some others) we only need one return at -- cgit From e6cfd20b7fdeea2478c444e6f9b60c1e18ccdf00 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 5 Feb 2022 20:03:02 +0800 Subject: vim-patch:8.2.4298: divide by zero with huge tabstop value Problem: Divide by zero with huge tabstop value. Solution: Reject tabstop value that overflows to zero. https://github.com/vim/vim/commit/fc88df42f1ae64bcc4d6cbc0fbd3445f8c59afdf --- src/nvim/option.c | 2 +- src/nvim/testdir/test_vartabs.vim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 2c4c4819ea..b9fed8b378 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7551,7 +7551,7 @@ bool tabstop_set(char_u *var, long **array) int n = atoi((char *)cp); // Catch negative values, overflow and ridiculous big values. - if (n < 0 || n > TABSTOP_MAX) { + if (n <= 0 || n > TABSTOP_MAX) { semsg(_(e_invarg2), cp); XFREE_CLEAR(*array); return false; diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 46e0d62313..d05008d2dd 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -135,6 +135,16 @@ func Test_vartabs() bwipeout! endfunc +func Test_retab_invalid_arg() + new + call setline(1, "\ttext") + retab 0 + call assert_fails("retab -8", 'E487: Argument must be positive') + call assert_fails("retab 10000", 'E475:') + call assert_fails("retab 720575940379279360", 'E475:') + bwipe! +endfunc + func! Test_vartabs_breakindent() if !exists("+breakindent") return -- cgit From 22f0725aac300ed9b249f995df7889f6c203d1e0 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 9 Jan 2022 22:48:29 +0000 Subject: vim-patch:8.1.2342: random number generator in Vim script is slow Problem: Random number generator in Vim script is slow. Solution: Add rand() and srand(). (Yasuhiro Matsumoto, closes vim/vim#1277) https://github.com/vim/vim/commit/06b0b4bc27077013e9b4b48fd1d9b33e543ccf99 Add missing method call usage to builtin.txt. vim_time and test_settime is N/A. Add a modeline to test_random.vim. Use typval_T* over listitem_T* vars so we don't need to use TV_LIST_ITEM_TV all over the place... Remove NULL list checks (tv_list_len covers this). --- src/nvim/eval.lua | 2 + src/nvim/eval/funcs.c | 95 ++++++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_random.vim | 31 +++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/nvim/testdir/test_random.vim (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 18967b80f2..ef2b8a0cc0 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -273,6 +273,7 @@ return { pyeval={args=1, base=1, func="f_py3eval"}, pyxeval={args=1, base=1, func="f_py3eval"}, perleval={args=1, base=1}, + rand={args={0, 1}, base=1}, range={args={1, 3}, base=1}, readdir={args={1, 2}, base=1}, readfile={args={1, 3}, base=1}, @@ -348,6 +349,7 @@ return { spellsuggest={args={1, 3}, base=1}, split={args={1, 3}, base=1}, sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"}, + srand={args={0, 1}, base=1}, stdpath={args=1}, str2float={args=1, base=1}, str2list={args={1, 2}, base=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 50030f2994..96d8c93db3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -22,6 +22,7 @@ #include "nvim/eval/encode.h" #include "nvim/eval/executor.h" #include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" @@ -6978,6 +6979,81 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) script_host_eval("python3", argvars, rettv); } +/// "rand()" function +static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + uint32_t w; +#define SHUFFLE_XORSHIFT128 \ + const uint32_t t = x ^ (x << 11); \ + x = y; \ + y = z; \ + z = w; \ + w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); + + if (argvars[0].v_type == VAR_UNKNOWN) { + static bool rand_seed_initialized = false; + static uint32_t xyzw[4] = { 123456789, 362436069, 521288629, 88675123 }; + + // When argument is not given, return random number initialized + // statically. + if (!rand_seed_initialized) { + xyzw[0] = time(NULL); + rand_seed_initialized = true; + } + + uint32_t x = xyzw[0]; + uint32_t y = xyzw[1]; + uint32_t z = xyzw[2]; + w = xyzw[3]; + SHUFFLE_XORSHIFT128; + xyzw[0] = x; + xyzw[1] = y; + xyzw[2] = z; + xyzw[3] = w; + } else if (argvars[0].v_type == VAR_LIST) { + list_T *const l = argvars[0].vval.v_list; + if (tv_list_len(l) != 4) { + goto theend; + } + + typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); + typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); + typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); + typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); + if (tvx->v_type != VAR_NUMBER) { + goto theend; + } + if (tvy->v_type != VAR_NUMBER) { + goto theend; + } + if (tvz->v_type != VAR_NUMBER) { + goto theend; + } + if (tvw->v_type != VAR_NUMBER) { + goto theend; + } + uint32_t x = tvx->vval.v_number; + uint32_t y = tvy->vval.v_number; + uint32_t z = tvz->vval.v_number; + w = tvw->vval.v_number; + SHUFFLE_XORSHIFT128; + tvx->vval.v_number = (varnumber_T)x; + tvy->vval.v_number = (varnumber_T)y; + tvz->vval.v_number = (varnumber_T)z; + tvw->vval.v_number = (varnumber_T)w; + } else { + goto theend; + } + +#undef SHUFFLE_XORSHIFT128 + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = (varnumber_T)w; + return; + +theend: + semsg(_(e_invarg2), tv_get_string(&argvars[0])); +} + /// "perleval()" function static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -10449,6 +10525,25 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "srand()" function +static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, 4); + if (argvars[0].v_type == VAR_UNKNOWN) { + tv_list_append_number(rettv->vval.v_list, (varnumber_T)time(NULL)); + } else { + bool error = false; + const uint32_t x = tv_get_number_chk(&argvars[0], &error); + if (error) { + return; + } + tv_list_append_number(rettv->vval.v_list, x); + } + tv_list_append_number(rettv->vval.v_list, 362436069); + tv_list_append_number(rettv->vval.v_list, 521288629); + tv_list_append_number(rettv->vval.v_list, 88675123); +} + /* * "str2float()" function */ diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim new file mode 100644 index 0000000000..bde034a96c --- /dev/null +++ b/src/nvim/testdir/test_random.vim @@ -0,0 +1,31 @@ +" Tests for srand() and rand() + +func Test_Rand() + let r = srand(123456789) + call assert_equal([123456789, 362436069, 521288629, 88675123], r) + call assert_equal(3701687786, rand(r)) + call assert_equal(458299110, rand(r)) + call assert_equal(2500872618, rand(r)) + call assert_equal(3633119408, rand(r)) + call assert_equal(516391518, rand(r)) + + " Nvim does not support test_settime + " call test_settime(12341234) + " let s = srand() + " call assert_equal(s, srand()) + " call test_settime(12341235) + " call assert_notequal(s, srand()) + + call srand() + let v = rand() + call assert_notequal(v, rand()) + + call assert_fails('echo srand([1])', 'E745:') + call assert_fails('echo rand([1, 2, 3])', 'E475:') + call assert_fails('echo rand([[1], 2, 3, 4])', 'E475:') + call assert_fails('echo rand([1, [2], 3, 4])', 'E475:') + call assert_fails('echo rand([1, 2, [3], 4])', 'E475:') + call assert_fails('echo rand([1, 2, 3, [4]])', 'E475:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From f6a0d5498b5f0d62e10f7ba891bc6ea5e20dad69 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 9 Jan 2022 23:53:55 +0000 Subject: vim-patch:8.1.2343: using time() for srand() is not very random Problem: Using time() for srand() is not very random. Solution: use /dev/urandom if available https://github.com/vim/vim/commit/07e4a197953d12902fb97beb48830a5323a52280 Use os_open and os_close. time_settime is N/A, so some parts of the test are disabled. There's maybe a very, very, very, very small chance the /dev/urandom test fails, but it shouldn't matter. :P --- src/nvim/eval/funcs.c | 32 ++++++++++++++++++++++++++++++-- src/nvim/testdir/test_random.vim | 16 ++++++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 96d8c93db3..6a0803704d 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -10528,16 +10528,44 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "srand()" function static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + static int dev_urandom_state = -1; // FAIL or OK once tried + tv_list_alloc_ret(rettv, 4); if (argvars[0].v_type == VAR_UNKNOWN) { - tv_list_append_number(rettv->vval.v_list, (varnumber_T)time(NULL)); + if (dev_urandom_state != FAIL) { + const int fd = os_open("/dev/urandom", O_RDONLY, 0); + struct { + union { + uint32_t number; + char bytes[sizeof(uint32_t)]; + } cont; + } buf; + + // Attempt reading /dev/urandom. + if (fd == -1) { + dev_urandom_state = FAIL; + } else { + buf.cont.number = 0; + if (read(fd, buf.cont.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) { + dev_urandom_state = FAIL; + } else { + dev_urandom_state = OK; + tv_list_append_number(rettv->vval.v_list, (varnumber_T)buf.cont.number); + } + os_close(fd); + } + } + if (dev_urandom_state != OK) { + // Reading /dev/urandom doesn't work, fall back to time(). + tv_list_append_number(rettv->vval.v_list, (varnumber_T)time(NULL)); + } } else { bool error = false; const uint32_t x = tv_get_number_chk(&argvars[0], &error); if (error) { return; } - tv_list_append_number(rettv->vval.v_list, x); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)x); } tv_list_append_number(rettv->vval.v_list, 362436069); tv_list_append_number(rettv->vval.v_list, 521288629); diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index bde034a96c..46091836d4 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -11,10 +11,16 @@ func Test_Rand() " Nvim does not support test_settime " call test_settime(12341234) - " let s = srand() - " call assert_equal(s, srand()) - " call test_settime(12341235) - " call assert_notequal(s, srand()) + let s = srand() + if filereadable('/dev/urandom') + " using /dev/urandom + call assert_notequal(s, srand()) + " else + " " using time() + " call assert_equal(s, srand()) + " call test_settime(12341235) + " call assert_notequal(s, srand()) + endif call srand() let v = rand() @@ -26,6 +32,8 @@ func Test_Rand() call assert_fails('echo rand([1, [2], 3, 4])', 'E475:') call assert_fails('echo rand([1, 2, [3], 4])', 'E475:') call assert_fails('echo rand([1, 2, 3, [4]])', 'E475:') + + " call test_settime(0) endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From c97614d98fc7ab040851b7fe1bc4cb575ce8c627 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 9 Jan 2022 23:26:03 +0000 Subject: vim-patch:8.1.2356: rand() does not use the best algorithm Problem: rand() does not use the best algorithm. Solution: use xoshiro128** instead of xorshift. (Kaito Udagawa, closes vim/vim#5279) https://github.com/vim/vim/commit/f8c1f9200c4b50969a8191a4fe0b0d09edb38979 --- src/nvim/eval/funcs.c | 137 +++++++++++++++++++++------------------ src/nvim/testdir/test_random.vim | 12 ++-- 2 files changed, 81 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6a0803704d..39cbb4823a 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6982,76 +6982,79 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "rand()" function static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - uint32_t w; -#define SHUFFLE_XORSHIFT128 \ - const uint32_t t = x ^ (x << 11); \ - x = y; \ - y = z; \ - z = w; \ - w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); + list_T *l = NULL; if (argvars[0].v_type == VAR_UNKNOWN) { - static bool rand_seed_initialized = false; - static uint32_t xyzw[4] = { 123456789, 362436069, 521288629, 88675123 }; - - // When argument is not given, return random number initialized - // statically. - if (!rand_seed_initialized) { - xyzw[0] = time(NULL); - rand_seed_initialized = true; - } - - uint32_t x = xyzw[0]; - uint32_t y = xyzw[1]; - uint32_t z = xyzw[2]; - w = xyzw[3]; - SHUFFLE_XORSHIFT128; - xyzw[0] = x; - xyzw[1] = y; - xyzw[2] = z; - xyzw[3] = w; + static list_T *globl = NULL; + + // When no argument is given use the global seed list. + if (globl == NULL) { + // Initialize the global seed list. + f_srand(argvars, rettv, fptr); + l = rettv->vval.v_list; + if (tv_list_len(l) != 4) { + tv_clear(rettv); + goto theend; + } + globl = l; + } else { + l = globl; + } } else if (argvars[0].v_type == VAR_LIST) { - list_T *const l = argvars[0].vval.v_list; + l = argvars[0].vval.v_list; if (tv_list_len(l) != 4) { goto theend; } - - typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); - typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); - typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); - typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); - if (tvx->v_type != VAR_NUMBER) { - goto theend; - } - if (tvy->v_type != VAR_NUMBER) { - goto theend; - } - if (tvz->v_type != VAR_NUMBER) { - goto theend; - } - if (tvw->v_type != VAR_NUMBER) { - goto theend; - } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - w = tvw->vval.v_number; - SHUFFLE_XORSHIFT128; - tvx->vval.v_number = (varnumber_T)x; - tvy->vval.v_number = (varnumber_T)y; - tvz->vval.v_number = (varnumber_T)z; - tvw->vval.v_number = (varnumber_T)w; } else { goto theend; } -#undef SHUFFLE_XORSHIFT128 + typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); + typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); + typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); + typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); + if (tvx->v_type != VAR_NUMBER) { + goto theend; + } + if (tvy->v_type != VAR_NUMBER) { + goto theend; + } + if (tvz->v_type != VAR_NUMBER) { + goto theend; + } + if (tvw->v_type != VAR_NUMBER) { + goto theend; + } + uint32_t x = tvx->vval.v_number; + uint32_t y = tvy->vval.v_number; + uint32_t z = tvz->vval.v_number; + uint32_t w = tvw->vval.v_number; + + // SHUFFLE_XOSHIRO128STARSTAR +#define ROTL(x, k) ((x << k) | (x >> (32 - k))) + const uint32_t result = ROTL(y * 5, 7) * 9; + const uint32_t t = y << 9; + z ^= x; + w ^= y; + y ^= z; + x ^= w; + z ^= t; + w = ROTL(w, 11); +#undef ROTL + + tvx->vval.v_number = (varnumber_T)x; + tvy->vval.v_number = (varnumber_T)y; + tvz->vval.v_number = (varnumber_T)z; + tvw->vval.v_number = (varnumber_T)w; + rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = (varnumber_T)w; + rettv->vval.v_number = (varnumber_T)result; return; theend: semsg(_(e_invarg2), tv_get_string(&argvars[0])); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = -1; } /// "perleval()" function @@ -10529,6 +10532,7 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { static int dev_urandom_state = -1; // FAIL or OK once tried + uint32_t x = 0; tv_list_alloc_ret(rettv, 4); if (argvars[0].v_type == VAR_UNKNOWN) { @@ -10550,26 +10554,35 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) dev_urandom_state = FAIL; } else { dev_urandom_state = OK; - tv_list_append_number(rettv->vval.v_list, (varnumber_T)buf.cont.number); + x = buf.cont.number; } os_close(fd); } } if (dev_urandom_state != OK) { // Reading /dev/urandom doesn't work, fall back to time(). - tv_list_append_number(rettv->vval.v_list, (varnumber_T)time(NULL)); + x = time(NULL); } } else { bool error = false; - const uint32_t x = tv_get_number_chk(&argvars[0], &error); + x = tv_get_number_chk(&argvars[0], &error); if (error) { return; } - tv_list_append_number(rettv->vval.v_list, (varnumber_T)x); } - tv_list_append_number(rettv->vval.v_list, 362436069); - tv_list_append_number(rettv->vval.v_list, 521288629); - tv_list_append_number(rettv->vval.v_list, 88675123); + + uint32_t z; +#define SPLITMIX32 ( \ + z = (x += 0x9e3779b9), \ + z = (z ^ (z >> 16)) * 0x85ebca6b, \ + z = (z ^ (z >> 13)) * 0xc2b2ae35, \ + z ^ (z >> 16)) + + tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); +#undef SPLITMIX32 } /* diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index 46091836d4..ed98433b94 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -2,12 +2,12 @@ func Test_Rand() let r = srand(123456789) - call assert_equal([123456789, 362436069, 521288629, 88675123], r) - call assert_equal(3701687786, rand(r)) - call assert_equal(458299110, rand(r)) - call assert_equal(2500872618, rand(r)) - call assert_equal(3633119408, rand(r)) - call assert_equal(516391518, rand(r)) + call assert_equal([1573771921, 319883699, 2742014374, 1324369493], r) + call assert_equal(4284103975, rand(r)) + call assert_equal(1001954530, rand(r)) + call assert_equal(2701803082, rand(r)) + call assert_equal(2658065534, rand(r)) + call assert_equal(3104308804, rand(r)) " Nvim does not support test_settime " call test_settime(12341234) -- cgit From cc7ccf6d31d14457070055be07884edaf2eb165f Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 10 Jan 2022 00:49:07 +0000 Subject: vim-patch:8.1.2357: no test with wrong argument for rand() Problem: No test with wrong argument for rand(). Solution: Add a test case. https://github.com/vim/vim/commit/68e9e5f7fccb8038cf0ca5b5d95c85a923152f46 --- src/nvim/testdir/test_random.vim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index ed98433b94..f953b93d51 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -26,7 +26,11 @@ func Test_Rand() let v = rand() call assert_notequal(v, rand()) + if has('float') + call assert_fails('echo srand(1.2)', 'E805:') + endif call assert_fails('echo srand([1])', 'E745:') + call assert_fails('echo rand("burp")', 'E475:') call assert_fails('echo rand([1, 2, 3])', 'E475:') call assert_fails('echo rand([[1], 2, 3, 4])', 'E475:') call assert_fails('echo rand([1, [2], 3, 4])', 'E475:') -- cgit From 4f7a8991a93ddb1b6ab7cd8a8f21b5197c4612bb Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 10 Jan 2022 10:31:16 +0000 Subject: vim-patch:8.2.0233: crash when using garbagecollect() in between rand() Problem: Crash when using garbagecollect() in between rand(). Solution: Redesign the rand() and srand() implementation. (Yasuhiro Matsumoto, closes vim/vim#5587, closes vim/vim#5588) https://github.com/vim/vim/commit/4f645c54efe33d7a11e314676e503118761f08a7 Omit test_srand_seed. Unmacroify SHUFFLE_XOSHIRO128STARSTAR and SPLITMIX32 while we're at it (leave ROTL alone as it's fairly innocent). --- src/nvim/eval/funcs.c | 237 +++++++++++++++++++++------------------ src/nvim/testdir/test_random.vim | 16 ++- 2 files changed, 141 insertions(+), 112 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 39cbb4823a..b96ff50787 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6979,73 +6979,129 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) script_host_eval("python3", argvars, rettv); } +static void init_srand(uint32_t *const x) + FUNC_ATTR_NONNULL_ALL +{ +#ifndef MSWIN + static int dev_urandom_state = NOTDONE; // FAIL or OK once tried + + if (dev_urandom_state != FAIL) { + const int fd = os_open("/dev/urandom", O_RDONLY, 0); + struct { + union { + uint32_t number; + char bytes[sizeof(uint32_t)]; + } contents; + } buf; + + // Attempt reading /dev/urandom. + if (fd == -1) { + dev_urandom_state = FAIL; + } else { + buf.contents.number = 0; + if (read(fd, buf.contents.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) { + dev_urandom_state = FAIL; + } else { + dev_urandom_state = OK; + *x = buf.contents.number; + } + os_close(fd); + } + } + if (dev_urandom_state != OK) { + // Reading /dev/urandom doesn't work, fall back to time(). +#endif + *x = time(NULL); +#ifndef MSWIN + } +#endif +} + +static inline uint32_t splitmix32(uint32_t *const x) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + uint32_t z = (*x += 0x9e3779b9); + z = (z ^ (z >> 16)) * 0x85ebca6b; + z = (z ^ (z >> 13)) * 0xc2b2ae35; + return z ^ (z >> 16); +} + +static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *const y, + uint32_t *const z, uint32_t *const w) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ +#define ROTL(x, k) ((x << k) | (x >> (32 - k))) + const uint32_t result = ROTL(*y * 5, 7) * 9; + const uint32_t t = *y << 9; + *z ^= *x; + *w ^= *y; + *y ^= *z; + *x ^= *w; + *z ^= t; + *w = ROTL(*w, 11); +#undef ROTL + return result; +} + /// "rand()" function static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l = NULL; + uint32_t result; if (argvars[0].v_type == VAR_UNKNOWN) { - static list_T *globl = NULL; + static uint32_t gx, gy, gz, gw; + static bool initialized = false; // When no argument is given use the global seed list. - if (globl == NULL) { + if (!initialized) { // Initialize the global seed list. - f_srand(argvars, rettv, fptr); - l = rettv->vval.v_list; - if (tv_list_len(l) != 4) { - tv_clear(rettv); - goto theend; - } - globl = l; - } else { - l = globl; + uint32_t x; + init_srand(&x); + + gx = splitmix32(&x); + gy = splitmix32(&x); + gz = splitmix32(&x); + gw = splitmix32(&x); + initialized = true; } + + result = shuffle_xoshiro128starstar(&gx, &gy, &gz, &gw); } else if (argvars[0].v_type == VAR_LIST) { - l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; if (tv_list_len(l) != 4) { goto theend; } - } else { - goto theend; - } - typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); - typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); - typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); - typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); - if (tvx->v_type != VAR_NUMBER) { - goto theend; - } - if (tvy->v_type != VAR_NUMBER) { - goto theend; - } - if (tvz->v_type != VAR_NUMBER) { - goto theend; - } - if (tvw->v_type != VAR_NUMBER) { - goto theend; - } - uint32_t x = tvx->vval.v_number; - uint32_t y = tvy->vval.v_number; - uint32_t z = tvz->vval.v_number; - uint32_t w = tvw->vval.v_number; + typval_T *const tvx = TV_LIST_ITEM_TV(tv_list_find(l, 0L)); + typval_T *const tvy = TV_LIST_ITEM_TV(tv_list_find(l, 1L)); + typval_T *const tvz = TV_LIST_ITEM_TV(tv_list_find(l, 2L)); + typval_T *const tvw = TV_LIST_ITEM_TV(tv_list_find(l, 3L)); + if (tvx->v_type != VAR_NUMBER) { + goto theend; + } + if (tvy->v_type != VAR_NUMBER) { + goto theend; + } + if (tvz->v_type != VAR_NUMBER) { + goto theend; + } + if (tvw->v_type != VAR_NUMBER) { + goto theend; + } + uint32_t x = tvx->vval.v_number; + uint32_t y = tvy->vval.v_number; + uint32_t z = tvz->vval.v_number; + uint32_t w = tvw->vval.v_number; - // SHUFFLE_XOSHIRO128STARSTAR -#define ROTL(x, k) ((x << k) | (x >> (32 - k))) - const uint32_t result = ROTL(y * 5, 7) * 9; - const uint32_t t = y << 9; - z ^= x; - w ^= y; - y ^= z; - x ^= w; - z ^= t; - w = ROTL(w, 11); -#undef ROTL + result = shuffle_xoshiro128starstar(&x, &y, &z, &w); - tvx->vval.v_number = (varnumber_T)x; - tvy->vval.v_number = (varnumber_T)y; - tvz->vval.v_number = (varnumber_T)z; - tvw->vval.v_number = (varnumber_T)w; + tvx->vval.v_number = (varnumber_T)x; + tvy->vval.v_number = (varnumber_T)y; + tvz->vval.v_number = (varnumber_T)z; + tvw->vval.v_number = (varnumber_T)w; + } else { + goto theend; + } rettv->v_type = VAR_NUMBER; rettv->vval.v_number = (varnumber_T)result; @@ -7057,6 +7113,28 @@ theend: rettv->vval.v_number = -1; } +/// "srand()" function +static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + uint32_t x = 0; + + tv_list_alloc_ret(rettv, 4); + if (argvars[0].v_type == VAR_UNKNOWN) { + init_srand(&x); + } else { + bool error = false; + x = tv_get_number_chk(&argvars[0], &error); + if (error) { + return; + } + } + + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)splitmix32(&x)); +} + /// "perleval()" function static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -10528,63 +10606,6 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "srand()" function -static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - static int dev_urandom_state = -1; // FAIL or OK once tried - uint32_t x = 0; - - tv_list_alloc_ret(rettv, 4); - if (argvars[0].v_type == VAR_UNKNOWN) { - if (dev_urandom_state != FAIL) { - const int fd = os_open("/dev/urandom", O_RDONLY, 0); - struct { - union { - uint32_t number; - char bytes[sizeof(uint32_t)]; - } cont; - } buf; - - // Attempt reading /dev/urandom. - if (fd == -1) { - dev_urandom_state = FAIL; - } else { - buf.cont.number = 0; - if (read(fd, buf.cont.bytes, sizeof(uint32_t)) != sizeof(uint32_t)) { - dev_urandom_state = FAIL; - } else { - dev_urandom_state = OK; - x = buf.cont.number; - } - os_close(fd); - } - } - if (dev_urandom_state != OK) { - // Reading /dev/urandom doesn't work, fall back to time(). - x = time(NULL); - } - } else { - bool error = false; - x = tv_get_number_chk(&argvars[0], &error); - if (error) { - return; - } - } - - uint32_t z; -#define SPLITMIX32 ( \ - z = (x += 0x9e3779b9), \ - z = (z ^ (z >> 16)) * 0x85ebca6b, \ - z = (z ^ (z >> 13)) * 0xc2b2ae35, \ - z ^ (z >> 16)) - - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); - tv_list_append_number(rettv->vval.v_list, (varnumber_T)SPLITMIX32); -#undef SPLITMIX32 -} - /* * "str2float()" function */ diff --git a/src/nvim/testdir/test_random.vim b/src/nvim/testdir/test_random.vim index f953b93d51..6d3f7dcfd9 100644 --- a/src/nvim/testdir/test_random.vim +++ b/src/nvim/testdir/test_random.vim @@ -12,7 +12,7 @@ func Test_Rand() " Nvim does not support test_settime " call test_settime(12341234) let s = srand() - if filereadable('/dev/urandom') + if !has('win32') && filereadable('/dev/urandom') " using /dev/urandom call assert_notequal(s, srand()) " else @@ -22,9 +22,11 @@ func Test_Rand() " call assert_notequal(s, srand()) endif - call srand() - let v = rand() - call assert_notequal(v, rand()) + " Nvim does not support test_srand_seed + " call test_srand_seed(123456789) + " call assert_equal(4284103975, rand()) + " call assert_equal(1001954530, rand()) + " call test_srand_seed() if has('float') call assert_fails('echo srand(1.2)', 'E805:') @@ -40,4 +42,10 @@ func Test_Rand() " call test_settime(0) endfunc +func Test_issue_5587() + call rand() + call garbagecollect() + call rand() +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 92e92f02e7106fcad1597bb8f0edf1e43186a07f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 5 Feb 2022 17:49:48 +0000 Subject: fix(diff): make algorithm work for vim.diff (#17300) Fixes #17207 --- src/nvim/lua/xdiff.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index b2e971f9f3..ea7a700e1e 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -184,11 +184,11 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, if (strequal("myers", v->data.string.data)) { // default } else if (strequal("minimal", v->data.string.data)) { - cfg->flags |= XDF_NEED_MINIMAL; + params->flags |= XDF_NEED_MINIMAL; } else if (strequal("patience", v->data.string.data)) { - cfg->flags |= XDF_PATIENCE_DIFF; + params->flags |= XDF_PATIENCE_DIFF; } else if (strequal("histogram", v->data.string.data)) { - cfg->flags |= XDF_HISTOGRAM_DIFF; + params->flags |= XDF_HISTOGRAM_DIFF; } else { api_set_error(err, kErrorTypeValidation, "not a valid algorithm"); goto exit_1; -- cgit From 7002a3433bed7600ec02d64927ae0e77d077f34e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 07:27:46 +0000 Subject: vim-patch:8.2.2658: :for cannot loop over a string Problem: :for cannot loop over a string. Solution: Accept a string argument and iterate over its characters. https://github.com/vim/vim/commit/74e54fcb447e5db32f9c2df34c0554bbecdccca2 v8.2.2659 is already ported. N/A patches for version.c: vim-patch:8.2.2736: Vim9: for loop over string is a bit slow Problem: Vim9: for loop over string is a bit slow. Solution: Avoid using strlen(). https://github.com/vim/vim/commit/175a41c13f3e27e30c662f2f418c5a347dbc645d --- src/nvim/eval.c | 34 ++++++++++++++++++++++++++++++---- src/nvim/testdir/test_vimscript.vim | 20 ++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5d9326eb24..aecb23f251 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -112,9 +112,11 @@ typedef struct { int fi_semicolon; // TRUE if ending in '; var]' int fi_varcount; // nr of variables in the list listwatch_T fi_lw; // keep an eye on the item used. - list_T *fi_list; // list being used + list_T *fi_list; // list being used int fi_bi; // index of blob blob_T *fi_blob; // blob being used + char_u *fi_string; // copy of string being used + int fi_byte_idx; // byte index in fi_string } forinfo_T; // values for vv_flags: @@ -2641,6 +2643,13 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) fi->fi_blob = btv.vval.v_blob; } tv_clear(&tv); + } else if (tv.v_type == VAR_STRING) { + fi->fi_byte_idx = 0; + fi->fi_string = tv.vval.v_string; + tv.vval.v_string = NULL; + if (fi->fi_string == NULL) { + fi->fi_string = vim_strsave((char_u *)""); + } } else { emsg(_(e_listblobreq)); tv_clear(&tv); @@ -2679,6 +2688,19 @@ bool next_for_item(void *fi_void, char_u *arg) fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; } + if (fi->fi_string != NULL) { + const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx); + if (len == 0) { + return false; + } + typval_T tv; + tv.v_type = VAR_STRING; + tv.v_lock = VAR_FIXED; + tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len); + fi->fi_byte_idx += len; + return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + } + listitem_T *item = fi->fi_lw.lw_item; if (item == NULL) { return false; @@ -2698,12 +2720,16 @@ void free_for_info(void *fi_void) { forinfo_T *fi = (forinfo_T *)fi_void; - if (fi != NULL && fi->fi_list != NULL) { + if (fi == NULL) { + return; + } + if (fi->fi_list != NULL) { tv_list_watch_remove(fi->fi_list, &fi->fi_lw); tv_list_unref(fi->fi_list); - } - if (fi != NULL && fi->fi_blob != NULL) { + } else if (fi->fi_blob != NULL) { tv_blob_unref(fi->fi_blob); + } else { + xfree(fi->fi_string); } xfree(fi); } diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 34733f127f..c59cab5f36 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1692,6 +1692,26 @@ func Test_function_defined_line() call delete('Xtest.vim') endfunc +func Test_for_over_string() + let res = '' + for c in 'aécÌ€d' + let res ..= c .. '-' + endfor + call assert_equal('a-é-cÌ€-d-', res) + + let res = '' + for c in '' + let res ..= c .. '-' + endfor + call assert_equal('', res) + + let res = '' + for c in v:_null_string + let res ..= c .. '-' + endfor + call assert_equal('', res) +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From 83a48d7a44c69a8b159bdcf90029005f2f4a8de5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 07:51:13 +0000 Subject: vim-patch:8.2.2661: leaking memory when looping over a string Problem: Leaking memory when looping over a string. Solution: Free the memory. https://github.com/vim/vim/commit/bb5d87c8504588be9c9d2fecc5b6455a2b2f6201 --- src/nvim/eval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index aecb23f251..b4baeb5240 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2698,7 +2698,10 @@ bool next_for_item(void *fi_void, char_u *arg) tv.v_lock = VAR_FIXED; tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len); fi->fi_byte_idx += len; - return ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + const int result + = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + xfree(tv.vval.v_string); + return result; } listitem_T *item = fi->fi_lw.lw_item; -- cgit From 8adbba7ac38d7a0b4e1f602f6522b9403c11fc7e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 08:07:13 +0000 Subject: feat(eval): port emsg from v8.2.3284 https://github.com/vim/vim/commit/80d7395dcfe96158428da6bb3d28a6eee1244e28 --- src/nvim/eval.c | 3 ++- src/nvim/testdir/test_eval_stuff.vim | 8 +++----- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b4baeb5240..93d34cf86f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -69,6 +69,7 @@ static char *e_nowhitespace static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_write2 = N_("E80: Error while writing: %s"); +static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); // TODO(ZyX-I): move to eval/executor static char *e_letwrong = N_("E734: Wrong variable type for %s="); @@ -2651,7 +2652,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) fi->fi_string = vim_strsave((char_u *)""); } } else { - emsg(_(e_listblobreq)); + emsg(_(e_string_list_or_blob_required)); tv_clear(&tv); } } diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 883ba5de3d..95eccde35c 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -65,11 +65,9 @@ func Test_E963() endfunc func Test_for_invalid() - " Vim gives incorrect emsg here until v8.2.3284, but the exact emsg from that - " patch cannot be used until v8.2.2658 is ported (for loop over Strings) - call assert_fails("for x in 99", 'E897:') - call assert_fails("for x in function('winnr')", 'E897:') - call assert_fails("for x in {'a': 9}", 'E897:') + call assert_fails("for x in 99", 'E1098:') + call assert_fails("for x in function('winnr')", 'E1098:') + call assert_fails("for x in {'a': 9}", 'E1098:') if 0 /1/5/2/s/\n -- cgit From 8ba9f19961d8573dc851d3d5f2ec217ad2fb7b28 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 04:46:16 +0800 Subject: vim-patch:8.2.1727: a popup created with "cursorline" will ignore "firstline" Problem: A popup created with "cursorline" will ignore "firstline". Solution: When both "cursorline" and "firstline" are present put the cursor on "firstline". (closes vim/vim#7000) Add the "winid" argument to getcurpos(). https://github.com/vim/vim/commit/99ca9c4868bb1669706b9e3de9a9218bd11cc459 Skip popup window related code. Cherry-pick all of Test_getcurpos_setpos() from patch 8.2.0610. --- src/nvim/eval.lua | 2 +- src/nvim/eval/funcs.c | 30 +++++++++++++++++++----------- src/nvim/testdir/test_functions.vim | 27 +++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index ef2b8a0cc0..8590614a0a 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -151,7 +151,7 @@ return { getcmdtype={}, getcmdwintype={}, getcompletion={args={2, 3}, base=1}, - getcurpos={}, + getcurpos={args={0, 1}, base=1}, getcwd={args={0, 2}, base=1}, getenv={args=1, base=1}, getfontname={args={0, 1}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b96ff50787..eb9c08c8f0 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3845,37 +3845,45 @@ static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) { - pos_T *fp; + pos_T *fp = NULL; + win_T *wp = curwin; int fnum = -1; if (getcurpos) { - fp = &curwin->w_cursor; + if (argvars[0].v_type != VAR_UNKNOWN) { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp != NULL) { + fp = &wp->w_cursor; + } + } else { + fp = &curwin->w_cursor; + } } else { fp = var2fpos(&argvars[0], true, &fnum); } list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos)); tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0)); tv_list_append_number(l, ((fp != NULL) - ? (varnumber_T)fp->lnum + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) : (varnumber_T)0)); - tv_list_append_number(l, ((fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0)); tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { const int save_set_curswant = curwin->w_set_curswant; const colnr_T save_curswant = curwin->w_curswant; const colnr_T save_virtcol = curwin->w_virtcol; - update_curswant(); - tv_list_append_number(l, (curwin->w_curswant == MAXCOL - ? (varnumber_T)MAXCOL - : (varnumber_T)curwin->w_curswant + 1)); + if (wp == curwin) { + update_curswant(); + } + tv_list_append_number(l, (wp == NULL) ? 0 : (wp->w_curswant == MAXCOL) + ? (varnumber_T)MAXCOL + : (varnumber_T)wp->w_curswant + 1); // Do not change "curswant", as it is unexpected that a get // function has a side effect. - if (save_set_curswant) { + if (wp == curwin && save_set_curswant) { curwin->w_set_curswant = save_set_curswant; curwin->w_curswant = save_curswant; curwin->w_virtcol = save_virtcol; diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 42facdd491..6c2b0b97b6 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1712,6 +1712,33 @@ func Test_nr2char() call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\"')) endfunc +" Test for getcurpos() and setpos() +func Test_getcurpos_setpos() + new + call setline(1, ['012345678', '012345678']) + normal gg6l + let sp = getcurpos() + normal 0 + call setpos('.', sp) + normal jyl + call assert_equal('6', @") + call assert_equal(-1, setpos('.', v:_null_list)) + call assert_equal(-1, setpos('.', {})) + + let winid = win_getid() + normal G$ + let pos = getcurpos() + wincmd w + call assert_equal(pos, getcurpos(winid)) + + wincmd w + close! + + call assert_equal(getcurpos(), getcurpos(0)) + call assert_equal([0, 0, 0, 0, 0], getcurpos(-1)) + call assert_equal([0, 0, 0, 0, 0], getcurpos(1999)) +endfunc + func HasDefault(msg = 'msg') return a:msg endfunc -- cgit From 6ab71683d14a408e79f7cbda3a07ab65f76c6b35 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 04:46:16 +0800 Subject: vim-patch:8.2.2324: not easy to get mark en cursor posotion by character count Problem: Not easy to get mark en cursor posotion by character count. Solution: Add functions that use character index. (Yegappan Lakshmanan, closes vim/vim#7648) https://github.com/vim/vim/commit/6f02b00bb0958f70bc15534e115b4c6dadff0e06 --- src/nvim/eval.c | 105 +++++++++-- src/nvim/eval.lua | 5 + src/nvim/eval/funcs.c | 334 ++++++++++++++++++++-------------- src/nvim/eval/typval.c | 2 +- src/nvim/tag.c | 2 +- src/nvim/testdir/test_cursor_func.vim | 187 ++++++++++++++++++- 6 files changed, 476 insertions(+), 159 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5d9326eb24..8421d8978a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8151,6 +8151,52 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return ret; } +/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a +/// character index. Works only for loaded buffers. Returns -1 on failure. +/// The index of the first character is one. +int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char_u *str = ml_get_buf(buf, lnum, false); + + if (*str == NUL) { + return 1; + } + + return mb_charlen_len(str, byteidx + 1); +} + +/// Convert the specified character index of line 'lnum' in buffer 'buf' to a +/// byte index. Works only for loaded buffers. Returns -1 on failure. The index +/// of the first byte and the first character is one. +int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char_u *str = ml_get_buf(buf, lnum, false); + + // Convert the character offset to a byte offset + char_u *t = str; + while (*t != NUL && --charidx > 0) { + t += utfc_ptr2len(t); + } + + return t - str + 1; +} + /// Translate a VimL object into a position /// /// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid @@ -8159,9 +8205,11 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) /// @param[in] tv Object to translate. /// @param[in] dollar_lnum True when "$" is last line. /// @param[out] ret_fnum Set to fnum for marks. +/// @param[in] charcol True to return character column. /// /// @return Pointer to position or NULL in case of error (e.g. invalid type). -pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum) +pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum, + const bool charcol) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { static pos_T pos; @@ -8191,7 +8239,11 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (error) { return NULL; } - len = (long)STRLEN(ml_get(pos.lnum)); + if (charcol) { + len = mb_charlen(ml_get(pos.lnum)); + } else { + len = STRLEN(ml_get(pos.lnum)); + } // We accept "$" for the column number: last column. li = tv_list_find(l, 1L); @@ -8222,19 +8274,31 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret return NULL; } if (name[0] == '.') { // Cursor. - return &curwin->w_cursor; + pos = curwin->w_cursor; + if (charcol) { + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + } + return &pos; } if (name[0] == 'v' && name[1] == NUL) { // Visual start. if (VIsual_active) { - return &VIsual; + pos = VIsual; + } else { + pos = curwin->w_cursor; + } + if (charcol) { + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; } - return &curwin->w_cursor; + return &pos; } if (name[0] == '\'') { // Mark. pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } + if (charcol) { + pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1; + } return pp; } @@ -8260,22 +8324,24 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos.col = 0; } else { pos.lnum = curwin->w_cursor.lnum; - pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + if (charcol) { + pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); + } else { + pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } } return &pos; } return NULL; } -/* - * Convert list in "arg" into a position and optional file number. - * When "fnump" is NULL there is no file number, only 3 items. - * Note that the column is passed on as-is, the caller may want to decrement - * it to use 1 for the first column. - * Return FAIL when conversion is not possible, doesn't check the position for - * validity. - */ -int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) +/// Convert list in "arg" into a position and optional file number. +/// When "fnump" is NULL there is no file number, only 3 items. +/// Note that the column is passed on as-is, the caller may want to decrement +/// it to use 1 for the first column. +/// Return FAIL when conversion is not possible, doesn't check the position for +/// validity. +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) { list_T *l; long i = 0; @@ -8311,6 +8377,15 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) if (n < 0) { return FAIL; } + // If character position is specified, then convert to byte position + if (charcol) { + // Get the text for the specified line in a loaded buffer + buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump); + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return FAIL; + } + n = buf_charidx_to_byteidx(buf, posp->lnum, n); + } posp->col = n; n = tv_list_find_nr(l, i, NULL); // off diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 8590614a0a..5c85d764fb 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -71,6 +71,7 @@ return { chanclose={args={1, 2}}, chansend={args=2}, char2nr={args={1, 2}, base=1}, + charcol={args=1, base=1}, charidx={args={2, 3}, base=1}, chdir={args=1, base=1}, cindent={args=1, base=1}, @@ -144,6 +145,7 @@ return { getchangelist={args={0, 1}, base=1}, getchar={args={0, 1}}, getcharmod={}, + getcharpos={args=1, base=1}, getcharsearch={}, getcharstr={args={0, 1}}, getcmdline={}, @@ -152,6 +154,7 @@ return { getcmdwintype={}, getcompletion={args={2, 3}, base=1}, getcurpos={args={0, 1}, base=1}, + getcursorcharpos={args={0, 1}, base=1}, getcwd={args={0, 2}, base=1}, getenv={args=1, base=1}, getfontname={args={0, 1}}, @@ -312,8 +315,10 @@ return { serverstop={args=1}, setbufline={args=3, base=3}, setbufvar={args=3, base=3}, + setcharpos={args=2, base=2}, setcharsearch={args=1, base=1}, setcmdpos={args=1, base=1}, + setcursorcharpos={args={1, 3}, base=1}, setenv={args=2, base=2}, setfperm={args=2, base=1}, setline={args=2, base=2}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index eb9c08c8f0..b188ded368 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1020,6 +1020,49 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = utf_ptr2char((const char_u *)tv_get_string(&argvars[0])); } +/// Get the current cursor column and store it in 'rettv'. If 'charcol' is true, +/// returns the character index of the column. Otherwise, returns the byte index +/// of the column. +static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) +{ + colnr_T col = 0; + pos_T *fp; + int fnum = curbuf->b_fnum; + + fp = var2fpos(&argvars[0], false, &fnum, charcol); + if (fp != NULL && fnum == curbuf->b_fnum) { + if (fp->col == MAXCOL) { + // '> can be MAXCOL, get the length of the line then + if (fp->lnum <= curbuf->b_ml.ml_line_count) { + col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; + } else { + col = MAXCOL; + } + } else { + col = fp->col + 1; + // col(".") when the cursor is on the NUL at the end of the line + // because of "coladd" can be seen as an extra column. + if (virtual_active() && fp == &curwin->w_cursor) { + char_u *p = get_cursor_pos_ptr(); + if (curwin->w_cursor.coladd >= + (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) { + int l; + if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) { + col += l; + } + } + } + } + } + rettv->vval.v_number = col; +} + +/// "charcol()" function +static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + get_col(argvars, rettv, true); +} + // "charidx()" function static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -1148,45 +1191,10 @@ static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "col(string)" function - */ +/// "col(string)" function static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - colnr_T col = 0; - pos_T *fp; - int fnum = curbuf->b_fnum; - - fp = var2fpos(&argvars[0], FALSE, &fnum); - if (fp != NULL && fnum == curbuf->b_fnum) { - if (fp->col == MAXCOL) { - // '> can be MAXCOL, get the length of the line then - if (fp->lnum <= curbuf->b_ml.ml_line_count) { - col = (colnr_T)STRLEN(ml_get(fp->lnum)) + 1; - } else { - col = MAXCOL; - } - } else { - col = fp->col + 1; - // col(".") when the cursor is on the NUL at the end of the line - // because of "coladd" can be seen as an extra column. - if (virtual_active() && fp == &curwin->w_cursor) { - char_u *p = get_cursor_pos_ptr(); - - if (curwin->w_cursor.coladd - >= (colnr_T)win_chartabsize(curwin, p, - (curwin->w_virtcol - - curwin->w_cursor.coladd))) { - int l; - - if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) { - col += l; - } - } - } - } - } - rettv->vval.v_number = col; + get_col(argvars, rettv, false); } /* @@ -1549,24 +1557,21 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = ctx_size(); } -/// "cursor(lnum, col)" function, or -/// "cursor(list)" -/// -/// Moves the cursor to the specified line and column. -/// -/// @returns 0 when the position could be set, -1 otherwise. -static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// Set the cursor position. +/// If 'charcol' is true, then use the column number as a character offet. +/// Otherwise use the column number as a byte offset. +static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) { long line, col; long coladd = 0; bool set_curswant = true; rettv->vval.v_number = -1; - if (argvars[1].v_type == VAR_UNKNOWN) { + if (argvars[0].v_type == VAR_LIST) { pos_T pos; colnr_T curswant = -1; - if (list2fpos(argvars, &pos, NULL, &curswant) == FAIL) { + if (list2fpos(argvars, &pos, NULL, &curswant, charcol) == FAIL) { emsg(_(e_invarg)); return; } @@ -1578,16 +1583,22 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) curwin->w_curswant = curswant - 1; set_curswant = false; } - } else { + } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) + && argvars[1].v_type == VAR_NUMBER) { line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); + if (charcol) { + col = buf_charidx_to_byteidx(curbuf, line, col); + } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); } + } else { + emsg(_(e_invarg)); + return; } - if (line < 0 || col < 0 - || coladd < 0) { - return; // type error; errmsg already given + if (line < 0 || col < 0 || coladd < 0) { + return; // type error; errmsg already given } if (line > 0) { curwin->w_cursor.lnum = line; @@ -1606,6 +1617,16 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 0; } +/// "cursor(lnum, col)" function, or +/// "cursor(list)" +/// +/// Moves the cursor to the specified line and column. +/// Returns 0 when the position could be set, -1 otherwise. +static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + set_cursorpos(argvars, rettv, false); +} + // "debugbreak()" function static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3288,6 +3309,67 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = mod_mask; } +static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol) +{ + pos_T *fp = NULL; + pos_T pos; + win_T *wp = curwin; + int fnum = -1; + + if (getcurpos) { + if (argvars[0].v_type != VAR_UNKNOWN) { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp != NULL) { + fp = &wp->w_cursor; + } + } else { + fp = &curwin->w_cursor; + } + if (fp != NULL && charcol) { + pos = *fp; + pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col) - 1; + fp = &pos; + } + } else { + fp = var2fpos(&argvars[0], true, &fnum, charcol); + } + + list_T *const l = tv_list_alloc_ret(rettv, 4 + getcurpos); + tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0)); + tv_list_append_number(l, ((fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0)); + tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + if (getcurpos) { + const int save_set_curswant = curwin->w_set_curswant; + const colnr_T save_curswant = curwin->w_curswant; + const colnr_T save_virtcol = curwin->w_virtcol; + + if (wp == curwin) { + update_curswant(); + } + tv_list_append_number(l, (wp == NULL) ? 0 : ((wp->w_curswant == MAXCOL) + ? (varnumber_T)MAXCOL + : (varnumber_T)wp->w_curswant + 1)); + + // Do not change "curswant", as it is unexpected that a get + // function has a side effect. + if (wp == curwin && save_set_curswant) { + curwin->w_set_curswant = save_set_curswant; + curwin->w_curswant = save_curswant; + curwin->w_virtcol = save_virtcol; + curwin->w_valid &= ~VALID_VIRTCOL; + } + } +} + +/// "getcharpos()" function +static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + getpos_both(argvars, rettv, false, true); +} + /* * "getcharsearch()" function */ @@ -3843,69 +3925,21 @@ static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = os_get_pid(); } -static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) +/// "getcurpos(string)" function +static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T *fp = NULL; - win_T *wp = curwin; - int fnum = -1; - - if (getcurpos) { - if (argvars[0].v_type != VAR_UNKNOWN) { - wp = find_win_by_nr_or_id(&argvars[0]); - if (wp != NULL) { - fp = &wp->w_cursor; - } - } else { - fp = &curwin->w_cursor; - } - } else { - fp = var2fpos(&argvars[0], true, &fnum); - } - - list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos)); - tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0)); - tv_list_append_number(l, ((fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0)); - tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); - if (getcurpos) { - const int save_set_curswant = curwin->w_set_curswant; - const colnr_T save_curswant = curwin->w_curswant; - const colnr_T save_virtcol = curwin->w_virtcol; - - if (wp == curwin) { - update_curswant(); - } - tv_list_append_number(l, (wp == NULL) ? 0 : (wp->w_curswant == MAXCOL) - ? (varnumber_T)MAXCOL - : (varnumber_T)wp->w_curswant + 1); - - // Do not change "curswant", as it is unexpected that a get - // function has a side effect. - if (wp == curwin && save_set_curswant) { - curwin->w_set_curswant = save_set_curswant; - curwin->w_curswant = save_curswant; - curwin->w_virtcol = save_virtcol; - curwin->w_valid &= ~VALID_VIRTCOL; - } - } + getpos_both(argvars, rettv, true, false); } -/* - * "getcurpos(string)" function - */ -static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - getpos_both(argvars, rettv, true); + getpos_both(argvars, rettv, true, true); } -/* - * "getpos(string)" function - */ +/// "getpos(string)" function static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - getpos_both(argvars, rettv, false); + getpos_both(argvars, rettv, false, false); } /// "getqflist()" functions @@ -5889,13 +5923,13 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) switchwin_T switchwin; if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { check_cursor(); - fp = var2fpos(&argvars[0], true, &fnum); + fp = var2fpos(&argvars[0], true, &fnum, false); } restore_win_noblock(&switchwin, true); } } else { // use current window - fp = var2fpos(&argvars[0], true, &fnum); + fp = var2fpos(&argvars[0], true, &fnum, false); } if (fp != NULL) { @@ -9000,6 +9034,49 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// Set the cursor or mark position. +/// If 'charpos' is TRUE, then use the column number as a character offet. +/// Otherwise use the column number as a byte offset. +static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) +{ + pos_T pos; + int fnum; + colnr_T curswant = -1; + + rettv->vval.v_number = -1; + const char *const name = tv_get_string_chk(argvars); + if (name != NULL) { + if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK) { + if (pos.col != MAXCOL && --pos.col < 0) { + pos.col = 0; + } + if (name[0] == '.' && name[1] == NUL) { + // set cursor; "fnum" is ignored + curwin->w_cursor = pos; + if (curswant >= 0) { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; + } + check_cursor(); + rettv->vval.v_number = 0; + } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { + // set mark + if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { + rettv->vval.v_number = 0; + } + } else { + emsg(_(e_invarg)); + } + } + } +} + +/// "setcharpos()" function +static void f_setcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + set_position(argvars, rettv, true); +} + static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; @@ -9042,6 +9119,12 @@ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "setcursorcharpos" function +static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + set_cursorpos(argvars, rettv, true); +} + /// "setenv()" function static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -9298,41 +9381,10 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "setpos()" function - */ +/// "setpos()" function static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos; - int fnum; - colnr_T curswant = -1; - - rettv->vval.v_number = -1; - const char *const name = tv_get_string_chk(argvars); - if (name != NULL) { - if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { - if (pos.col != MAXCOL && --pos.col < 0) { - pos.col = 0; - } - if (name[0] == '.' && name[1] == NUL) { - // set cursor; "fnum" is ignored - curwin->w_cursor = pos; - if (curswant >= 0) { - curwin->w_curswant = curswant - 1; - curwin->w_set_curswant = false; - } - check_cursor(); - rettv->vval.v_number = 0; - } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { - // set mark - if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { - rettv->vval.v_number = 0; - } - } else { - emsg(_(e_invarg)); - } - } - } + set_position(argvars, rettv, false); } /* @@ -12015,7 +12067,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) pos_T *fp; int fnum = curbuf->b_fnum; - fp = var2fpos(&argvars[0], FALSE, &fnum); + fp = var2fpos(&argvars[0], false, &fnum, false); if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count && fnum == curbuf->b_fnum) { // Limit the column to a valid value, getvvcol() doesn't check. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 42ac1839e6..e583ce49b2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3091,7 +3091,7 @@ linenr_T tv_get_lnum(const typval_T *const tv) linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. int fnum; - pos_T *const fp = var2fpos(tv, true, &fnum); + pos_T *const fp = var2fpos(tv, true, &fnum, false); if (fp != NULL) { lnum = fp->lnum; } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index a10a2a0c32..54d7e54fb4 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -3408,7 +3408,7 @@ static void tagstack_push_items(win_T *wp, list_T *l) if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) { continue; } - if (list2fpos(&di->di_tv, &mark, &fnum, NULL) != OK) { + if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) { continue; } if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true)) diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index e8c4a952ee..d8b3ac3fcc 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -1,4 +1,4 @@ -" Tests for cursor(). +" Tests for cursor() and other functions that get/set the cursor position func Test_wrong_arguments() call assert_fails('call cursor(1. 3)', 'E474:') @@ -119,3 +119,188 @@ func Test_screenpos_number() close bwipe! endfunc + +func SaveVisualStartCharPos() + call add(g:VisualStartPos, getcharpos('v')) + return '' +endfunc + +" Test for the getcharpos() function +func Test_getcharpos() + call assert_fails('call getcharpos({})', 'E731:') + call assert_equal([0, 0, 0, 0], getcharpos(0)) + new + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + + " Test for '.' and '$' + normal 1G + call assert_equal([0, 1, 1, 0], getcharpos('.')) + call assert_equal([0, 4, 1, 0], getcharpos('$')) + normal 2G6l + call assert_equal([0, 2, 7, 0], getcharpos('.')) + normal 3G$ + call assert_equal([0, 3, 1, 0], getcharpos('.')) + normal 4G$ + call assert_equal([0, 4, 9, 0], getcharpos('.')) + + " Test for a mark + normal 2G7lmmgg + call assert_equal([0, 2, 8, 0], getcharpos("'m")) + delmarks m + call assert_equal([0, 0, 0, 0], getcharpos("'m")) + + " Test for the visual start column + vnoremap SaveVisualStartCharPos() + let g:VisualStartPos = [] + exe "normal 2G6lv$\ohh\o\" + call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos) + call assert_equal([0, 2, 9, 0], getcharpos('v')) + let g:VisualStartPos = [] + exe "normal 3Gv$\o\" + call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos) + let g:VisualStartPos = [] + exe "normal 1Gv$\o\" + call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos) + vunmap + + %bw! +endfunc + +" Test for the setcharpos() function +func Test_setcharpos() + call assert_equal(-1, setcharpos('.', v:_null_list)) + new + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + call setcharpos('.', [0, 1, 1, 0]) + call assert_equal([1, 1], [line('.'), col('.')]) + call setcharpos('.', [0, 2, 7, 0]) + call assert_equal([2, 9], [line('.'), col('.')]) + call setcharpos('.', [0, 3, 4, 0]) + call assert_equal([3, 1], [line('.'), col('.')]) + call setcharpos('.', [0, 3, 1, 0]) + call assert_equal([3, 1], [line('.'), col('.')]) + call setcharpos('.', [0, 4, 0, 0]) + call assert_equal([4, 1], [line('.'), col('.')]) + call setcharpos('.', [0, 4, 20, 0]) + call assert_equal([4, 9], [line('.'), col('.')]) + + " Test for mark + delmarks m + call setcharpos("'m", [0, 2, 9, 0]) + normal `m + call assert_equal([2, 11], [line('.'), col('.')]) + + %bw! + call assert_equal(-1, setcharpos('.', [10, 3, 1, 0])) +endfunc + +func SaveVisualStartCharCol() + call add(g:VisualStartCol, charcol('v')) + return '' +endfunc + +" Test for the charcol() function +func Test_charcol() + call assert_fails('call charcol({})', 'E731:') + call assert_equal(0, charcol(0)) + new + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + + " Test for '.' and '$' + normal 1G + call assert_equal(1, charcol('.')) + call assert_equal(1, charcol('$')) + normal 2G6l + call assert_equal(7, charcol('.')) + call assert_equal(10, charcol('$')) + normal 3G$ + call assert_equal(1, charcol('.')) + call assert_equal(2, charcol('$')) + normal 4G$ + call assert_equal(9, charcol('.')) + call assert_equal(10, charcol('$')) + + " Test for [lnum, '$'] + call assert_equal(1, charcol([1, '$'])) + call assert_equal(10, charcol([2, '$'])) + call assert_equal(2, charcol([3, '$'])) + call assert_equal(0, charcol([5, '$'])) + + " Test for a mark + normal 2G7lmmgg + call assert_equal(8, charcol("'m")) + delmarks m + call assert_equal(0, charcol("'m")) + + " Test for the visual start column + vnoremap SaveVisualStartCharCol() + let g:VisualStartCol = [] + exe "normal 2G6lv$\ohh\o\" + call assert_equal([7, 9, 5], g:VisualStartCol) + call assert_equal(9, charcol('v')) + let g:VisualStartCol = [] + exe "normal 3Gv$\o\" + call assert_equal([1, 1], g:VisualStartCol) + let g:VisualStartCol = [] + exe "normal 1Gv$\o\" + call assert_equal([1, 1], g:VisualStartCol) + vunmap + + %bw! +endfunc + +" Test for getcursorcharpos() +func Test_getcursorcharpos() + call assert_equal(getcursorcharpos(), getcursorcharpos(0)) + call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(-1)) + call assert_equal([0, 0, 0, 0, 0], getcursorcharpos(1999)) + + new + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + normal 1G9l + call assert_equal([0, 1, 1, 0, 1], getcursorcharpos()) + normal 2G9l + call assert_equal([0, 2, 9, 0, 14], getcursorcharpos()) + normal 3G9l + call assert_equal([0, 3, 1, 0, 1], getcursorcharpos()) + normal 4G9l + call assert_equal([0, 4, 9, 0, 9], getcursorcharpos()) + + let winid = win_getid() + normal 2G5l + wincmd w + call assert_equal([0, 2, 6, 0, 11], getcursorcharpos(winid)) + %bw! +endfunc + +" Test for setcursorcharpos() +func Test_setcursorcharpos() + call assert_fails('call setcursorcharpos(v:_null_list)', 'E474:') + call assert_fails('call setcursorcharpos([1])', 'E474:') + call assert_fails('call setcursorcharpos([1, 1, 1, 1, 1])', 'E474:') + new + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + normal G + call setcursorcharpos([1, 1]) + call assert_equal([1, 1], [line('.'), col('.')]) + call setcursorcharpos([2, 7, 0]) + call assert_equal([2, 9], [line('.'), col('.')]) + call setcursorcharpos(3, 4) + call assert_equal([3, 1], [line('.'), col('.')]) + call setcursorcharpos([3, 1]) + call assert_equal([3, 1], [line('.'), col('.')]) + call setcursorcharpos([4, 0, 0, 0]) + call assert_equal([4, 1], [line('.'), col('.')]) + call setcursorcharpos([4, 20]) + call assert_equal([4, 9], [line('.'), col('.')]) + normal 1G + call setcursorcharpos([100, 100, 100, 100]) + call assert_equal([4, 9], [line('.'), col('.')]) + normal 1G + call setcursorcharpos('$', 1) + call assert_equal([4, 1], [line('.'), col('.')]) + + %bw! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 8c3244c9a1c4b82ab86431f173716ce606b83813 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 04:46:16 +0800 Subject: vim-patch:8.2.2363: curpos() does not accept a string argument as before Problem: curpos() does not accept a string argument as before. solution: Make a string argument work again. (Yegappan Lakshmanan, closes vim/vim#7690 https://github.com/vim/vim/commit/9ebcf231bdccc1673cc92b20f5190fc577ad29d0 --- src/nvim/eval/funcs.c | 2 +- src/nvim/testdir/test_cursor_func.vim | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b188ded368..edf6ed3c12 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1584,7 +1584,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) set_curswant = false; } } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) - && argvars[1].v_type == VAR_NUMBER) { + && (argvars[1].v_type == VAR_NUMBER || argvars[1].v_type == VAR_STRING)) { line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index d8b3ac3fcc..f2ffd50726 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -24,6 +24,9 @@ func Test_move_cursor() " below last line goes to last line call cursor(9, 1) call assert_equal([4, 1, 0, 1], getcurpos()[1:]) + " pass string arguments + call cursor('3', '3') + call assert_equal([3, 3, 0, 3], getcurpos()[1:]) call setline(1, ["\"]) call cursor(1, 1, 1) -- cgit From b518b577ea89439359dc9faea5ec1bf341c50e52 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 5 Feb 2022 18:00:58 +0000 Subject: fix(lua): restore priority of the preloader Neovim currently places its own loader for searching runtime files at the front of `package.loaders`. This prevents any preloaders in `package.preload` from being used. This change fixes that by moving the default package preloader to run before Neovim's loader. For example, LuaJIT provides preloaders for the built-in modules `ffi` and `bit`, so this optimisation will improve the loading of those. --- src/nvim/lua/vim.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 731e7d8d36..f5993c3f55 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -87,7 +87,8 @@ function vim._load_package(name) return nil end -table.insert(package.loaders, 1, vim._load_package) +-- Insert vim._load_package after the preloader at position 2 +table.insert(package.loaders, 2, vim._load_package) -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c -- cgit From d65ee129143fedd43178c9be52095b5d2d06b5c2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 16:29:12 +0800 Subject: vim-patch:8.2.1741: pathshorten() only supports using one character Problem: pathshorten() only supports using one character. Solution: Add an argument to control the length. (closes vim/vim#7006) https://github.com/vim/vim/commit/6a33ef0deb5c75c003a9f3bd1c57f3ca5e77327e Cherry-pick a line in test from patch 8.2.0634. Use Nvim's config paths in docs. shorten_dir() returning a pointer looks a bit confusing here, as it is actually the same pointer passed to it, and it doesn't really reduce much code, so change it back to void. Assigning rettv->vval.v_string = NULL is not needed if a pointer is within 64 bits. While this is usually the case, I'm not sure if it can be taken for granted. --- src/nvim/eval.lua | 2 +- src/nvim/eval/funcs.c | 19 +++++++++++++++---- src/nvim/path.c | 29 +++++++++++++++++++++-------- src/nvim/screen.c | 2 +- src/nvim/testdir/test_functions.vim | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5c85d764fb..eedc8ac45d 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -262,7 +262,7 @@ return { nextnonblank={args=1, base=1}, nr2char={args={1, 2}, base=1}, ['or']={args=2, base=1}, - pathshorten={args=1, base=1}, + pathshorten={args={1, 2}, base=1}, pow={args=2, base=1}, prevnonblank={args=1, base=1}, printf={args=varargs(1), base=2}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index edf6ed3c12..475c6bfffb 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6846,12 +6846,23 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { + int trim_len = 1; + + if (argvars[1].v_type != VAR_UNKNOWN) { + trim_len = (int)tv_get_number(&argvars[1]); + if (trim_len < 1) { + trim_len = 1; + } + } + rettv->v_type = VAR_STRING; - const char *const s = tv_get_string_chk(&argvars[0]); - if (!s) { - return; + const char_u *p = (char_u *)tv_get_string_chk(&argvars[0]); + if (p == NULL) { + rettv->vval.v_string = NULL; + } else { + rettv->vval.v_string = vim_strsave(p); + shorten_dir_len(rettv->vval.v_string, trim_len); } - rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s)); } /* diff --git a/src/nvim/path.c b/src/nvim/path.c index 39e276e0a5..fe564182d8 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -269,16 +269,17 @@ int vim_ispathlistsep(int c) #endif } -/* - * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" - * It's done in-place. - */ -char_u *shorten_dir(char_u *str) +/// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" +/// "trim_len" specifies how many characters to keep for each directory. +/// Must be 1 or more. +/// It's done in-place. +void shorten_dir_len(char_u *str, int trim_len) { char_u *tail = path_tail(str); char_u *d = str; bool skip = false; - for (char_u *s = str;; ++s) { + int dirchunk_len = 0; + for (char_u *s = str;; s++) { if (s >= tail) { // copy the whole tail *d++ = *s; if (*s == NUL) { @@ -287,10 +288,16 @@ char_u *shorten_dir(char_u *str) } else if (vim_ispathsep(*s)) { // copy '/' and next char *d++ = *s; skip = false; + dirchunk_len = 0; } else if (!skip) { *d++ = *s; // copy next char if (*s != '~' && *s != '.') { // and leading "~" and "." - skip = true; + dirchunk_len++; // only count word chars for the size + // keep copying chars until we have our preferred length (or + // until the above if/else branches move us along) + if (dirchunk_len >= trim_len) { + skip = true; + } } int l = utfc_ptr2len(s); while (--l > 0) { @@ -298,7 +305,13 @@ char_u *shorten_dir(char_u *str) } } } - return str; +} + +/// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" +/// It's done in-place. +void shorten_dir(char_u *str) +{ + shorten_dir_len(str, 1); } /* diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7e7115b6af..6b2a2afa41 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7350,7 +7350,7 @@ void draw_tabline(void) if (room > 0) { // Get buffer name in NameBuff[] get_trans_bufname(cwp->w_buffer); - (void)shorten_dir(NameBuff); + shorten_dir(NameBuff); len = vim_strsize(NameBuff); p = NameBuff; while (len > room) { diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6c2b0b97b6..438bed51c6 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -376,6 +376,25 @@ func Test_pathshorten() call assert_equal('~.f/bar', pathshorten('~.foo/bar')) call assert_equal('.~f/bar', pathshorten('.~foo/bar')) call assert_equal('~/f/bar', pathshorten('~/foo/bar')) + call assert_fails('call pathshorten([])', 'E730:') + + " test pathshorten with optional variable to set preferred size of shortening + call assert_equal('', pathshorten('', 2)) + call assert_equal('foo', pathshorten('foo', 2)) + call assert_equal('/foo', pathshorten('/foo', 2)) + call assert_equal('fo/', pathshorten('foo/', 2)) + call assert_equal('fo/bar', pathshorten('foo/bar', 2)) + call assert_equal('fo/ba/foobar', pathshorten('foo/bar/foobar', 2)) + call assert_equal('/fo/ba/foobar', pathshorten('/foo/bar/foobar', 2)) + call assert_equal('.fo/bar', pathshorten('.foo/bar', 2)) + call assert_equal('~fo/bar', pathshorten('~foo/bar', 2)) + call assert_equal('~.fo/bar', pathshorten('~.foo/bar', 2)) + call assert_equal('.~fo/bar', pathshorten('.~foo/bar', 2)) + call assert_equal('~/fo/bar', pathshorten('~/foo/bar', 2)) + call assert_fails('call pathshorten([],2)', 'E730:') + call assert_notequal('~/fo/bar', pathshorten('~/foo/bar', 3)) + call assert_equal('~/foo/bar', pathshorten('~/foo/bar', 3)) + call assert_equal('~/f/bar', pathshorten('~/foo/bar', 0)) endfunc func Test_strpart() -- cgit From 05f38bbede99ea0223550554bcfb9ab43d036d1f Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 6 Feb 2022 12:09:16 +0100 Subject: vim-patch:8.2.4305: tex filetype detection fails Problem: Tex filetype detection fails. Solution: Check value to be positive. (closes vim/vim#9704) https://github.com/vim/vim/commit/e5b7897585eccec84431d8b23df5cde2e283828c --- src/nvim/testdir/test_filetype.vim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index d4e5563865..713875b9d6 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -1277,4 +1277,23 @@ func Test_frm_file() filetype off endfunc +func Test_tex_file() + filetype on + + " only tests one case, should do more + let lines =<< trim END + % This is a sentence. + + This is a sentence. + END + call writefile(lines, "Xfile.tex") + split Xfile.tex + call assert_equal('plaintex', &filetype) + bwipe + + call delete('Xfile.tex') + filetype off +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 06d2d271d780da1ad009654a2603eba534a5e280 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 6 Feb 2022 12:15:24 +0100 Subject: vim-patch:8.2.4306: no test for fixed perl filetype check Problem: No test for fixed perl filetype check. Solution: Add a test. Sort test functions. https://github.com/vim/vim/commit/500761b1cf666f030009d2dcdacfdce28f68f43d --- src/nvim/testdir/test_filetype.vim | 641 +++++++++++++++++++------------------ 1 file changed, 333 insertions(+), 308 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 713875b9d6..4ef35b3a46 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -731,101 +731,126 @@ func Test_setfiletype_completion() call assert_equal('"setfiletype java javacc javascript javascriptreact', @:) endfunc -func Test_hook_file() +""""""""""""""""""""""""""""""""""""""""""""""""" +" Tests for specific extentions and filetypes. +" Keep sorted. +""""""""""""""""""""""""""""""""""""""""""""""""" + +func Test_bas_file() filetype on - call writefile(['[Trigger]', 'this is pacman config'], 'Xfile.hook') - split Xfile.hook - call assert_equal('dosini', &filetype) + call writefile(['looks like BASIC'], 'Xfile.bas') + split Xfile.bas + call assert_equal('basic', &filetype) bwipe! - call writefile(['not pacman'], 'Xfile.hook') - split Xfile.hook - call assert_notequal('dosini', &filetype) + " Test dist#ft#FTbas() + + let g:filetype_bas = 'freebasic' + split Xfile.bas + call assert_equal('freebasic', &filetype) bwipe! + unlet g:filetype_bas - call delete('Xfile.hook') - filetype off -endfunc + " FreeBASIC -func Test_tf_file() - filetype on + call writefile(["/' FreeBASIC multiline comment '/"], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! - call writefile([';;; TF MUD client is super duper cool'], 'Xfile.tf') - split Xfile.tf - call assert_equal('tf', &filetype) + call writefile(['#define TESTING'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) bwipe! - call writefile(['provider "azurerm" {'], 'Xfile.tf') - split Xfile.tf - call assert_equal('terraform', &filetype) + call writefile(['option byval'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) bwipe! - call delete('Xfile.tf') - filetype off -endfunc + call writefile(['extern "C"'], 'Xfile.bas') + split Xfile.bas + call assert_equal('freebasic', &filetype) + bwipe! + " QB64 -func Test_ts_file() - filetype on + call writefile(['$LET TESTING = 1'], 'Xfile.bas') + split Xfile.bas + call assert_equal('qb64', &filetype) + bwipe! - call writefile([''], 'Xfile.ts') - split Xfile.ts - call assert_equal('xml', &filetype) + call writefile(['OPTION _EXPLICIT'], 'Xfile.bas') + split Xfile.bas + call assert_equal('qb64', &filetype) bwipe! - call writefile(['// looks like Typescript'], 'Xfile.ts') - split Xfile.ts - call assert_equal('typescript', &filetype) + " Visual Basic + + call writefile(['Attribute VB_NAME = "Testing"'], 'Xfile.bas') + split Xfile.bas + call assert_equal('vb', &filetype) bwipe! - call delete('Xfile.hook') + call delete('Xfile.bas') filetype off endfunc -func Test_ttl_file() +func Test_dep3patch_file() filetype on - call writefile(['@base .'], 'Xfile.ttl') - split Xfile.ttl - call assert_equal('turtle', &filetype) - bwipe! + call assert_true(mkdir('debian/patches', 'p')) - call writefile(['looks like Tera Term Language'], 'Xfile.ttl') - split Xfile.ttl - call assert_equal('teraterm', &filetype) + " series files are not patches + call writefile(['Description: some awesome patch'], 'debian/patches/series') + split debian/patches/series + call assert_notequal('dep3patch', &filetype) bwipe! - call delete('Xfile.ttl') - filetype off -endfunc + " diff/patch files without the right headers should still show up as ft=diff + call writefile([], 'debian/patches/foo.diff') + split debian/patches/foo.diff + call assert_equal('diff', &filetype) + bwipe! -func Test_pp_file() - filetype on + " Files with the right headers are detected as dep3patch, even if they don't + " have a diff/patch extension + call writefile(['Subject: dep3patches'], 'debian/patches/bar') + split debian/patches/bar + call assert_equal('dep3patch', &filetype) + bwipe! - call writefile(['looks like puppet'], 'Xfile.pp') - split Xfile.pp - call assert_equal('puppet', &filetype) + " Files in sub-directories are detected + call assert_true(mkdir('debian/patches/s390x', 'p')) + call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar') + split debian/patches/s390x/bar + call assert_equal('dep3patch', &filetype) bwipe! - let g:filetype_pp = 'pascal' - split Xfile.pp - call assert_equal('pascal', &filetype) + " The detection stops when seeing the "header end" marker + call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz') + split debian/patches/baz + call assert_notequal('dep3patch', &filetype) bwipe! - unlet g:filetype_pp - " Test dist#ft#FTpp() - call writefile(['{ pascal comment'], 'Xfile.pp') - split Xfile.pp - call assert_equal('pascal', &filetype) + call delete('debian', 'rf') +endfunc + +func Test_dsl_file() + filetype on + + call writefile([' '], 'Xfile.m') - split Xfile.m - call assert_equal('objc', &filetype) + call writefile(['FoamFile {', ' object something;'], '0/Xfile') + split 0/Xfile + call assert_equal('foam', &filetype) bwipe! - call writefile(['#define FORTY_TWO'], 'Xfile.m') - split Xfile.m - call assert_equal('objc', &filetype) + call writefile(['FoamFile {', ' object something;'], '0.orig/Xfile') + split 0.orig/Xfile + call assert_equal('foam', &filetype) bwipe! - " Octave + call delete('0', 'rf') + call delete('0.orig', 'rf') + call delete('Xfile1Dict') + call delete('Xfile1Dict.something') + call delete('XfileProperties') + call delete('XfileProperties.something') + filetype off +endfunc - call writefile(['# Octave line comment'], 'Xfile.m') - split Xfile.m - call assert_equal('octave', &filetype) - bwipe! +func Test_frm_file() + filetype on - call writefile(['%!test "Octave test"'], 'Xfile.m') - split Xfile.m - call assert_equal('octave', &filetype) + call writefile(['looks like FORM'], 'Xfile.frm') + split Xfile.frm + call assert_equal('form', &filetype) bwipe! - call writefile(['unwind_protect'], 'Xfile.m') - split Xfile.m - call assert_equal('octave', &filetype) - bwipe! + " Test dist#ft#FTfrm() - call writefile(['try; 42; end_try_catch'], 'Xfile.m') - split Xfile.m - call assert_equal('octave', &filetype) + let g:filetype_frm = 'form' + split Xfile.frm + call assert_equal('form', &filetype) bwipe! + unlet g:filetype_frm - " Mathematica + " Visual Basic - call writefile(['(* Mathematica comment'], 'Xfile.m') - split Xfile.m - call assert_equal('mma', &filetype) + call writefile(['Begin VB.Form Form1'], 'Xfile.frm') + split Xfile.frm + call assert_equal('vb', &filetype) bwipe! - " MATLAB + call delete('Xfile.frm') + filetype off +endfunc - call writefile(['% MATLAB line comment'], 'Xfile.m') - split Xfile.m - call assert_equal('matlab', &filetype) - bwipe! - - " Murphi - - call writefile(['-- Murphi comment'], 'Xfile.m') - split Xfile.m - call assert_equal('murphi', &filetype) - bwipe! - - call writefile(['/* Murphi block comment */', 'Type'], 'Xfile.m') - split Xfile.m - call assert_equal('murphi', &filetype) - bwipe! - - call writefile(['Type'], 'Xfile.m') - split Xfile.m - call assert_equal('murphi', &filetype) - bwipe! - - call delete('Xfile.m') - filetype off -endfunc - -func Test_xpm_file() - filetype on - - call writefile(['this is XPM2'], 'file.xpm') - split file.xpm - call assert_equal('xpm2', &filetype) - bwipe! - - call delete('file.xpm') - filetype off -endfunc - -func Test_fs_file() - filetype on - - call writefile(['looks like F#'], 'Xfile.fs') - split Xfile.fs - call assert_equal('fsharp', &filetype) +func Test_fs_file() + filetype on + + call writefile(['looks like F#'], 'Xfile.fs') + split Xfile.fs + call assert_equal('fsharp', &filetype) bwipe! let g:filetype_fs = 'forth' @@ -1048,68 +1025,6 @@ func Test_fs_file() filetype off endfunc -func Test_dep3patch_file() - filetype on - - call assert_true(mkdir('debian/patches', 'p')) - - " series files are not patches - call writefile(['Description: some awesome patch'], 'debian/patches/series') - split debian/patches/series - call assert_notequal('dep3patch', &filetype) - bwipe! - - " diff/patch files without the right headers should still show up as ft=diff - call writefile([], 'debian/patches/foo.diff') - split debian/patches/foo.diff - call assert_equal('diff', &filetype) - bwipe! - - " Files with the right headers are detected as dep3patch, even if they don't - " have a diff/patch extension - call writefile(['Subject: dep3patches'], 'debian/patches/bar') - split debian/patches/bar - call assert_equal('dep3patch', &filetype) - bwipe! - - " Files in sub-directories are detected - call assert_true(mkdir('debian/patches/s390x', 'p')) - call writefile(['Subject: dep3patches'], 'debian/patches/s390x/bar') - split debian/patches/s390x/bar - call assert_equal('dep3patch', &filetype) - bwipe! - - " The detection stops when seeing the "header end" marker - call writefile(['---', 'Origin: the cloud'], 'debian/patches/baz') - split debian/patches/baz - call assert_notequal('dep3patch', &filetype) - bwipe! - - call delete('debian', 'rf') -endfunc - -func Test_patch_file() - filetype on - - call writefile([], 'Xfile.patch') - split Xfile.patch - call assert_equal('diff', &filetype) - bwipe! - - call writefile(['From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch') - split Xfile.patch - call assert_equal('gitsendemail', &filetype) - bwipe! - - call writefile(['From 0000000000000000000000000000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch') - split Xfile.patch - call assert_equal('gitsendemail', &filetype) - bwipe! - - call delete('Xfile.patch') - filetype off -endfunc - func Test_git_file() filetype on @@ -1139,141 +1054,188 @@ func Test_git_file() filetype off endfunc -func Test_foam_file() +func Test_hook_file() filetype on - call assert_true(mkdir('0', 'p')) - call assert_true(mkdir('0.orig', 'p')) - call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict') - split Xfile1Dict - call assert_equal('foam', &filetype) + call writefile(['[Trigger]', 'this is pacman config'], 'Xfile.hook') + split Xfile.hook + call assert_equal('dosini', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], 'Xfile1Dict.something') - split Xfile1Dict.something - call assert_equal('foam', &filetype) + call writefile(['not pacman'], 'Xfile.hook') + split Xfile.hook + call assert_notequal('dosini', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], 'XfileProperties') - split XfileProperties - call assert_equal('foam', &filetype) + call delete('Xfile.hook') + filetype off +endfunc + +func Test_m_file() + filetype on + + call writefile(['looks like Matlab'], 'Xfile.m') + split Xfile.m + call assert_equal('matlab', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something') - split XfileProperties.something - call assert_equal('foam', &filetype) + let g:filetype_m = 'octave' + split Xfile.m + call assert_equal('octave', &filetype) bwipe! + unlet g:filetype_m - call writefile(['FoamFile {', ' object something;'], 'XfileProperties') - split XfileProperties - call assert_equal('foam', &filetype) + " Test dist#ft#FTm() + + " Objective-C + + call writefile(['// Objective-C line comment'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], 'XfileProperties.something') - split XfileProperties.something - call assert_equal('foam', &filetype) + call writefile(['/* Objective-C block comment */'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], '0/Xfile') - split 0/Xfile - call assert_equal('foam', &filetype) + call writefile(['#import "test.m"'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) bwipe! - call writefile(['FoamFile {', ' object something;'], '0.orig/Xfile') - split 0.orig/Xfile - call assert_equal('foam', &filetype) + call writefile(['#include '], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) bwipe! - call delete('0', 'rf') - call delete('0.orig', 'rf') - filetype off -endfunc + call writefile(['#define FORTY_TWO'], 'Xfile.m') + split Xfile.m + call assert_equal('objc', &filetype) + bwipe! -func Test_bas_file() - filetype on + " Octave - call writefile(['looks like BASIC'], 'Xfile.bas') - split Xfile.bas - call assert_equal('basic', &filetype) + call writefile(['# Octave line comment'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) bwipe! - " Test dist#ft#FTbas() - - let g:filetype_bas = 'freebasic' - split Xfile.bas - call assert_equal('freebasic', &filetype) + call writefile(['%!test "Octave test"'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) bwipe! - unlet g:filetype_bas - " FreeBASIC + call writefile(['unwind_protect'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) + bwipe! - call writefile(["/' FreeBASIC multiline comment '/"], 'Xfile.bas') - split Xfile.bas - call assert_equal('freebasic', &filetype) + call writefile(['try; 42; end_try_catch'], 'Xfile.m') + split Xfile.m + call assert_equal('octave', &filetype) bwipe! - call writefile(['#define TESTING'], 'Xfile.bas') - split Xfile.bas - call assert_equal('freebasic', &filetype) + " Mathematica + + call writefile(['(* Mathematica comment'], 'Xfile.m') + split Xfile.m + call assert_equal('mma', &filetype) bwipe! - call writefile(['option byval'], 'Xfile.bas') - split Xfile.bas - call assert_equal('freebasic', &filetype) + " MATLAB + + call writefile(['% MATLAB line comment'], 'Xfile.m') + split Xfile.m + call assert_equal('matlab', &filetype) bwipe! - call writefile(['extern "C"'], 'Xfile.bas') - split Xfile.bas - call assert_equal('freebasic', &filetype) + " Murphi + + call writefile(['-- Murphi comment'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) bwipe! - " QB64 + call writefile(['/* Murphi block comment */', 'Type'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) + bwipe! - call writefile(['$LET TESTING = 1'], 'Xfile.bas') - split Xfile.bas - call assert_equal('qb64', &filetype) + call writefile(['Type'], 'Xfile.m') + split Xfile.m + call assert_equal('murphi', &filetype) bwipe! - call writefile(['OPTION _EXPLICIT'], 'Xfile.bas') - split Xfile.bas - call assert_equal('qb64', &filetype) + call delete('Xfile.m') + filetype off +endfunc + +func Test_patch_file() + filetype on + + call writefile([], 'Xfile.patch') + split Xfile.patch + call assert_equal('diff', &filetype) bwipe! - " Visual Basic + call writefile(['From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch') + split Xfile.patch + call assert_equal('gitsendemail', &filetype) + bwipe! - call writefile(['Attribute VB_NAME = "Testing"'], 'Xfile.bas') - split Xfile.bas - call assert_equal('vb', &filetype) + call writefile(['From 0000000000000000000000000000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001'], 'Xfile.patch') + split Xfile.patch + call assert_equal('gitsendemail', &filetype) bwipe! - call delete('Xfile.bas') + call delete('Xfile.patch') filetype off endfunc -func Test_frm_file() +func Test_perl_file() filetype on - call writefile(['looks like FORM'], 'Xfile.frm') - split Xfile.frm - call assert_equal('form', &filetype) - bwipe! + " only tests one case, should do more + let lines =<< trim END - " Test dist#ft#FTfrm() + use a + END + call writefile(lines, "Xfile.t") + split Xfile.t + call assert_equal('perl', &filetype) + bwipe - let g:filetype_frm = 'form' - split Xfile.frm - call assert_equal('form', &filetype) + call delete('Xfile.t') + filetype off +endfunc + +func Test_pp_file() + filetype on + + call writefile(['looks like puppet'], 'Xfile.pp') + split Xfile.pp + call assert_equal('puppet', &filetype) bwipe! - unlet g:filetype_frm - " Visual Basic + let g:filetype_pp = 'pascal' + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + unlet g:filetype_pp - call writefile(['Begin VB.Form Form1'], 'Xfile.frm') - split Xfile.frm - call assert_equal('vb', &filetype) + " Test dist#ft#FTpp() + call writefile(['{ pascal comment'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) bwipe! - call delete('Xfile.frm') + call writefile(['procedure pascal'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + call delete('Xfile.pp') filetype off endfunc @@ -1295,5 +1257,68 @@ func Test_tex_file() filetype off endfunc +func Test_tf_file() + filetype on + + call writefile([';;; TF MUD client is super duper cool'], 'Xfile.tf') + split Xfile.tf + call assert_equal('tf', &filetype) + bwipe! + + call writefile(['provider "azurerm" {'], 'Xfile.tf') + split Xfile.tf + call assert_equal('terraform', &filetype) + bwipe! + + call delete('Xfile.tf') + filetype off +endfunc + +func Test_ts_file() + filetype on + + call writefile([''], 'Xfile.ts') + split Xfile.ts + call assert_equal('xml', &filetype) + bwipe! + + call writefile(['// looks like Typescript'], 'Xfile.ts') + split Xfile.ts + call assert_equal('typescript', &filetype) + bwipe! + + call delete('Xfile.ts') + filetype off +endfunc + +func Test_ttl_file() + filetype on + + call writefile(['@base .'], 'Xfile.ttl') + split Xfile.ttl + call assert_equal('turtle', &filetype) + bwipe! + + call writefile(['looks like Tera Term Language'], 'Xfile.ttl') + split Xfile.ttl + call assert_equal('teraterm', &filetype) + bwipe! + + call delete('Xfile.ttl') + filetype off +endfunc + +func Test_xpm_file() + filetype on + + call writefile(['this is XPM2'], 'file.xpm') + split file.xpm + call assert_equal('xpm2', &filetype) + bwipe! + + call delete('file.xpm') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From daf7c17cdb237639db35c30bcf05e10b3edb3d68 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 21:13:03 +0800 Subject: vim-patch:8.2.4303: a few messages should not be translated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: A few messages should not be translated. Solution: Remove _(). (Dominique Pellé, closes vim/vim#9702) https://github.com/vim/vim/commit/cd53eed2c55f2256008962965b1de1d1df76d535 --- src/nvim/syntax.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 3aef654a8e..746a5b83a0 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3168,9 +3168,9 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing) if (*arg == NUL) { switch (curwin->w_s->b_syn_foldlevel) { case SYNFLD_START: - msg(_("syntax foldlevel start")); break; + msg("syntax foldlevel start"); break; case SYNFLD_MINIMUM: - msg(_("syntax foldlevel minimum")); break; + msg("syntax foldlevel minimum"); break; default: break; } @@ -3209,11 +3209,11 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) next = skiptowhite(arg); if (*arg == NUL) { if (curwin->w_s->b_syn_spell == SYNSPL_TOP) { - msg(_("syntax spell toplevel")); + msg("syntax spell toplevel"); } else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) { - msg(_("syntax spell notoplevel")); + msg("syntax spell notoplevel"); } else { - msg(_("syntax spell default")); + msg("syntax spell default"); } } else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { curwin->w_s->b_syn_spell = SYNSPL_TOP; -- cgit From 8f0b3cadbebef3e0209a11fd362918a0e97a08ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Feb 2022 21:13:03 +0800 Subject: vim-patch:8.2.4307: a few more messages should not be translated Problem: A few more messages should not be translated. Solution: Remove _(). https://github.com/vim/vim/commit/0c1550d9e94046d3fc9a8ad70b895eaa1e53fca5 --- src/nvim/syntax.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 746a5b83a0..119f6e811f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3112,9 +3112,9 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing) next = skiptowhite(arg); if (*arg == NUL) { if (curwin->w_s->b_syn_conceal) { - msg(_("syntax conceal on")); + msg("syntax conceal on"); } else { - msg(_("syntax conceal off")); + msg("syntax conceal off"); } } else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) { curwin->w_s->b_syn_conceal = true; @@ -3141,9 +3141,9 @@ static void syn_cmd_case(exarg_T *eap, int syncing) next = skiptowhite(arg); if (*arg == NUL) { if (curwin->w_s->b_syn_ic) { - msg(_("syntax case ignore")); + msg("syntax case ignore"); } else { - msg(_("syntax case match")); + msg("syntax case match"); } } else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) { curwin->w_s->b_syn_ic = false; @@ -3245,7 +3245,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing) if (*arg == NUL) { msg_puts("\n"); if (curwin->w_s->b_syn_isk != empty_option) { - msg_puts(_("syntax iskeyword ")); + msg_puts("syntax iskeyword "); msg_outtrans(curwin->w_s->b_syn_isk); } else { msg_outtrans((char_u *)_("syntax iskeyword not set")); -- cgit From 7b8fcf0234441a7db29897d60c728ad2adb83464 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1163 Problem: Expressions "0 + v:true" and "'' . v:true" cause an error. Solution: Return something sensible when using a special variable as a number or as a string. (suggested by Damien) https://github.com/vim/vim/commit/17a13437c9414a8693369a97f3be2fc8ad48c12e Code is N/A. This only ports the tests. Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index c59cab5f36..c3e882ad05 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1156,6 +1156,16 @@ func Test_type() call assert_equal(v:t_list, type(v:_null_list)) call assert_equal(v:t_dict, type(v:_null_dict)) call assert_equal(v:t_blob, type(v:_null_blob)) + + call assert_equal(0, 0 + v:false) + call assert_equal(1, 0 + v:true) + " call assert_equal(0, 0 + v:none) + call assert_equal(0, 0 + v:null) + + call assert_equal('false', '' . v:false) + call assert_equal('true', '' . v:true) + " call assert_equal('none', '' . v:none) + call assert_equal('null', '' . v:null) endfunc "------------------------------------------------------------------------------- -- cgit From 6a00b1689653546f0469d7f449b3709430f5883b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1164 Problem: No tests for comparing special variables. Error in jsondecode() not reported. test_json does not work Japanse system. Solution: Set scriptencoding. (Ken Takata) Add a few more tests. Add error. https://github.com/vim/vim/commit/6039c7f05376f0e470cf62bf2757e653aea357f3 Code is N/A. This only ports the tests. Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index c3e882ad05..fc95d70a88 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1166,6 +1166,18 @@ func Test_type() call assert_equal('true', '' . v:true) " call assert_equal('none', '' . v:none) call assert_equal('null', '' . v:null) + + call assert_true(v:false == 0) + call assert_false(v:false != 0) + call assert_true(v:true == 1) + call assert_false(v:true != 1) + call assert_false(v:true == v:false) + call assert_true(v:true != v:false) + + call assert_true(v:null == 0) + call assert_false(v:null != 0) + " call assert_true(v:none == 0) + " call assert_false(v:none != 0) endfunc "------------------------------------------------------------------------------- -- cgit From b3a14a71b095f9ad2abffbc9b61ae574907f2c21 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1167 Problem: No tests for "is" and "isnot" with the new variables. Solution: Add tests. https://github.com/vim/vim/commit/04369229657f182d35b471eb8b38f273a4d9ef65 Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index fc95d70a88..674a28f154 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1178,6 +1178,30 @@ func Test_type() call assert_false(v:null != 0) " call assert_true(v:none == 0) " call assert_false(v:none != 0) + + call assert_true(v:false is v:false) + call assert_true(v:true is v:true) + " call assert_true(v:none is v:none) + call assert_true(v:null is v:null) + + call assert_false(v:false isnot v:false) + call assert_false(v:true isnot v:true) + " call assert_false(v:none isnot v:none) + call assert_false(v:null isnot v:null) + + call assert_false(v:false is 0) + call assert_false(v:true is 1) + call assert_false(v:true is v:false) + " call assert_false(v:none is 0) + call assert_false(v:null is 0) + " call assert_false(v:null is v:none) + + call assert_true(v:false isnot 0) + call assert_true(v:true isnot 1) + call assert_true(v:true isnot v:false) + " call assert_true(v:none isnot 0) + call assert_true(v:null isnot 0) + " call assert_true(v:null isnot v:none) endfunc "------------------------------------------------------------------------------- -- cgit From bf8f2ebb79d4c16dae0268ef26bdbc032069000d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1173 Problem: No test for new behavior of v:true et al. Solution: Add a test. https://github.com/vim/vim/commit/65591001e405cbaaf9772c9375d0bb6049cf9a3a Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 674a28f154..4088aa33dc 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1202,6 +1202,11 @@ func Test_type() " call assert_true(v:none isnot 0) call assert_true(v:null isnot 0) " call assert_true(v:null isnot v:none) + + call assert_equal(v:false, eval(string(v:false))) + call assert_equal(v:true, eval(string(v:true))) + " call assert_equal(v:none, eval(string(v:none))) + call assert_equal(v:null, eval(string(v:null))) endfunc "------------------------------------------------------------------------------- -- cgit From 3fa5d501835becf68bd50498fe74c786c792d6d6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1178 Problem: empty() doesn't work for the new special variables. Solution: Make empty() work. (Damien) https://github.com/vim/vim/commit/767d8c1a1ae762ecf47297c168b8c23caf05d30a Code is N/A. This only ports the tests. Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 4088aa33dc..75a1b1a881 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1207,6 +1207,11 @@ func Test_type() call assert_equal(v:true, eval(string(v:true))) " call assert_equal(v:none, eval(string(v:none))) call assert_equal(v:null, eval(string(v:null))) + + call assert_true(empty(v:false)) + call assert_false(empty(v:true)) + call assert_true(empty(v:null)) + " call assert_true(empty(v:none)) endfunc "------------------------------------------------------------------------------- -- cgit From a937fc53ef3652b81be247d8ddad091652327a71 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1181 Problem: free_tv() can't handle special variables. (Damien) Solution: Add the variable type. https://github.com/vim/vim/commit/6650a694547eb744afa060ec62dd8270e99db9f2 Code is N/A. This only ports the tests. --- src/nvim/testdir/test_vimscript.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 75a1b1a881..0d9aa647fd 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1212,6 +1212,16 @@ func Test_type() call assert_false(empty(v:true)) call assert_true(empty(v:null)) " call assert_true(empty(v:none)) + + func ChangeYourMind() + try + return v:true + finally + return 'something else' + endtry + endfunc + + call ChangeYourMind() endfunc "------------------------------------------------------------------------------- -- cgit From e6e9ffb3456607747132e398039ea8de16553a52 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:7.4.1228 Problem: copy() and deepcopy() fail with special variables. (Nikolai Pavlov) Solution: Make it work. Add a test. Closes vim/vim#614. https://github.com/vim/vim/commit/155500077c80cdb5d9c63996000c011b66a676bf Code is N/A. This only ports the tests. Comment out tests involving v:none as Nvim has removed it. --- src/nvim/testdir/test_vimscript.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 0d9aa647fd..a8c0c941c3 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1208,6 +1208,16 @@ func Test_type() " call assert_equal(v:none, eval(string(v:none))) call assert_equal(v:null, eval(string(v:null))) + call assert_equal(v:false, copy(v:false)) + call assert_equal(v:true, copy(v:true)) + " call assert_equal(v:none, copy(v:none)) + call assert_equal(v:null, copy(v:null)) + + call assert_equal([v:false], deepcopy([v:false])) + call assert_equal([v:true], deepcopy([v:true])) + " call assert_equal([v:none], deepcopy([v:none])) + call assert_equal([v:null], deepcopy([v:null])) + call assert_true(empty(v:false)) call assert_false(empty(v:true)) call assert_true(empty(v:null)) -- cgit From fe621b4ac01c6865650794b8e986665be4fe0ae7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 05:34:20 +0800 Subject: vim-patch:8.1.0711: test files still use function! Problem: Test files still use function!. Solution: Remove the exclamation mark. Fix overwriting a function. https://github.com/vim/vim/commit/1e1153600c0377472d62cc553173fe555ddcf5a7 Some of the changes were already applied previously. --- src/nvim/testdir/test_autocmd.vim | 2 +- src/nvim/testdir/test_charsearch_utf8.vim | 2 +- src/nvim/testdir/test_display.vim | 2 +- src/nvim/testdir/test_edit.vim | 8 +- src/nvim/testdir/test_ins_complete.vim | 4 +- src/nvim/testdir/test_lambda.vim | 158 ++++++++++++------------ src/nvim/testdir/test_listdict.vim | 10 +- src/nvim/testdir/test_marks.vim | 8 +- src/nvim/testdir/test_matchadd_conceal.vim | 4 +- src/nvim/testdir/test_matchadd_conceal_utf8.vim | 10 +- src/nvim/testdir/test_messages.vim | 4 +- src/nvim/testdir/test_options.vim | 14 +-- src/nvim/testdir/test_substitute.vim | 2 +- src/nvim/testdir/test_system.vim | 4 +- src/nvim/testdir/test_utf8_comparisons.vim | 6 +- src/nvim/testdir/test_vartabs.vim | 2 +- src/nvim/testdir/test_vimscript.vim | 12 +- src/nvim/testdir/test_window_cmd.vim | 4 +- 18 files changed, 128 insertions(+), 128 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 433410248b..a8d51ef598 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -4,7 +4,7 @@ source shared.vim source check.vim source term_util.vim -func! s:cleanup_buffers() abort +func s:cleanup_buffers() abort for bnr in range(1, bufnr('$')) if bufloaded(bnr) && bufnr('%') != bnr execute 'bd! ' . bnr diff --git a/src/nvim/testdir/test_charsearch_utf8.vim b/src/nvim/testdir/test_charsearch_utf8.vim index 09341a90b0..82a807ac5b 100644 --- a/src/nvim/testdir/test_charsearch_utf8.vim +++ b/src/nvim/testdir/test_charsearch_utf8.vim @@ -1,7 +1,7 @@ " Tests for related f{char} and t{char} using utf-8. " Test for t,f,F,T movement commands -function! Test_search_cmds() +func Test_search_cmds() new! call setline(1, "・最åˆã‹ã‚‰æœ€å¾Œã¾ã§æœ€å¼·ã®Vimã¯æœ€é«˜") 1 diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index c2a9683f7c..9f74d0a38a 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -41,7 +41,7 @@ func Test_display_foldcolumn() quit! endfunc -func! Test_display_foldtext_mbyte() +func Test_display_foldtext_mbyte() CheckFeature folding call NewWindow(10, 40) diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index 17393d6edb..a1f6a84a99 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -213,7 +213,7 @@ func Test_edit_07() bw! endfunc -func! Test_edit_08() +func Test_edit_08() throw 'skipped: moved to test/functional/legacy/edit_spec.lua' " reset insertmode from i_ctrl-r_= let g:bufnr = bufnr('%') @@ -417,7 +417,7 @@ func Test_edit_13() bwipe! endfunc -func! Test_edit_CR() +func Test_edit_CR() " Test for in insert mode " basically only in quickfix mode ist tested, the rest " has been taken care of by other tests @@ -450,7 +450,7 @@ func! Test_edit_CR() call delete('Xqflist.txt') endfunc -func! Test_edit_CTRL_() +func Test_edit_CTRL_() " disabled for Windows builds, why? if !has("rightleft") || has("win32") return @@ -734,7 +734,7 @@ func Test_edit_CTRL_O() bw! endfunc -func! Test_edit_CTRL_R() +func Test_edit_CTRL_R() " Insert Register new " call test_override("ALL", 1) diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index f066d842b4..186fa8871f 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -109,7 +109,7 @@ func s:CompleteDone_CompleteFuncNone( findstart, base ) return v:none endfunc -function! s:CompleteDone_CompleteFuncDict( findstart, base ) +func s:CompleteDone_CompleteFuncDict( findstart, base ) if a:findstart return 0 endif @@ -126,7 +126,7 @@ function! s:CompleteDone_CompleteFuncDict( findstart, base ) \ } \ ] \ } -endfunction +endfunc func s:CompleteDone_CheckCompletedItemNone() let s:called_completedone = 1 diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 72ddbcf6dc..c1fe47d1c9 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -1,24 +1,24 @@ " Test for lambda and closure -function! Test_lambda_feature() +func Test_lambda_feature() call assert_equal(1, has('lambda')) -endfunction +endfunc -function! Test_lambda_with_filter() +func Test_lambda_with_filter() let s:x = 2 call assert_equal([2, 3], filter([1, 2, 3], {i, v -> v >= s:x})) -endfunction +endfunc -function! Test_lambda_with_map() +func Test_lambda_with_map() let s:x = 1 call assert_equal([2, 3, 4], map([1, 2, 3], {i, v -> v + s:x})) -endfunction +endfunc -function! Test_lambda_with_sort() +func Test_lambda_with_sort() call assert_equal([1, 2, 3, 4, 7], sort([3,7,2,1,4], {a, b -> a - b})) -endfunction +endfunc -function! Test_lambda_with_timer() +func Test_lambda_with_timer() if !has('timers') return endif @@ -54,10 +54,10 @@ function! Test_lambda_with_timer() call assert_true(s:n > m) endfunc -function! Test_lambda_with_partial() +func Test_lambda_with_partial() let l:Cb = function({... -> ['zero', a:1, a:2, a:3]}, ['one', 'two']) call assert_equal(['zero', 'one', 'two', 'three'], l:Cb('three')) -endfunction +endfunc function Test_lambda_fails() call assert_equal(3, {a, b -> a + b}(1, 2)) @@ -70,59 +70,59 @@ func Test_not_lambda() call assert_equal('foo', x['>']) endfunc -function! Test_lambda_capture_by_reference() +func Test_lambda_capture_by_reference() let v = 1 let l:F = {x -> x + v} let v = 2 call assert_equal(12, l:F(10)) -endfunction +endfunc -function! Test_lambda_side_effect() - function! s:update_and_return(arr) +func Test_lambda_side_effect() + func! s:update_and_return(arr) let a:arr[1] = 5 return a:arr - endfunction + endfunc - function! s:foo(arr) + func! s:foo(arr) return {-> s:update_and_return(a:arr)} - endfunction + endfunc let arr = [3,2,1] call assert_equal([3, 5, 1], s:foo(arr)()) -endfunction +endfunc -function! Test_lambda_refer_local_variable_from_other_scope() - function! s:foo(X) +func Test_lambda_refer_local_variable_from_other_scope() + func! s:foo(X) return a:X() " refer l:x in s:bar() - endfunction + endfunc - function! s:bar() + func! s:bar() let x = 123 return s:foo({-> x}) - endfunction + endfunc call assert_equal(123, s:bar()) -endfunction +endfunc -function! Test_lambda_do_not_share_local_variable() - function! s:define_funcs() +func Test_lambda_do_not_share_local_variable() + func! s:define_funcs() let l:One = {-> split(execute("let a = 'abc' | echo a"))[0]} let l:Two = {-> exists("a") ? a : "no"} return [l:One, l:Two] - endfunction + endfunc let l:F = s:define_funcs() call assert_equal('no', l:F[1]()) call assert_equal('abc', l:F[0]()) call assert_equal('no', l:F[1]()) -endfunction +endfunc -function! Test_lambda_closure_counter() - function! s:foo() +func Test_lambda_closure_counter() + func! s:foo() let x = 0 return {-> [execute("let x += 1"), x][-1]} - endfunction + endfunc let l:F = s:foo() call garbagecollect() @@ -130,52 +130,52 @@ function! Test_lambda_closure_counter() call assert_equal(2, l:F()) call assert_equal(3, l:F()) call assert_equal(4, l:F()) -endfunction +endfunc -function! Test_lambda_with_a_var() - function! s:foo() +func Test_lambda_with_a_var() + func! s:foo() let x = 2 return {... -> a:000 + [x]} - endfunction - function! s:bar() + endfunc + func! s:bar() return s:foo()(1) - endfunction + endfunc call assert_equal([1, 2], s:bar()) -endfunction +endfunc -function! Test_lambda_call_lambda_from_lambda() - function! s:foo(x) +func Test_lambda_call_lambda_from_lambda() + func! s:foo(x) let l:F1 = {-> {-> a:x}} return {-> l:F1()} - endfunction + endfunc let l:F = s:foo(1) call assert_equal(1, l:F()()) -endfunction +endfunc -function! Test_lambda_delfunc() - function! s:gen() +func Test_lambda_delfunc() + func! s:gen() let pl = l: let l:Foo = {-> get(pl, "Foo", get(pl, "Bar", {-> 0}))} let l:Bar = l:Foo delfunction l:Foo return l:Bar - endfunction + endfunc let l:F = s:gen() call assert_fails(':call l:F()', 'E933:') -endfunction +endfunc -function! Test_lambda_scope() - function! s:NewCounter() +func Test_lambda_scope() + func! s:NewCounter() let c = 0 return {-> [execute('let c += 1'), c][-1]} - endfunction + endfunc - function! s:NewCounter2() + func! s:NewCounter2() return {-> [execute('let c += 100'), c][-1]} - endfunction + endfunc let l:C = s:NewCounter() let l:D = s:NewCounter2() @@ -183,37 +183,37 @@ function! Test_lambda_scope() call assert_equal(1, l:C()) call assert_fails(':call l:D()', 'E121:') call assert_equal(2, l:C()) -endfunction +endfunc -function! Test_lambda_share_scope() - function! s:New() +func Test_lambda_share_scope() + func! s:New() let c = 0 let l:Inc0 = {-> [execute('let c += 1'), c][-1]} let l:Dec0 = {-> [execute('let c -= 1'), c][-1]} return [l:Inc0, l:Dec0] - endfunction + endfunc let [l:Inc, l:Dec] = s:New() call assert_equal(1, l:Inc()) call assert_equal(2, l:Inc()) call assert_equal(1, l:Dec()) -endfunction +endfunc -function! Test_lambda_circular_reference() - function! s:Foo() +func Test_lambda_circular_reference() + func! s:Foo() let d = {} let d.f = {-> d} return d.f - endfunction + endfunc call s:Foo() call garbagecollect() let i = 0 | while i < 10000 | call s:Foo() | let i+= 1 | endwhile call garbagecollect() -endfunction +endfunc -function! Test_lambda_combination() +func Test_lambda_combination() call assert_equal(2, {x -> {x -> x}}(1)(2)) call assert_equal(10, {y -> {x -> x(y)(10)}({y -> y})}({z -> z})) call assert_equal(5.0, {x -> {y -> x / y}}(10)(2.0)) @@ -226,17 +226,17 @@ function! Test_lambda_combination() let Z = {f -> {x -> f({y -> x(x)(y)})}({x -> f({y -> x(x)(y)})})} let Fact = {f -> {x -> x == 0 ? 1 : x * f(x - 1)}} call assert_equal(120, Z(Fact)(5)) -endfunction +endfunc -function! Test_closure_counter() - function! s:foo() +func Test_closure_counter() + func! s:foo() let x = 0 - function! s:bar() closure + func! s:bar() closure let x += 1 return x - endfunction + endfunc return function('s:bar') - endfunction + endfunc let l:F = s:foo() call garbagecollect() @@ -244,30 +244,30 @@ function! Test_closure_counter() call assert_equal(2, l:F()) call assert_equal(3, l:F()) call assert_equal(4, l:F()) -endfunction +endfunc -function! Test_closure_unlet() - function! s:foo() +func Test_closure_unlet() + func! s:foo() let x = 1 - function! s:bar() closure + func! s:bar() closure unlet x - endfunction + endfunc call s:bar() return l: - endfunction + endfunc call assert_false(has_key(s:foo(), 'x')) call garbagecollect() -endfunction +endfunc -function! LambdaFoo() +func LambdaFoo() let x = 0 - function! LambdaBar() closure + func! LambdaBar() closure let x += 1 return x - endfunction + endfunc return function('LambdaBar') -endfunction +endfunc func Test_closure_refcount() let g:Count = LambdaFoo() diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index f6c404d390..10c6164c7c 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -516,22 +516,22 @@ func Test_dict_lock_operator() endfunc " No remove() of write-protected scope-level variable -func! Tfunc(this_is_a_long_parameter_name) +func Tfunc1(this_is_a_long_parameter_name) call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742') -endfun +endfunc func Test_dict_scope_var_remove() - call Tfunc('testval') + call Tfunc1('testval') endfunc " No extend() of write-protected scope-level variable func Test_dict_scope_var_extend() call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') endfunc -func! Tfunc(this_is_a_long_parameter_name) +func Tfunc2(this_is_a_long_parameter_name) call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') endfunc func Test_dict_scope_var_extend_overwrite() - call Tfunc('testval') + call Tfunc2('testval') endfunc " No :unlet of variable in locked scope diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index 6b9904ec0a..00ee8f6d6a 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -1,6 +1,6 @@ " Test that a deleted mark is restored after delete-undo-redo-undo. -function! Test_Restore_DelMark() +func Test_Restore_DelMark() enew! call append(0, [" textline A", " textline B", " textline C"]) normal! 2gg @@ -11,10 +11,10 @@ function! Test_Restore_DelMark() call assert_equal(2, pos[1]) call assert_equal(1, pos[2]) enew! -endfunction +endfunc " Test that CTRL-A and CTRL-X updates last changed mark '[, ']. -function! Test_Incr_Marks() +func Test_Incr_Marks() enew! call append(0, ["123 123 123", "123 123 123", "123 123 123"]) normal! gg @@ -23,7 +23,7 @@ function! Test_Incr_Marks() call assert_equal("123 XXXXXXX", getline(2)) call assert_equal("XXX 123 123", getline(3)) enew! -endfunction +endfunc func Test_previous_jump_mark() new diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index 2cbaf5cb76..29a2c30b0d 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -7,7 +7,7 @@ source shared.vim source term_util.vim source view_util.vim -function! Test_simple_matchadd() +func Test_simple_matchadd() new 1put='# This is a Test' @@ -333,7 +333,7 @@ func Test_matchadd_and_syn_conceal() call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) call assert_equal(screenattr(1, 11) , screenattr(1, 32)) -endfunction +endfunc func Test_cursor_column_in_concealed_line_after_window_scroll() CheckRunVimInTerminal diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 7bfac13ad8..1d0c740734 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -3,19 +3,19 @@ if !has('conceal') finish endif -function! s:screenline(lnum) abort +func s:screenline(lnum) abort let line = [] for c in range(1, winwidth(0)) call add(line, nr2char(a:lnum->screenchar(c))) endfor return s:trim(join(line, '')) -endfunction +endfunc -function! s:trim(str) abort +func s:trim(str) abort return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') -endfunction +endfunc -function! Test_match_using_multibyte_conceal_char() +func Test_match_using_multibyte_conceal_char() new setlocal concealcursor=n conceallevel=1 diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index e0286548d9..9c84d77dd2 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -40,7 +40,7 @@ endfunc " indicator (e.g., "-- INSERT --") when ":stopinsert" is invoked. Message " output could then be disturbed when 'cmdheight' was greater than one. " This test ensures that the bugfix for this issue remains in place. -function! Test_stopinsert_does_not_break_message_output() +func Test_stopinsert_does_not_break_message_output() set cmdheight=2 redraw! @@ -55,7 +55,7 @@ function! Test_stopinsert_does_not_break_message_output() redraw! set cmdheight& -endfunction +endfunc func Test_message_completion() call feedkeys(":message \\\"\", 'tx') diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index f7bfa48943..a5adb5ff16 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -22,16 +22,16 @@ func Test_whichwrap() call assert_equal('h', &whichwrap) set whichwrap& -endfunction +endfunc -function! Test_isfname() +func Test_isfname() " This used to cause Vim to access uninitialized memory. set isfname= call assert_equal("~X", expand("~X")) set isfname& -endfunction +endfunc -function Test_wildchar() +func Test_wildchar() " Empty 'wildchar' used to access invalid memory. call assert_fails('set wildchar=', 'E521:') call assert_fails('set wildchar=abc', 'E521:') @@ -42,7 +42,7 @@ function Test_wildchar() let a=execute('set wildchar?') call assert_equal("\n wildchar=", a) set wildchar& -endfunction +endfunc func Test_wildoptions() set wildoptions= @@ -90,7 +90,7 @@ func Test_options_command() close endfunc -function! Test_path_keep_commas() +func Test_path_keep_commas() " Test that changing 'path' keeps two commas. set path=foo,,bar set path-=bar @@ -98,7 +98,7 @@ function! Test_path_keep_commas() call assert_equal('foo,,bar', &path) set path& -endfunction +endfunc func Test_filetype_valid() set ft=valid_name diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index 20b760ac15..ecd980472a 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -105,7 +105,7 @@ func Test_substitute_variants() call assert_equal(var.exp, getline('.'), msg) endfor endfor -endfunction +endfunc " Test the l, p, # flags. func Test_substitute_flags_lp() diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 1858b48807..5b8079d7b6 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -46,9 +46,9 @@ func Test_System() bwipe! call assert_fails('call system("wc -l", 99999)', 'E86:') -endfunction +endfunc -function! Test_system_exmode() +func Test_system_exmode() if has('unix') " echo $? only works on Unix let cmd = ' -es --headless -u NONE -c "source Xscript" +q; echo "result=$?"' " Need to put this in a script, "catch" isn't found after an unknown diff --git a/src/nvim/testdir/test_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim index 62947c6e6e..f3c86b44fb 100644 --- a/src/nvim/testdir/test_utf8_comparisons.vim +++ b/src/nvim/testdir/test_utf8_comparisons.vim @@ -1,12 +1,12 @@ " Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) " Also test "g~ap". -function! Ch(a, op, b, expected) +func Ch(a, op, b, expected) call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected, \ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected)) -endfunction +endfunc -function! Chk(a, b, result) +func Chk(a, b, result) if a:result == 0 call Ch(a:a, '==?', a:b, 1) call Ch(a:a, '!=?', a:b, 0) diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index d05008d2dd..017bb6675d 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -145,7 +145,7 @@ func Test_retab_invalid_arg() bwipe! endfunc -func! Test_vartabs_breakindent() +func Test_vartabs_breakindent() if !exists("+breakindent") return endif diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index a8c0c941c3..75a965f16d 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -25,7 +25,7 @@ com! -nargs=1 Xout call Xout() " in the variable argument list. This function is useful if similar tests are " to be made for a ":return" from a function call or a ":finish" in a script " file. -function! MakeScript(funcname, ...) +func MakeScript(funcname, ...) let script = tempname() execute "redir! >" . script execute "function" a:funcname @@ -1224,11 +1224,11 @@ func Test_type() " call assert_true(empty(v:none)) func ChangeYourMind() - try - return v:true - finally - return 'something else' - endtry + try + return v:true + finally + return 'something else' + endtry endfunc call ChangeYourMind() diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index db5c0b2a11..ef6dec580f 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -575,7 +575,7 @@ func Test_winrestcmd() only endfunc -function! Fun_RenewFile() +func Fun_RenewFile() " Need to wait a bit for the timestamp to be older. let old_ftime = getftime("tmp.txt") while getftime("tmp.txt") == old_ftime @@ -585,7 +585,7 @@ function! Fun_RenewFile() sp wincmd p edit! tmp.txt -endfunction +endfunc func Test_window_prevwin() " Can we make this work on MS-Windows? -- cgit From d457168e3b6078ae018a2b1fe59ff54f82d3ba14 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 06:48:10 +0800 Subject: vim-patch:8.2.0208: fnamemodify() does not apply ":~" when followed by ":." Problem: Fnamemodify() does not apply ":~" when followed by ":.". Solution: Don't let a failing ":." cause the ":~" to be skipped. (Yasuhiro Matsumoto, closes vim/vim#5577) https://github.com/vim/vim/commit/d816cd94d87afb73c505bf1e5cd5e07522482113 --- src/nvim/eval.c | 28 +++++++++++++++++++++------- src/nvim/testdir/test_fnamemodify.vim | 12 ++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dccad5a2d0..70909b46cb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10624,12 +10624,13 @@ int modify_fname(char_u *src, bool tilde_file, size_t *usedlen, char_u **fnamep, char_u *s, *p, *pbuf; char_u dirname[MAXPATHL]; int c; - int has_fullname = 0; + bool has_fullname = false; + bool has_homerelative = false; repeat: // ":p" - full path/file_name if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { - has_fullname = 1; + has_fullname = true; valid |= VALID_PATH; *usedlen += 2; @@ -10698,7 +10699,7 @@ repeat: } pbuf = NULL; // Need full path first (use expand_env() to remove a "~/") - if (!has_fullname) { + if (!has_fullname && !has_homerelative) { if (c == '.' && **fnamep == '~') { p = pbuf = expand_env_save(*fnamep); } else { @@ -10708,14 +10709,26 @@ repeat: p = *fnamep; } - has_fullname = 0; + has_fullname = false; if (p != NULL) { if (c == '.') { os_dirname(dirname, MAXPATHL); - s = path_shorten_fname(p, dirname); - if (s != NULL) { - *fnamep = s; + if (has_homerelative) { + s = vim_strsave(dirname); + home_replace(NULL, s, dirname, MAXPATHL, true); + xfree(s); + } + size_t namelen = STRLEN(dirname); + + // Do not call shorten_fname() here since it removes the prefix + // even though the path does not have a prefix. + if (fnamencmp(p, dirname, namelen) == 0) { + p += namelen; + while (*p && vim_ispathsep(*p)) { + ++p; + } + *fnamep = p; if (pbuf != NULL) { xfree(*bufp); // free any allocated file name *bufp = pbuf; @@ -10730,6 +10743,7 @@ repeat: *fnamep = s; xfree(*bufp); *bufp = s; + has_homerelative = true; } } xfree(pbuf); diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index 411f7ebbb3..e8fad397c3 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -3,8 +3,10 @@ func Test_fnamemodify() let save_home = $HOME let save_shell = &shell + let save_shellslash = &shellslash let $HOME = fnamemodify('.', ':p:h:h') set shell=sh + set shellslash call assert_equal('/', fnamemodify('.', ':p')[-1:]) call assert_equal('r', fnamemodify('.', ':p:h')[-1:]) @@ -28,6 +30,15 @@ func Test_fnamemodify() call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + let cwd = getcwd() + call mkdir($HOME . '/XXXXXXXX/a', 'p') + call mkdir($HOME . '/XXXXXXXX/b', 'p') + call chdir($HOME . '/XXXXXXXX/a/') + call assert_equal('foo', fnamemodify($HOME . '/XXXXXXXX/a/foo', ':p:~:.')) + call assert_equal('~/XXXXXXXX/b/foo', fnamemodify($HOME . '/XXXXXXXX/b/foo', ':p:~:.')) + call chdir(cwd) + call delete($HOME . '/XXXXXXXX', 'rf') + call assert_equal('''abc def''', fnamemodify('abc def', ':S')) call assert_equal('''abc" "def''', fnamemodify('abc" "def', ':S')) call assert_equal('''abc"%"def''', fnamemodify('abc"%"def', ':S')) @@ -44,6 +55,7 @@ func Test_fnamemodify() let $HOME = save_home let &shell = save_shell + let &shellslash = save_shellslash endfunc func Test_fnamemodify_er() -- cgit From f47ba10636b498430fc8d2d490e5bdf6b4e01033 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 06:48:10 +0800 Subject: vim-patch:8.2.0215: wrong file name shortening Problem: Wrong file name shortening. (Ingo Karkat) Solution: Better check for path separator. (Yasuhiro Matsumoto, closes vim/vim#5583, closes vim/vim#5584) https://github.com/vim/vim/commit/a78e9c61a0ded9c5302bc77e889aa1b3d3467f61 --- src/nvim/eval.c | 19 +++++++++++-------- src/nvim/testdir/test_fnamemodify.vim | 2 ++ 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 70909b46cb..0fe928beb7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10725,14 +10725,17 @@ repeat: // even though the path does not have a prefix. if (fnamencmp(p, dirname, namelen) == 0) { p += namelen; - while (*p && vim_ispathsep(*p)) { - ++p; - } - *fnamep = p; - if (pbuf != NULL) { - xfree(*bufp); // free any allocated file name - *bufp = pbuf; - pbuf = NULL; + if (vim_ispathsep(*p)) { + while (*p && vim_ispathsep(*p)) { + p++; + } + *fnamep = p; + if (pbuf != NULL) { + // free any allocated file name + xfree(*bufp); + *bufp = pbuf; + pbuf = NULL; + } } } } else { diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index e8fad397c3..ca17be41ec 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -36,6 +36,8 @@ func Test_fnamemodify() call chdir($HOME . '/XXXXXXXX/a/') call assert_equal('foo', fnamemodify($HOME . '/XXXXXXXX/a/foo', ':p:~:.')) call assert_equal('~/XXXXXXXX/b/foo', fnamemodify($HOME . '/XXXXXXXX/b/foo', ':p:~:.')) + call mkdir($HOME . '/XXXXXXXX/a.ext', 'p') + call assert_equal('~/XXXXXXXX/a.ext/foo', fnamemodify($HOME . '/XXXXXXXX/a.ext/foo', ':p:~:.')) call chdir(cwd) call delete($HOME . '/XXXXXXXX', 'rf') -- cgit From 53e4434c722d94f9c49dee2fd787d05d36a46bf3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 06:48:10 +0800 Subject: vim-patch:8.2.0942: expanding to local dir after homedir keeps "~/" Problem: Expanding to local dir after homedir keeps "~/". Solution: Adjust modify_fname(). (Christian Brabandt, closes vim/vim#6205, closes vim/vim#5979) https://github.com/vim/vim/commit/0e390f40e944036fb558a63b91238cfda128d95f --- src/nvim/eval.c | 2 +- src/nvim/os/env.c | 7 +++---- src/nvim/testdir/test_fnamemodify.vim | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0fe928beb7..926c385892 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10700,7 +10700,7 @@ repeat: pbuf = NULL; // Need full path first (use expand_env() to remove a "~/") if (!has_fullname && !has_homerelative) { - if (c == '.' && **fnamep == '~') { + if ((c == '.' || c == '~') && **fnamep == '~') { p = pbuf = expand_env_save(*fnamep); } else { p = pbuf = (char_u *)FullName_save((char *)*fnamep, FALSE); diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index e9f44d2775..e9868d6b61 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -1111,10 +1111,9 @@ size_t home_replace(const buf_T *const buf, const char_u *src, char_u *const dst *dst_p++ = '~'; } - // If it's just the home directory, add "/". - if (!vim_ispathsep(src[0]) && --dstlen > 0) { - *dst_p++ = '/'; - } + // Do not add directory separator into dst, because dst is + // expected to just return the directory name without the + // directory separator '/'. break; } if (p == homedir_env_mod) { diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index ca17be41ec..a8d513eede 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -31,6 +31,9 @@ func Test_fnamemodify() call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) let cwd = getcwd() + call chdir($HOME) + call assert_equal('foobar', fnamemodify('~/foobar', ':~:.')) + call chdir(cwd) call mkdir($HOME . '/XXXXXXXX/a', 'p') call mkdir($HOME . '/XXXXXXXX/b', 'p') call chdir($HOME . '/XXXXXXXX/a/') -- cgit From bfc11e9c64f7da952fb3f6c0ee0f62d80ce4394a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 06:48:10 +0800 Subject: vim-patch:8.2.3824: no ASAN support for MSVC Problem: No ASAN support for MSVC. Solution: Add ASAN support and fix a coupld of uncovered problems. (Yegappan Lakshmanan, closes vim/vim#9357) https://github.com/vim/vim/commit/6df0f2759d12ec0bc999b2963ecea4387a2bc516 --- src/nvim/path.c | 8 +++++--- src/nvim/testdir/test_fnamemodify.vim | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/path.c b/src/nvim/path.c index fe564182d8..54170e7a8b 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1521,7 +1521,7 @@ void simplify_filename(char_u *filename) p = filename; #ifdef BACKSLASH_IN_FILENAME - if (p[1] == ':') { // skip "x:" + if (p[0] != NUL && p[1] == ':') { // skip "x:" p += 2; } #endif @@ -2402,9 +2402,11 @@ static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int fo int path_is_absolute(const char_u *fname) { #ifdef WIN32 + if (*fname == NUL) { + return true; + } // A name like "d:/foo" and "//server/share" is absolute - return ((isalpha(fname[0]) && fname[1] == ':' - && vim_ispathsep_nocolon(fname[2])) + return ((isalpha(fname[0]) && fname[1] == ':' && vim_ispathsep_nocolon(fname[2])) || (vim_ispathsep_nocolon(fname[0]) && fname[0] == fname[1])); #else // UNIX: This just checks if the file name starts with '/' or '~'. diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index a8d513eede..73ede415ae 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -90,6 +90,7 @@ func Test_fnamemodify_er() call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e')) call assert_equal('b.c', fnamemodify('a.b.c.d.e', ':r:r:e:e:e:e')) + call assert_equal('', fnamemodify('', ':p:t')) call assert_equal('', fnamemodify(v:_null_string, v:_null_string)) endfunc -- cgit From 72816136a57adbd051c688ee3213fa9446ea48f6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 06:48:10 +0800 Subject: vim-patch:8.2.3939: MS-Windows: fnamemodify('', ':p') does not work Problem: MS-Windows: fnamemodify('', ':p') does not work. Solution: Do not consider an empty string a full path. (Yegappan Lakshmanan, closes vim/vim#9428, closes vim/vim#9427) https://github.com/vim/vim/commit/5a664fe57fe7ba65a771bc95ef1c205e4db193b7 --- src/nvim/path.c | 2 +- src/nvim/testdir/test_fnamemodify.vim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/path.c b/src/nvim/path.c index 54170e7a8b..7f7f941e26 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -2403,7 +2403,7 @@ int path_is_absolute(const char_u *fname) { #ifdef WIN32 if (*fname == NUL) { - return true; + return false; } // A name like "d:/foo" and "//server/share" is absolute return ((isalpha(fname[0]) && fname[1] == ':' && vim_ispathsep_nocolon(fname[2])) diff --git a/src/nvim/testdir/test_fnamemodify.vim b/src/nvim/testdir/test_fnamemodify.vim index 73ede415ae..5ae2a5ee17 100644 --- a/src/nvim/testdir/test_fnamemodify.vim +++ b/src/nvim/testdir/test_fnamemodify.vim @@ -29,6 +29,7 @@ func Test_fnamemodify() call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e')) call assert_equal('fb2.tar.gz', fnamemodify('abc.fb2.tar.gz', ':e:e:e:e')) call assert_equal('tar', fnamemodify('abc.fb2.tar.gz', ':e:e:r')) + call assert_equal(getcwd(), fnamemodify('', ':p:h')) let cwd = getcwd() call chdir($HOME) -- cgit From f02a5a7bdaafc1c5ff61aee133eb2b6ba5f57586 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 7 Feb 2022 01:51:09 +0000 Subject: chore(typval): return NULL over false for pointer return type (#17316) While we're at it, abort() for an unhandled v_type. --- src/nvim/eval/typval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index e583ce49b2..6f8b032d41 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3205,8 +3205,9 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_BLOB: case VAR_UNKNOWN: emsg(_(str_errors[tv->v_type])); - return false; + return NULL; } + abort(); return NULL; } -- cgit From fba00b5e7ef2b6903a4588a2c080d8b33a8a2b68 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 14:58:32 +0000 Subject: vim-patch:8.2.1665: cannot do fuzzy string matching Problem: Cannot do fuzzy string matching. Solution: Add matchfuzzy(). (Yegappan Lakshmanan, closes vim/vim#6932) https://github.com/vim/vim/commit/635414dd2f3ae7d4d972d79b806348a6516cb91a Adjust Test_matchfuzzy's 2nd assert to expect the last error thrown, as v8.2.1183 hasn't been ported yet (to be honest, the error message is kinda weird if the 2nd argument is not convertible to string). We can still port this fully as porting v8.2.1183 would require removing this change to pass CI. --- src/nvim/eval.lua | 1 + src/nvim/search.c | 298 ++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_functions.vim | 26 ++++ 3 files changed, 325 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index eedc8ac45d..37b051222e 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -249,6 +249,7 @@ return { matcharg={args=1, base=1}, matchdelete={args={1, 2}, base=1}, matchend={args={2, 4}, base=1}, + matchfuzzy={args=2, base=1}, matchlist={args={2, 4}, base=1}, matchstr={args={2, 4}, base=1}, matchstrpos={args={2,4}, base=1}, diff --git a/src/nvim/search.c b/src/nvim/search.c index 93180f97fe..7bc994fad3 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4764,6 +4764,304 @@ the_end: restore_last_search_pattern(); } +/// Fuzzy string matching +/// +/// Ported from the lib_fts library authored by Forrest Smith. +/// https://github.com/forrestthewoods/lib_fts/tree/master/code +/// +/// Blog describing the algorithm: +/// https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/ +/// +/// Each matching string is assigned a score. The following factors are checked: +/// Matched letter +/// Unmatched letter +/// Consecutively matched letters +/// Proximity to start +/// Letter following a separator (space, underscore) +/// Uppercase letter following lowercase (aka CamelCase) +/// +/// Matched letters are good. Unmatched letters are bad. Matching near the start +/// is good. Matching the first letter in the middle of a phrase is good. +/// Matching the uppercase letters in camel case entries is good. +/// +/// The score assigned for each factor is explained below. +/// File paths are different from file names. File extensions may be ignorable. +/// Single words care about consecutive matches but not separators or camel +/// case. +/// Score starts at 0 +/// Matched letter: +0 points +/// Unmatched letter: -1 point +/// Consecutive match bonus: +5 points +/// Separator bonus: +10 points +/// Camel case bonus: +10 points +/// Unmatched leading letter: -3 points (max: -9) +/// +/// There is some nuance to this. Scores don’t have an intrinsic meaning. The +/// score range isn’t 0 to 100. It’s roughly [-50, 50]. Longer words have a +/// lower minimum score due to unmatched letter penalty. Longer search patterns +/// have a higher maximum score due to match bonuses. +/// +/// Separator and camel case bonus is worth a LOT. Consecutive matches are worth +/// quite a bit. +/// +/// There is a penalty if you DON’T match the first three letters. Which +/// effectively rewards matching near the start. However there’s no difference +/// in matching between the middle and end. +/// +/// There is not an explicit bonus for an exact match. Unmatched letters receive +/// a penalty. So shorter strings and closer matches are worth more. +typedef struct { + const listitem_T *item; + int score; +} fuzzyItem_T; + +static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, int *const outScore, + const char_u *const strBegin, const char_u *const srcMatches, + char_u *const matches, const int maxMatches, int nextMatch, + int *const recursionCount, const int recursionLimit) + FUNC_ATTR_NONNULL_ARG(1, 2, 3, 4, 6, 9) FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Recursion params + bool recursiveMatch = false; + char_u bestRecursiveMatches[256]; + int bestRecursiveScore = 0; + + // Count recursions + (*recursionCount)++; + if (*recursionCount >= recursionLimit) { + return false; + } + + // Detect end of strings + if (*fuzpat == '\0' || *str == '\0') { + return false; + } + + // Loop through fuzpat and str looking for a match + bool first_match = true; + while (*fuzpat != '\0' && *str != '\0') { + // Found match + if (mb_tolower(*fuzpat) == mb_tolower(*str)) { + // Supplied matches buffer was too short + if (nextMatch >= maxMatches) { + return false; + } + + // "Copy-on-Write" srcMatches into matches + if (first_match && srcMatches) { + memcpy(matches, srcMatches, nextMatch); + first_match = false; + } + + // Recursive call that "skips" this match + char_u recursiveMatches[256]; + int recursiveScore = 0; + if (fuzzy_match_recursive(fuzpat, str + 1, &recursiveScore, strBegin, matches, + recursiveMatches, sizeof(recursiveMatches), nextMatch, + recursionCount, recursionLimit)) { + // Pick best recursive score + if (!recursiveMatch || recursiveScore > bestRecursiveScore) { + memcpy(bestRecursiveMatches, recursiveMatches, 256); + bestRecursiveScore = recursiveScore; + } + recursiveMatch = true; + } + + // Advance + matches[nextMatch++] = (char_u)(str - strBegin); + fuzpat++; + } + str++; + } + + // Determine if full fuzpat was matched + const bool matched = *fuzpat == '\0'; + + // Calculate score + if (matched) { + // bonus for adjacent matches + const int sequential_bonus = 15; + // bonus if match occurs after a separator + const int separator_bonus = 30; + // bonus if match is uppercase and prev is lower + const int camel_bonus = 30; + // bonus if the first letter is matched + const int first_letter_bonus = 15; + // penalty applied for every letter in str before the first match + const int leading_letter_penalty = -5; + // maximum penalty for leading letters + const int max_leading_letter_penalty = -15; + // penalty for every letter that doesn't matter + const int unmatched_letter_penalty = -1; + + // Iterate str to end + while (*str != '\0') { + str++; + } + + // Initialize score + *outScore = 100; + + // Apply leading letter penalty + int penalty = leading_letter_penalty * matches[0]; + if (penalty < max_leading_letter_penalty) { + penalty = max_leading_letter_penalty; + } + *outScore += penalty; + + // Apply unmatched penalty + const int unmatched = (int)(str - strBegin) - nextMatch; + *outScore += unmatched_letter_penalty * unmatched; + + // Apply ordering bonuses + for (int i = 0; i < nextMatch; i++) { + const char_u currIdx = matches[i]; + + if (i > 0) { + const char_u prevIdx = matches[i - 1]; + + // Sequential + if (currIdx == (prevIdx + 1)) { + *outScore += sequential_bonus; + } + } + + // Check for bonuses based on neighbor character value + if (currIdx > 0) { + // Camel case + const char_u neighbor = strBegin[currIdx - 1]; + const char_u curr = strBegin[currIdx]; + + if (islower(neighbor) && isupper(curr)) { + *outScore += camel_bonus; + } + + // Separator + const bool neighborSeparator = neighbor == '_' || neighbor == ' '; + if (neighborSeparator) { + *outScore += separator_bonus; + } + } else { + // First letter + *outScore += first_letter_bonus; + } + } + } + + // Return best result + if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) { + // Recursive score is better than "this" + memcpy(matches, bestRecursiveMatches, maxMatches); + *outScore = bestRecursiveScore; + return true; + } else if (matched) { + return true; // "this" score is better than recursive + } + + return false; // no match +} + +/// fuzzy_match() +/// +/// Performs exhaustive search via recursion to find all possible matches and +/// match with highest score. +/// Scores values have no intrinsic meaning. Possible score range is not +/// normalized and varies with pattern. +/// Recursion is limited internally (default=10) to prevent degenerate cases +/// (fuzpat="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). +/// Uses char_u for match indices. Therefore patterns are limited to 256 +/// characters. +/// +/// Returns true if fuzpat is found AND calculates a score. +static bool fuzzy_match(const char_u *const str, const char_u *const fuzpat, int *const outScore) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + char_u matches[256]; + int recursionCount = 0; + int recursionLimit = 10; + + *outScore = 0; + + return fuzzy_match_recursive(fuzpat, str, outScore, str, NULL, matches, sizeof(matches), 0, + &recursionCount, recursionLimit); +} + +/// Sort the fuzzy matches in the descending order of the match score. +static int fuzzy_item_compare(const void *const s1, const void *const s2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + const int v1 = ((const fuzzyItem_T *)s1)->score; + const int v2 = ((const fuzzyItem_T *)s2)->score; + + return v1 == v2 ? 0 : v1 > v2 ? -1 : 1; +} + +/// Fuzzy search the string 'str' in 'strlist' and return the matching strings +/// in 'fmatchlist'. +static void match_fuzzy(const list_T *const strlist, const char_u *const str, + list_T *const fmatchlist) + FUNC_ATTR_NONNULL_ARG(2, 3) +{ + const long len = tv_list_len(strlist); + if (len == 0) { + return; + } + + fuzzyItem_T *const ptrs = xmalloc(sizeof(fuzzyItem_T) * len); + long i = 0; + bool found_match = false; + + // For all the string items in strlist, get the fuzzy matching score + TV_LIST_ITER_CONST(strlist, li, { + ptrs[i].item = li; + ptrs[i].score = -9999; + // ignore non-string items in the list + const typval_T *const tv = TV_LIST_ITEM_TV(li); + if (tv->v_type == VAR_STRING && tv->vval.v_string != NULL) { + int score; + if (fuzzy_match(tv->vval.v_string, str, &score)) { + ptrs[i].score = score; + found_match = true; + } + } + i++; + }); + + if (found_match) { + // Sort the list by the descending order of the match score + qsort(ptrs, (size_t)len, sizeof(fuzzyItem_T), fuzzy_item_compare); + + // Copy the matching strings with 'score != -9999' to the return list + for (i = 0; i < len; i++) { + if (ptrs[i].score == -9999) { + break; + } + const typval_T *const tv = TV_LIST_ITEM_TV(ptrs[i].item); + tv_list_append_string(fmatchlist, (const char *)tv->vval.v_string, -1); + } + } + + xfree(ptrs); +} + +/// "matchfuzzy()" function +void f_matchfuzzy(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + if (argvars[0].vval.v_list == NULL) { + return; + } + if (argvars[1].v_type != VAR_STRING || argvars[1].vval.v_string == NULL) { + semsg(_(e_invarg2), tv_get_string(&argvars[1])); + return; + } + match_fuzzy(argvars[0].vval.v_list, (const char_u *)tv_get_string(&argvars[1]), + tv_list_alloc_ret(rettv, kListLenUnknown)); +} + /// Find identifiers or defines in included files. /// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. /// diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 438bed51c6..57ae0d3020 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1731,6 +1731,32 @@ func Test_nr2char() call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\"')) endfunc +" Test for matchfuzzy() +func Test_matchfuzzy() + call assert_fails('call matchfuzzy(10, "abc")', 'E714:') + " Needs v8.2.1183. + " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:') + call assert_fails('call matchfuzzy(["abc"], [])', 'E475:') + call assert_equal([], matchfuzzy([], 'abc')) + call assert_equal([], matchfuzzy(['abc'], '')) + call assert_equal(['abc'], matchfuzzy(['abc', 10], 'ac')) + call assert_equal([], matchfuzzy([10, 20], 'ac')) + call assert_equal(['abc'], matchfuzzy(['abc'], 'abc')) + call assert_equal(['crayon', 'camera'], matchfuzzy(['camera', 'crayon'], 'cra')) + call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa')) + call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one')) + call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo')) + call assert_equal(['one_two', 'onetwo'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo')) + call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa')) + call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257))) + + %bw! + eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)}) + let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl') + call assert_equal(1, len(l)) + call assert_match('needle', l[0]) +endfunc + " Test for getcurpos() and setpos() func Test_getcurpos_setpos() new -- cgit From 960ea01972ad5fd291a846dce67f96a95222c310 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 16:40:28 +0000 Subject: vim-patch:8.2.1726: fuzzy matching only works on strings Problem: Fuzzy matching only works on strings. Solution: Support passing a dict. Add matchfuzzypos() to also get the match positions. (Yegappan Lakshmanan, closes vim/vim#6947) https://github.com/vim/vim/commit/4f73b8e9cc83f647b34002554a8bdf9abec0a82f Also remove some N/A and seemingly useless NULL checks -- Nvim allocs can't return NULL. I'm not sure why the retmatchpos stuff in match_fuzzy checks for NULL too, given that Vim checks for NULL alloc in do_fuzzymatch; assert that the li stuff is not NULL as that's the one check I'm ever-so-slightly unsure about. Adjust tests. Note that the text_cb tests actually throw E6000 in Nvim, but we also can't assert that error due to v8.2.1183 not being ported yet. --- src/nvim/eval.lua | 3 +- src/nvim/eval/funcs.c | 1 - src/nvim/globals.h | 1 + src/nvim/search.c | 400 ++++++++++++++++++++++++----------- src/nvim/testdir/test_functions.vim | 26 --- src/nvim/testdir/test_matchfuzzy.vim | 203 ++++++++++++++++++ 6 files changed, 479 insertions(+), 155 deletions(-) create mode 100644 src/nvim/testdir/test_matchfuzzy.vim (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 37b051222e..6dedd0f745 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -249,7 +249,8 @@ return { matcharg={args=1, base=1}, matchdelete={args={1, 2}, base=1}, matchend={args={2, 4}, base=1}, - matchfuzzy={args=2, base=1}, + matchfuzzy={args={2, 3}, base=1}, + matchfuzzypos={args={2, 3}, base=1}, matchlist={args={2, 4}, base=1}, matchstr={args={2, 4}, base=1}, matchstrpos={args={2,4}, base=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 138745094c..111fae0928 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -98,7 +98,6 @@ PRAGMA_DIAG_POP #endif -static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listblobarg = N_("E899: Argument of %s must be a List or Blob"); static char *e_invalwindow = N_("E957: Invalid window number"); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 041b60d838..f6fbe98ff0 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -979,6 +979,7 @@ EXTERN char e_invalidreg[] INIT(= N_("E850: Invalid register name")); EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window")); +EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List")); EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char e_float_as_string[] INIT(= N_("E806: using Float as a String")); diff --git a/src/nvim/search.c b/src/nvim/search.c index 7bc994fad3..8f3e66744f 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -48,6 +48,8 @@ #include "nvim/vim.h" #include "nvim/window.h" +typedef uint32_t matchidx_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "search.c.generated.h" #endif @@ -4811,24 +4813,109 @@ the_end: /// There is not an explicit bonus for an exact match. Unmatched letters receive /// a penalty. So shorter strings and closer matches are worth more. typedef struct { - const listitem_T *item; + listitem_T *item; int score; + list_T *lmatchpos; } fuzzyItem_T; -static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, int *const outScore, - const char_u *const strBegin, const char_u *const srcMatches, - char_u *const matches, const int maxMatches, int nextMatch, - int *const recursionCount, const int recursionLimit) - FUNC_ATTR_NONNULL_ARG(1, 2, 3, 4, 6, 9) FUNC_ATTR_WARN_UNUSED_RESULT +/// bonus for adjacent matches +#define SEQUENTIAL_BONUS 15 +/// bonus if match occurs after a separator +#define SEPARATOR_BONUS 30 +/// bonus if match is uppercase and prev is lower +#define CAMEL_BONUS 30 +/// bonus if the first letter is matched +#define FIRST_LETTER_BONUS 15 +/// penalty applied for every letter in str before the first match +#define LEADING_LETTER_PENALTY -5 +/// maximum penalty for leading letters +#define MAX_LEADING_LETTER_PENALTY -15 +/// penalty for every letter that doesn't match +#define UNMATCHED_LETTER_PENALTY -1 +/// Score for a string that doesn't fuzzy match the pattern +#define SCORE_NONE -9999 + +#define FUZZY_MATCH_RECURSION_LIMIT 10 +/// Maximum number of characters that can be fuzzy matched +#define MAXMATCHES 256 + +/// Compute a score for a fuzzy matched string. The matching character locations +/// are in 'matches'. +static int fuzzy_match_compute_score(const char_u *const str, const int strSz, + const matchidx_T *const matches, const int numMatches) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + // Initialize score + int score = 100; + + // Apply leading letter penalty + int penalty = LEADING_LETTER_PENALTY * matches[0]; + if (penalty < MAX_LEADING_LETTER_PENALTY) { + penalty = MAX_LEADING_LETTER_PENALTY; + } + score += penalty; + + // Apply unmatched penalty + const int unmatched = strSz - numMatches; + score += UNMATCHED_LETTER_PENALTY * unmatched; + + // Apply ordering bonuses + for (int i = 0; i < numMatches; i++) { + const matchidx_T currIdx = matches[i]; + + if (i > 0) { + const matchidx_T prevIdx = matches[i - 1]; + + // Sequential + if (currIdx == prevIdx + 1) { + score += SEQUENTIAL_BONUS; + } + } + + // Check for bonuses based on neighbor character value + if (currIdx > 0) { + // Camel case + const char_u *p = str; + int neighbor; + + for (matchidx_T sidx = 0; sidx < currIdx; sidx++) { + neighbor = utf_ptr2char(p); + mb_ptr2char_adv(&p); + } + const int curr = utf_ptr2char(p); + + if (mb_islower(neighbor) && mb_isupper(curr)) { + score += CAMEL_BONUS; + } + + // Separator + const bool neighborSeparator = neighbor == '_' || neighbor == ' '; + if (neighborSeparator) { + score += SEPARATOR_BONUS; + } + } else { + // First letter + score += FIRST_LETTER_BONUS; + } + } + return score; +} + +static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchidx_T strIdx, + int *const outScore, const char_u *const strBegin, + const int strLen, const matchidx_T *const srcMatches, + matchidx_T *const matches, const int maxMatches, int nextMatch, + int *const recursionCount) + FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5, 8, 11) FUNC_ATTR_WARN_UNUSED_RESULT { // Recursion params bool recursiveMatch = false; - char_u bestRecursiveMatches[256]; + matchidx_T bestRecursiveMatches[MAXMATCHES]; int bestRecursiveScore = 0; // Count recursions (*recursionCount)++; - if (*recursionCount >= recursionLimit) { + if (*recursionCount >= FUZZY_MATCH_RECURSION_LIMIT) { return false; } @@ -4839,119 +4926,59 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, int * // Loop through fuzpat and str looking for a match bool first_match = true; - while (*fuzpat != '\0' && *str != '\0') { + while (*fuzpat != NUL && *str != NUL) { + const int c1 = utf_ptr2char(fuzpat); + const int c2 = utf_ptr2char(str); + // Found match - if (mb_tolower(*fuzpat) == mb_tolower(*str)) { + if (mb_tolower(c1) == mb_tolower(c2)) { // Supplied matches buffer was too short if (nextMatch >= maxMatches) { return false; } // "Copy-on-Write" srcMatches into matches - if (first_match && srcMatches) { - memcpy(matches, srcMatches, nextMatch); + if (first_match && srcMatches != NULL) { + memcpy(matches, srcMatches, nextMatch * sizeof(srcMatches[0])); first_match = false; } // Recursive call that "skips" this match - char_u recursiveMatches[256]; + matchidx_T recursiveMatches[MAXMATCHES]; int recursiveScore = 0; - if (fuzzy_match_recursive(fuzpat, str + 1, &recursiveScore, strBegin, matches, - recursiveMatches, sizeof(recursiveMatches), nextMatch, - recursionCount, recursionLimit)) { + const char_u *const next_char = str + utfc_ptr2len(str); + if (fuzzy_match_recursive(fuzpat, next_char, strIdx + 1, &recursiveScore, strBegin, strLen, + matches, recursiveMatches, + sizeof(recursiveMatches) / sizeof(recursiveMatches[0]), nextMatch, + recursionCount)) { // Pick best recursive score if (!recursiveMatch || recursiveScore > bestRecursiveScore) { - memcpy(bestRecursiveMatches, recursiveMatches, 256); + memcpy(bestRecursiveMatches, recursiveMatches, MAXMATCHES * sizeof(recursiveMatches[0])); bestRecursiveScore = recursiveScore; } recursiveMatch = true; } // Advance - matches[nextMatch++] = (char_u)(str - strBegin); - fuzpat++; + matches[nextMatch++] = strIdx; + mb_ptr2char_adv(&fuzpat); } - str++; + mb_ptr2char_adv(&str); + strIdx++; } // Determine if full fuzpat was matched - const bool matched = *fuzpat == '\0'; + const bool matched = *fuzpat == NUL; // Calculate score if (matched) { - // bonus for adjacent matches - const int sequential_bonus = 15; - // bonus if match occurs after a separator - const int separator_bonus = 30; - // bonus if match is uppercase and prev is lower - const int camel_bonus = 30; - // bonus if the first letter is matched - const int first_letter_bonus = 15; - // penalty applied for every letter in str before the first match - const int leading_letter_penalty = -5; - // maximum penalty for leading letters - const int max_leading_letter_penalty = -15; - // penalty for every letter that doesn't matter - const int unmatched_letter_penalty = -1; - - // Iterate str to end - while (*str != '\0') { - str++; - } - - // Initialize score - *outScore = 100; - - // Apply leading letter penalty - int penalty = leading_letter_penalty * matches[0]; - if (penalty < max_leading_letter_penalty) { - penalty = max_leading_letter_penalty; - } - *outScore += penalty; - - // Apply unmatched penalty - const int unmatched = (int)(str - strBegin) - nextMatch; - *outScore += unmatched_letter_penalty * unmatched; - - // Apply ordering bonuses - for (int i = 0; i < nextMatch; i++) { - const char_u currIdx = matches[i]; - - if (i > 0) { - const char_u prevIdx = matches[i - 1]; - - // Sequential - if (currIdx == (prevIdx + 1)) { - *outScore += sequential_bonus; - } - } - - // Check for bonuses based on neighbor character value - if (currIdx > 0) { - // Camel case - const char_u neighbor = strBegin[currIdx - 1]; - const char_u curr = strBegin[currIdx]; - - if (islower(neighbor) && isupper(curr)) { - *outScore += camel_bonus; - } - - // Separator - const bool neighborSeparator = neighbor == '_' || neighbor == ' '; - if (neighborSeparator) { - *outScore += separator_bonus; - } - } else { - // First letter - *outScore += first_letter_bonus; - } - } + *outScore = fuzzy_match_compute_score(strBegin, strLen, matches, nextMatch); } // Return best result if (recursiveMatch && (!matched || bestRecursiveScore > *outScore)) { // Recursive score is better than "this" - memcpy(matches, bestRecursiveMatches, maxMatches); + memcpy(matches, bestRecursiveMatches, maxMatches * sizeof(matches[0])); *outScore = bestRecursiveScore; return true; } else if (matched) { @@ -4969,21 +4996,22 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, int * /// normalized and varies with pattern. /// Recursion is limited internally (default=10) to prevent degenerate cases /// (fuzpat="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). -/// Uses char_u for match indices. Therefore patterns are limited to 256 +/// Uses char_u for match indices. Therefore patterns are limited to MAXMATCHES /// characters. /// -/// Returns true if fuzpat is found AND calculates a score. -static bool fuzzy_match(const char_u *const str, const char_u *const fuzpat, int *const outScore) +/// @return true if 'fuzpat' matches 'str'. Also returns the match score in +/// 'outScore' and the matching character positions in 'matches'. +static bool fuzzy_match(char_u *const str, const char_u *const fuzpat, int *const outScore, + matchidx_T *const matches, const int maxMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - char_u matches[256]; int recursionCount = 0; - int recursionLimit = 10; + const int len = mb_charlen(str); *outScore = 0; - return fuzzy_match_recursive(fuzpat, str, outScore, str, NULL, matches, sizeof(matches), 0, - &recursionCount, recursionLimit); + return fuzzy_match_recursive(fuzpat, str, 0, outScore, str, len, NULL, matches, maxMatches, 0, + &recursionCount); } /// Sort the fuzzy matches in the descending order of the match score. @@ -4996,70 +5024,188 @@ static int fuzzy_item_compare(const void *const s1, const void *const s2) return v1 == v2 ? 0 : v1 > v2 ? -1 : 1; } -/// Fuzzy search the string 'str' in 'strlist' and return the matching strings -/// in 'fmatchlist'. -static void match_fuzzy(const list_T *const strlist, const char_u *const str, - list_T *const fmatchlist) - FUNC_ATTR_NONNULL_ARG(2, 3) +/// Fuzzy search the string 'str' in a list of 'items' and return the matching +/// strings in 'fmatchlist'. +/// If 'items' is a list of strings, then search for 'str' in the list. +/// If 'items' is a list of dicts, then either use 'key' to lookup the string +/// for each item or use 'item_cb' Funcref function to get the string. +/// If 'retmatchpos' is true, then return a list of positions where 'str' +/// matches for each item. +static void match_fuzzy(list_T *const items, char_u *const str, const char_u *const key, + Callback *const item_cb, const bool retmatchpos, list_T *const fmatchlist) + FUNC_ATTR_NONNULL_ARG(2, 4, 6) { - const long len = tv_list_len(strlist); + const long len = tv_list_len(items); if (len == 0) { return; } - fuzzyItem_T *const ptrs = xmalloc(sizeof(fuzzyItem_T) * len); + fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T)); long i = 0; bool found_match = false; + matchidx_T matches[MAXMATCHES]; - // For all the string items in strlist, get the fuzzy matching score - TV_LIST_ITER_CONST(strlist, li, { + // For all the string items in items, get the fuzzy matching score + TV_LIST_ITER(items, li, { ptrs[i].item = li; - ptrs[i].score = -9999; - // ignore non-string items in the list + ptrs[i].score = SCORE_NONE; + char_u *itemstr = NULL; + typval_T rettv; + rettv.v_type = VAR_UNKNOWN; const typval_T *const tv = TV_LIST_ITEM_TV(li); - if (tv->v_type == VAR_STRING && tv->vval.v_string != NULL) { - int score; - if (fuzzy_match(tv->vval.v_string, str, &score)) { - ptrs[i].score = score; - found_match = true; + if (tv->v_type == VAR_STRING) { // list of strings + itemstr = tv->vval.v_string; + } else if (tv->v_type == VAR_DICT && (key != NULL || item_cb->type != kCallbackNone)) { + // For a dict, either use the specified key to lookup the string or + // use the specified callback function to get the string. + if (key != NULL) { + itemstr = (char_u *)tv_dict_get_string(tv->vval.v_dict, (const char *)key, false); + } else { + typval_T argv[2]; + + // Invoke the supplied callback (if any) to get the dict item + tv->vval.v_dict->dv_refcount++; + argv[0].v_type = VAR_DICT; + argv[0].vval.v_dict = tv->vval.v_dict; + argv[1].v_type = VAR_UNKNOWN; + if (callback_call(item_cb, 1, argv, &rettv)) { + if (rettv.v_type == VAR_STRING) { + itemstr = rettv.vval.v_string; + } + } + tv_dict_unref(tv->vval.v_dict); + } + } + + int score; + if (itemstr != NULL + && fuzzy_match(itemstr, str, &score, matches, sizeof(matches) / sizeof(matches[0]))) { + // Copy the list of matching positions in itemstr to a list, if + // 'retmatchpos' is set. + if (retmatchpos) { + const int strsz = mb_charlen(str); + ptrs[i].lmatchpos = tv_list_alloc(strsz); + for (int j = 0; j < strsz; j++) { + tv_list_append_number(ptrs[i].lmatchpos, matches[j]); + } } + ptrs[i].score = score; + found_match = true; } i++; + tv_clear(&rettv); }); if (found_match) { // Sort the list by the descending order of the match score - qsort(ptrs, (size_t)len, sizeof(fuzzyItem_T), fuzzy_item_compare); + qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_item_compare); + + // For matchfuzzy(), return a list of matched strings. + // ['str1', 'str2', 'str3'] + // For matchfuzzypos(), return a list with two items. + // The first item is a list of matched strings. The second item + // is a list of lists where each list item is a list of matched + // character positions. + // [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]] + list_T *l; + if (retmatchpos) { + const listitem_T *const li = tv_list_find(fmatchlist, 0); + assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); + l = TV_LIST_ITEM_TV(li)->vval.v_list; + } else { + l = fmatchlist; + } - // Copy the matching strings with 'score != -9999' to the return list + // Copy the matching strings with a valid score to the return list for (i = 0; i < len; i++) { - if (ptrs[i].score == -9999) { + if (ptrs[i].score == SCORE_NONE) { break; } - const typval_T *const tv = TV_LIST_ITEM_TV(ptrs[i].item); - tv_list_append_string(fmatchlist, (const char *)tv->vval.v_string, -1); + tv_list_append_tv(l, TV_LIST_ITEM_TV(ptrs[i].item)); } - } + // next copy the list of matching positions + if (retmatchpos) { + const listitem_T *const li = tv_list_find(fmatchlist, -1); + assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); + l = TV_LIST_ITEM_TV(li)->vval.v_list; + for (i = 0; i < len; i++) { + if (ptrs[i].score == SCORE_NONE) { + break; + } + tv_list_append_list(l, ptrs[i].lmatchpos); + } + } + } xfree(ptrs); } -/// "matchfuzzy()" function -void f_matchfuzzy(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// Do fuzzy matching. Returns the list of matched strings in 'rettv'. +/// If 'retmatchpos' is true, also returns the matching character positions. +static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, + const bool retmatchpos) + FUNC_ATTR_NONNULL_ALL { - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - if (argvars[0].vval.v_list == NULL) { + // validate and get the arguments + if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { + semsg(_(e_listarg), retmatchpos ? "matchfuzzypos()" : "matchfuzzy()"); return; } if (argvars[1].v_type != VAR_STRING || argvars[1].vval.v_string == NULL) { semsg(_(e_invarg2), tv_get_string(&argvars[1])); return; } - match_fuzzy(argvars[0].vval.v_list, (const char_u *)tv_get_string(&argvars[1]), - tv_list_alloc_ret(rettv, kListLenUnknown)); + + Callback cb = CALLBACK_NONE; + const char_u *key = NULL; + if (argvars[2].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { + emsg(_(e_dictreq)); + return; + } + + // To search a dict, either a callback function or a key can be + // specified. + dict_T *const d = argvars[2].vval.v_dict; + const dictitem_T *const di = tv_dict_find(d, "key", -1); + if (di != NULL) { + if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL + || *di->di_tv.vval.v_string == NUL) { + semsg(_(e_invarg2), tv_get_string(&di->di_tv)); + return; + } + key = (const char_u *)tv_get_string(&di->di_tv); + } else if (!tv_dict_get_callback(d, "text_cb", -1, &cb)) { + semsg(_(e_invargval), "text_cb"); + return; + } + } + + // get the fuzzy matches + tv_list_alloc_ret(rettv, retmatchpos ? 2 : kListLenUnknown); + if (retmatchpos) { + // For matchfuzzypos(), a list with two items are returned. First item + // is a list of matching strings and the second item is a list of + // lists with matching positions within each string. + tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); + tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); + } + + match_fuzzy(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), key, &cb, retmatchpos, + rettv->vval.v_list); + callback_free(&cb); +} + +/// "matchfuzzy()" function +void f_matchfuzzy(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_fuzzymatch(argvars, rettv, false); +} + +/// "matchfuzzypos()" function +void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + do_fuzzymatch(argvars, rettv, true); } /// Find identifiers or defines in included files. diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 57ae0d3020..438bed51c6 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1731,32 +1731,6 @@ func Test_nr2char() call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\"')) endfunc -" Test for matchfuzzy() -func Test_matchfuzzy() - call assert_fails('call matchfuzzy(10, "abc")', 'E714:') - " Needs v8.2.1183. - " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:') - call assert_fails('call matchfuzzy(["abc"], [])', 'E475:') - call assert_equal([], matchfuzzy([], 'abc')) - call assert_equal([], matchfuzzy(['abc'], '')) - call assert_equal(['abc'], matchfuzzy(['abc', 10], 'ac')) - call assert_equal([], matchfuzzy([10, 20], 'ac')) - call assert_equal(['abc'], matchfuzzy(['abc'], 'abc')) - call assert_equal(['crayon', 'camera'], matchfuzzy(['camera', 'crayon'], 'cra')) - call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa')) - call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one')) - call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo')) - call assert_equal(['one_two', 'onetwo'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo')) - call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa')) - call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257))) - - %bw! - eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)}) - let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl') - call assert_equal(1, len(l)) - call assert_match('needle', l[0]) -endfunc - " Test for getcurpos() and setpos() func Test_getcurpos_setpos() new diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim new file mode 100644 index 0000000000..4390852639 --- /dev/null +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -0,0 +1,203 @@ +" Tests for fuzzy matching + +source shared.vim +source check.vim + +" Test for matchfuzzy() +func Test_matchfuzzy() + call assert_fails('call matchfuzzy(10, "abc")', 'E686:') + " Needs v8.2.1183; match the final error that's thrown for now + " call assert_fails('call matchfuzzy(["abc"], [])', 'E730:') + call assert_fails('call matchfuzzy(["abc"], [])', 'E475:') + call assert_fails("let x = matchfuzzy(v:_null_list, 'foo')", 'E686:') + call assert_fails('call matchfuzzy(["abc"], v:_null_string)', 'E475:') + call assert_equal([], matchfuzzy([], 'abc')) + call assert_equal([], matchfuzzy(['abc'], '')) + call assert_equal(['abc'], matchfuzzy(['abc', 10], 'ac')) + call assert_equal([], matchfuzzy([10, 20], 'ac')) + call assert_equal(['abc'], matchfuzzy(['abc'], 'abc')) + call assert_equal(['crayon', 'camera'], matchfuzzy(['camera', 'crayon'], 'cra')) + call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa')) + call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one')) + call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo')) + call assert_equal(['one_two', 'onetwo'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo')) + call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa')) + call assert_equal(256, matchfuzzy([repeat('a', 256)], repeat('a', 256))[0]->len()) + call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257))) + + " Tests for match preferences + " preference for camel case match + call assert_equal(['oneTwo', 'onetwo'], ['onetwo', 'oneTwo']->matchfuzzy('onetwo')) + " preference for match after a separator (_ or space) + call assert_equal(['one_two', 'one two', 'onetwo'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) + " preference for leading letter match + call assert_equal(['onetwo', 'xonetwo'], ['xonetwo', 'onetwo']->matchfuzzy('onetwo')) + " preference for sequential match + call assert_equal(['onetwo', 'oanbectdweo'], ['oanbectdweo', 'onetwo']->matchfuzzy('onetwo')) + " non-matching leading letter(s) penalty + call assert_equal(['xonetwo', 'xxonetwo'], ['xxonetwo', 'xonetwo']->matchfuzzy('onetwo')) + " total non-matching letter(s) penalty + call assert_equal(['one', 'onex', 'onexx'], ['onexx', 'one', 'onex']->matchfuzzy('one')) + + %bw! + eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)}) + let l = getbufinfo()->map({_, v -> v.name})->matchfuzzy('ndl') + call assert_equal(1, len(l)) + call assert_match('needle', l[0]) + + let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}] + call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'text_cb' : {v -> v.val}})) + call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'key' : 'val'})) + call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> v.val}})) + call assert_equal([], matchfuzzy(l, 'day', {'key' : 'val'})) + call assert_fails("let x = matchfuzzy(l, 'cam', 'random')", 'E715:') + call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> []}})) + call assert_equal([], matchfuzzy(l, 'day', {'text_cb' : {v -> 1}})) + call assert_fails("let x = matchfuzzy(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:') + call assert_equal([], matchfuzzy(l, 'cam')) + " Nvim's callback implementation is different, so E6000 is expected instead, + " but we need v8.2.1183 to assert it + " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E921:') + " call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E6000:') + call assert_fails("let x = matchfuzzy(l, 'cam', {'text_cb' : []})", 'E475:') + " call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E730:') + call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : []})", 'E475:') + call assert_fails("let x = matchfuzzy(l, 'cam', v:_null_dict)", 'E715:') + call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:') + " Nvim doesn't have null functions + " call assert_fails("let x = matchfuzzy(l, 'foo', {'text_cb' : test_null_function()})", 'E475:') + + let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}] + call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : 'name'})", 'E730:') + + " Test in latin1 encoding + let save_enc = &encoding + " Nvim supports utf-8 encoding only + " set encoding=latin1 + call assert_equal(['abc'], matchfuzzy(['abc'], 'abc')) + let &encoding = save_enc +endfunc + +" Test for the fuzzymatchpos() function +func Test_matchfuzzypos() + call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'curl'], 'rl')) + call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'one', 'curl'], 'rl')) + call assert_equal([['hello', 'hello world hello world'], + \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], + \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 'hello')) + call assert_equal([['aaaaaaa'], [[0, 1, 2]]], matchfuzzypos(['aaaaaaa'], 'aaa')) + call assert_equal([[], []], matchfuzzypos(['world', 'curl'], 'ab')) + let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256)) + call assert_equal(range(256), x[1][0]) + call assert_equal([[], []], matchfuzzypos([repeat('a', 300)], repeat('a', 257))) + call assert_equal([[], []], matchfuzzypos([], 'abc')) + + " match in a long string + call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]]], + \ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc')) + + " preference for camel case match + call assert_equal([['xabcxxaBc'], [[6, 7, 8]]], matchfuzzypos(['xabcxxaBc'], 'abc')) + " preference for match after a separator (_ or space) + call assert_equal([['xabx_ab'], [[5, 6]]], matchfuzzypos(['xabx_ab'], 'ab')) + " preference for leading letter match + call assert_equal([['abcxabc'], [[0, 1]]], matchfuzzypos(['abcxabc'], 'ab')) + " preference for sequential match + call assert_equal([['aobncedone'], [[7, 8, 9]]], matchfuzzypos(['aobncedone'], 'one')) + " best recursive match + call assert_equal([['xoone'], [[2, 3, 4]]], matchfuzzypos(['xoone'], 'one')) + + let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}] + call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]], + \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}})) + call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]], + \ matchfuzzypos(l, 'cam', {'key' : 'val'})) + call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> v.val}})) + call assert_equal([[], []], matchfuzzypos(l, 'day', {'key' : 'val'})) + call assert_fails("let x = matchfuzzypos(l, 'cam', 'random')", 'E715:') + call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> []}})) + call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 1}})) + call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:') + call assert_equal([[], []], matchfuzzypos(l, 'cam')) + " Nvim's callback implementation is different, so E6000 is expected instead, + " but we need v8.2.1183 to assert it + " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:') + " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E6000:') + call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E475:') + " call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:') + call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E475:') + call assert_fails("let x = matchfuzzypos(l, 'cam', v:_null_dict)", 'E715:') + call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : v:_null_string})", 'E475:') + " Nvim doesn't have null functions + " call assert_fails("let x = matchfuzzypos(l, 'foo', {'text_cb' : test_null_function()})", 'E475:') + + let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}] + call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : 'name'})", 'E730:') +endfunc + +func Test_matchfuzzy_mbyte() + CheckFeature multi_lang + call assert_equal(['ンヹㄇヺヴ'], matchfuzzy(['ンヹㄇヺヴ'], 'ヹヺ')) + " reverse the order of characters + call assert_equal([], matchfuzzy(['ンヹㄇヺヴ'], 'ヺヹ')) + call assert_equal(['αβΩxxx', 'xαxβxΩx'], + \ matchfuzzy(['αβΩxxx', 'xαxβxΩx'], 'αβΩ')) + call assert_equal(['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'Ï€bÏ€'], + \ matchfuzzy(['Ï€bÏ€', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ')) + + " preference for camel case match + call assert_equal(['oneÄ„wo', 'oneÄ…wo'], + \ ['oneÄ…wo', 'oneÄ„wo']->matchfuzzy('oneÄ…wo')) + " preference for match after a separator (_ or space) + call assert_equal(['â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡abㄟㄠ'], + \ ['â… â…¡abㄟㄠ', 'â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) + " preference for leading letter match + call assert_equal(['Å—Åţũŵż', 'xÅ—Åţũŵż'], + \ ['xÅ—Åţũŵż', 'Å—Åţũŵż']->matchfuzzy('Å—Åţũŵż')) + " preference for sequential match + call assert_equal(['ㄞㄡㄤffï¬ï¬‚', 'ㄞaã„¡bㄤcffdï¬efl'], + \ ['ㄞaã„¡bㄤcffdï¬efl', 'ㄞㄡㄤffï¬ï¬‚']->matchfuzzy('ㄞㄡㄤffï¬ï¬‚')) + " non-matching leading letter(s) penalty + call assert_equal(['xㄞㄡㄤffï¬ï¬‚', 'xxㄞㄡㄤffï¬ï¬‚'], + \ ['xxㄞㄡㄤffï¬ï¬‚', 'xㄞㄡㄤffï¬ï¬‚']->matchfuzzy('ㄞㄡㄤffï¬ï¬‚')) + " total non-matching letter(s) penalty + call assert_equal(['Å—ÅÅ£', 'Å—ÅÅ£x', 'Å—ÅÅ£xx'], + \ ['Å—ÅÅ£xx', 'Å—ÅÅ£', 'Å—ÅÅ£x']->matchfuzzy('Å—ÅÅ£')) +endfunc + +func Test_matchfuzzypos_mbyte() + CheckFeature multi_lang + call assert_equal([['ã“ã‚“ã«ã¡ã¯ä¸–界'], [[0, 1, 2, 3, 4]]], + \ matchfuzzypos(['ã“ã‚“ã«ã¡ã¯ä¸–界'], 'ã“ã‚“ã«ã¡ã¯')) + call assert_equal([['ンヹㄇヺヴ'], [[1, 3]]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ')) + " reverse the order of characters + call assert_equal([[], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ')) + call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]]], + \ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ')) + call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'Ï€bÏ€'], + \ [[0, 1], [0, 1], [0, 1], [0, 2]]], + \ matchfuzzypos(['Ï€bÏ€', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ')) + call assert_equal([['ααααααα'], [[0, 1, 2]]], + \ matchfuzzypos(['ααααααα'], 'ααα')) + + call assert_equal([[], []], matchfuzzypos(['ンヹㄇ', 'Å—ÅÅ£'], 'ffï¬ï¬‚')) + let x = matchfuzzypos([repeat('Ψ', 256)], repeat('Ψ', 256)) + call assert_equal(range(256), x[1][0]) + call assert_equal([[], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 257))) + + " match in a long string + call assert_equal([[repeat('♪', 300) .. '✗✗✗'], [[300, 301, 302]]], + \ matchfuzzypos([repeat('♪', 300) .. '✗✗✗'], '✗✗✗')) + " preference for camel case match + call assert_equal([['xѳѵÒxxѳѴÒ'], [[6, 7, 8]]], matchfuzzypos(['xѳѵÒxxѳѴÒ'], 'ѳѵÒ')) + " preference for match after a separator (_ or space) + call assert_equal([['xã¡ã x_ã¡ã '], [[5, 6]]], matchfuzzypos(['xã¡ã x_ã¡ã '], 'ã¡ã ')) + " preference for leading letter match + call assert_equal([['ѳѵÒxѳѵÒ'], [[0, 1]]], matchfuzzypos(['ѳѵÒxѳѵÒ'], 'ѳѵ')) + " preference for sequential match + call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]]], matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ')) + " best recursive match + call assert_equal([['xффйд'], [[2, 3, 4]]], matchfuzzypos(['xффйд'], 'фйд')) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 8313d31e4a5065ac0e945ebc689fa083a50e41dc Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 21:12:53 +0000 Subject: vim-patch:8.2.1872: matchfuzzy() does not prefer sequential matches Problem: Matchfuzzy() does not prefer sequential matches. Solution: Give sequential matches a higher bonus. (Christian Brabandt, closes vim/vim#7140) https://github.com/vim/vim/commit/e9f9f16387554e5b34ba42ce00c42c28dd66af58 --- src/nvim/search.c | 5 +++-- src/nvim/testdir/test_matchfuzzy.vim | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index 8f3e66744f..a5b7d8f5ee 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4818,8 +4818,9 @@ typedef struct { list_T *lmatchpos; } fuzzyItem_T; -/// bonus for adjacent matches -#define SEQUENTIAL_BONUS 15 +/// bonus for adjacent matches; this is higher than SEPARATOR_BONUS so that +/// matching a whole word is preferred. +#define SEQUENTIAL_BONUS 40 /// bonus if match occurs after a separator #define SEPARATOR_BONUS 30 /// bonus if match is uppercase and prev is lower diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim index 4390852639..293f7387b8 100644 --- a/src/nvim/testdir/test_matchfuzzy.vim +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -20,7 +20,7 @@ func Test_matchfuzzy() call assert_equal(['aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa', 'aba'], matchfuzzy(['aba', 'aabbaa', 'aaabbbaaa', 'aaaabbbbaaaa'], 'aa')) call assert_equal(['one'], matchfuzzy(['one', 'two'], 'one')) call assert_equal(['oneTwo', 'onetwo'], matchfuzzy(['onetwo', 'oneTwo'], 'oneTwo')) - call assert_equal(['one_two', 'onetwo'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo')) + call assert_equal(['onetwo', 'one_two'], matchfuzzy(['onetwo', 'one_two'], 'oneTwo')) call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa')) call assert_equal(256, matchfuzzy([repeat('a', 256)], repeat('a', 256))[0]->len()) call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257))) @@ -29,7 +29,11 @@ func Test_matchfuzzy() " preference for camel case match call assert_equal(['oneTwo', 'onetwo'], ['onetwo', 'oneTwo']->matchfuzzy('onetwo')) " preference for match after a separator (_ or space) - call assert_equal(['one_two', 'one two', 'onetwo'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) + if has("win32") + call assert_equal(['onetwo', 'one two', 'one_two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) + else + call assert_equal(['onetwo', 'one_two', 'one two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) + endif " preference for leading letter match call assert_equal(['onetwo', 'xonetwo'], ['xonetwo', 'onetwo']->matchfuzzy('onetwo')) " preference for sequential match @@ -38,6 +42,8 @@ func Test_matchfuzzy() call assert_equal(['xonetwo', 'xxonetwo'], ['xxonetwo', 'xonetwo']->matchfuzzy('onetwo')) " total non-matching letter(s) penalty call assert_equal(['one', 'onex', 'onexx'], ['onexx', 'one', 'onex']->matchfuzzy('one')) + " prefer complete matches over separator matches + call assert_equal(['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c'], ['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c']->matchfuzzy('vimrc')) %bw! eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)}) @@ -148,9 +154,16 @@ func Test_matchfuzzy_mbyte() " preference for camel case match call assert_equal(['oneÄ„wo', 'oneÄ…wo'], \ ['oneÄ…wo', 'oneÄ„wo']->matchfuzzy('oneÄ…wo')) - " preference for match after a separator (_ or space) - call assert_equal(['â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡abㄟㄠ'], - \ ['â… â…¡abㄟㄠ', 'â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) + " preference for complete match then match after separator (_ or space) + if has("win32") + " order is different between Windows and Unix :( + " It's important that the complete match is first + call assert_equal(['â… â…¡abㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡a_bㄟㄠ'], + \ ['â… â…¡abㄟㄠ', 'â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) + else + call assert_equal(['â… â…¡abㄟㄠ'] + sort(['â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']), + \ ['â… â…¡abㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡a_bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) + endif " preference for leading letter match call assert_equal(['Å—Åţũŵż', 'xÅ—Åţũŵż'], \ ['xÅ—Åţũŵż', 'Å—Åţũŵż']->matchfuzzy('Å—Åţũŵż')) -- cgit From 30deb14f397e576aedfa54600ed7408b3e03459d Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 21:25:41 +0000 Subject: vim-patch:8.2.1893: fuzzy matching does not support multiple words Problem: Fuzzy matching does not support multiple words. Solution: Add support for matching white space separated words. (Yegappan Lakshmanan, closes vim/vim#7163) https://github.com/vim/vim/commit/8ded5b647aa4b3338da721b343e0bce0f86655f6 --- src/nvim/search.c | 171 +++++++++++++++++++++++++---------- src/nvim/testdir/test_matchfuzzy.vim | 62 +++++++++---- 2 files changed, 167 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index a5b7d8f5ee..a021466446 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4771,16 +4771,16 @@ the_end: /// Ported from the lib_fts library authored by Forrest Smith. /// https://github.com/forrestthewoods/lib_fts/tree/master/code /// -/// Blog describing the algorithm: +/// The following blog describes the fuzzy matching algorithm: /// https://www.forrestthewoods.com/blog/reverse_engineering_sublime_texts_fuzzy_match/ /// /// Each matching string is assigned a score. The following factors are checked: -/// Matched letter -/// Unmatched letter -/// Consecutively matched letters -/// Proximity to start -/// Letter following a separator (space, underscore) -/// Uppercase letter following lowercase (aka CamelCase) +/// - Matched letter +/// - Unmatched letter +/// - Consecutively matched letters +/// - Proximity to start +/// - Letter following a separator (space, underscore) +/// - Uppercase letter following lowercase (aka CamelCase) /// /// Matched letters are good. Unmatched letters are bad. Matching near the start /// is good. Matching the first letter in the middle of a phrase is good. @@ -4790,16 +4790,17 @@ the_end: /// File paths are different from file names. File extensions may be ignorable. /// Single words care about consecutive matches but not separators or camel /// case. -/// Score starts at 0 +/// Score starts at 100 /// Matched letter: +0 points /// Unmatched letter: -1 point -/// Consecutive match bonus: +5 points -/// Separator bonus: +10 points -/// Camel case bonus: +10 points -/// Unmatched leading letter: -3 points (max: -9) +/// Consecutive match bonus: +15 points +/// First letter bonus: +15 points +/// Separator bonus: +30 points +/// Camel case bonus: +30 points +/// Unmatched leading letter: -5 points (max: -15) /// /// There is some nuance to this. Scores don’t have an intrinsic meaning. The -/// score range isn’t 0 to 100. It’s roughly [-50, 50]. Longer words have a +/// score range isn’t 0 to 100. It’s roughly [50, 150]. Longer words have a /// lower minimum score due to unmatched letter penalty. Longer search patterns /// have a higher maximum score due to match bonuses. /// @@ -4813,6 +4814,7 @@ the_end: /// There is not an explicit bonus for an exact match. Unmatched letters receive /// a penalty. So shorter strings and closer matches are worth more. typedef struct { + int idx; ///< used for stable sort listitem_T *item; int score; list_T *lmatchpos; @@ -4833,6 +4835,8 @@ typedef struct { #define MAX_LEADING_LETTER_PENALTY -15 /// penalty for every letter that doesn't match #define UNMATCHED_LETTER_PENALTY -1 +/// penalty for gap in matching positions (-2 * k) +#define GAP_PENALTY -2 /// Score for a string that doesn't fuzzy match the pattern #define SCORE_NONE -9999 @@ -4870,6 +4874,8 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, // Sequential if (currIdx == prevIdx + 1) { score += SEQUENTIAL_BONUS; + } else { + score += GAP_PENALTY * (currIdx - prevIdx); } } @@ -4881,7 +4887,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, for (matchidx_T sidx = 0; sidx < currIdx; sidx++) { neighbor = utf_ptr2char(p); - mb_ptr2char_adv(&p); + MB_PTR_ADV(p); } const int curr = utf_ptr2char(p); @@ -4902,11 +4908,13 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, return score; } -static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchidx_T strIdx, - int *const outScore, const char_u *const strBegin, - const int strLen, const matchidx_T *const srcMatches, - matchidx_T *const matches, const int maxMatches, int nextMatch, - int *const recursionCount) +/// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'. +/// @return the number of matching characters. +static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchidx_T strIdx, + int *const outScore, const char_u *const strBegin, + const int strLen, const matchidx_T *const srcMatches, + matchidx_T *const matches, const int maxMatches, int nextMatch, + int *const recursionCount) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5, 8, 11) FUNC_ATTR_WARN_UNUSED_RESULT { // Recursion params @@ -4917,12 +4925,12 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, match // Count recursions (*recursionCount)++; if (*recursionCount >= FUZZY_MATCH_RECURSION_LIMIT) { - return false; + return 0; } // Detect end of strings if (*fuzpat == '\0' || *str == '\0') { - return false; + return 0; } // Loop through fuzpat and str looking for a match @@ -4935,7 +4943,7 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, match if (mb_tolower(c1) == mb_tolower(c2)) { // Supplied matches buffer was too short if (nextMatch >= maxMatches) { - return false; + return 0; } // "Copy-on-Write" srcMatches into matches @@ -4962,9 +4970,9 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, match // Advance matches[nextMatch++] = strIdx; - mb_ptr2char_adv(&fuzpat); + MB_PTR_ADV(fuzpat); } - mb_ptr2char_adv(&str); + MB_PTR_ADV(str); strIdx++; } @@ -4981,12 +4989,12 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, match // Recursive score is better than "this" memcpy(matches, bestRecursiveMatches, maxMatches * sizeof(matches[0])); *outScore = bestRecursiveScore; - return true; + return nextMatch; } else if (matched) { - return true; // "this" score is better than recursive + return nextMatch; // "this" score is better than recursive } - return false; // no match + return 0; // no match } /// fuzzy_match() @@ -4996,45 +5004,98 @@ static bool fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, match /// Scores values have no intrinsic meaning. Possible score range is not /// normalized and varies with pattern. /// Recursion is limited internally (default=10) to prevent degenerate cases -/// (fuzpat="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). +/// (pat_arg="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). /// Uses char_u for match indices. Therefore patterns are limited to MAXMATCHES /// characters. /// -/// @return true if 'fuzpat' matches 'str'. Also returns the match score in +/// @return true if 'pat_arg' matches 'str'. Also returns the match score in /// 'outScore' and the matching character positions in 'matches'. -static bool fuzzy_match(char_u *const str, const char_u *const fuzpat, int *const outScore, - matchidx_T *const matches, const int maxMatches) +static bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq, + int *const outScore, matchidx_T *const matches, const int maxMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - int recursionCount = 0; const int len = mb_charlen(str); + bool complete = false; + int numMatches = 0; *outScore = 0; - return fuzzy_match_recursive(fuzpat, str, 0, outScore, str, len, NULL, matches, maxMatches, 0, - &recursionCount); + char_u *const save_pat = vim_strsave(pat_arg); + char_u *pat = save_pat; + char_u *p = pat; + + // Try matching each word in 'pat_arg' in 'str' + while (true) { + if (matchseq) { + complete = true; + } else { + // Extract one word from the pattern (separated by space) + p = skipwhite(p); + if (*p == NUL) { + break; + } + pat = p; + while (*p != NUL && !ascii_iswhite(utf_ptr2char(p))) { + MB_PTR_ADV(p); + } + if (*p == NUL) { // processed all the words + complete = true; + } + *p = NUL; + } + + int score = 0; + int recursionCount = 0; + const int matchCount + = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches, + maxMatches - numMatches, 0, &recursionCount); + if (matchCount == 0) { + numMatches = 0; + break; + } + + // Accumulate the match score and the number of matches + *outScore += score; + numMatches += matchCount; + + if (complete) { + break; + } + + // try matching the next word + p++; + } + + xfree(save_pat); + return numMatches != 0; } /// Sort the fuzzy matches in the descending order of the match score. -static int fuzzy_item_compare(const void *const s1, const void *const s2) +/// For items with same score, retain the order using the index (stable sort) +static int fuzzy_match_item_compare(const void *const s1, const void *const s2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { const int v1 = ((const fuzzyItem_T *)s1)->score; const int v2 = ((const fuzzyItem_T *)s2)->score; + const int idx1 = ((const fuzzyItem_T *)s1)->idx; + const int idx2 = ((const fuzzyItem_T *)s2)->idx; - return v1 == v2 ? 0 : v1 > v2 ? -1 : 1; + return v1 == v2 ? (idx1 - idx2) : v1 > v2 ? -1 : 1; } /// Fuzzy search the string 'str' in a list of 'items' and return the matching /// strings in 'fmatchlist'. +/// If 'matchseq' is true, then for multi-word search strings, match all the +/// words in sequence. /// If 'items' is a list of strings, then search for 'str' in the list. /// If 'items' is a list of dicts, then either use 'key' to lookup the string /// for each item or use 'item_cb' Funcref function to get the string. /// If 'retmatchpos' is true, then return a list of positions where 'str' /// matches for each item. -static void match_fuzzy(list_T *const items, char_u *const str, const char_u *const key, - Callback *const item_cb, const bool retmatchpos, list_T *const fmatchlist) - FUNC_ATTR_NONNULL_ARG(2, 4, 6) +static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bool matchseq, + const char_u *const key, Callback *const item_cb, + const bool retmatchpos, list_T *const fmatchlist) + FUNC_ATTR_NONNULL_ARG(2, 5, 7) { const long len = tv_list_len(items); if (len == 0) { @@ -5048,6 +5109,7 @@ static void match_fuzzy(list_T *const items, char_u *const str, const char_u *co // For all the string items in items, get the fuzzy matching score TV_LIST_ITER(items, li, { + ptrs[i].idx = i; ptrs[i].item = li; ptrs[i].score = SCORE_NONE; char_u *itemstr = NULL; @@ -5079,15 +5141,20 @@ static void match_fuzzy(list_T *const items, char_u *const str, const char_u *co } int score; - if (itemstr != NULL - && fuzzy_match(itemstr, str, &score, matches, sizeof(matches) / sizeof(matches[0]))) { + if (itemstr != NULL && fuzzy_match(itemstr, str, matchseq, &score, matches, + sizeof(matches) / sizeof(matches[0]))) { // Copy the list of matching positions in itemstr to a list, if // 'retmatchpos' is set. if (retmatchpos) { - const int strsz = mb_charlen(str); - ptrs[i].lmatchpos = tv_list_alloc(strsz); - for (int j = 0; j < strsz; j++) { - tv_list_append_number(ptrs[i].lmatchpos, matches[j]); + ptrs[i].lmatchpos = tv_list_alloc(kListLenMayKnow); + int j = 0; + const char_u *p = str; + while (*p != NUL) { + if (!ascii_iswhite(utf_ptr2char(p))) { + tv_list_append_number(ptrs[i].lmatchpos, matches[j]); + j++; + } + MB_PTR_ADV(p); } } ptrs[i].score = score; @@ -5099,7 +5166,7 @@ static void match_fuzzy(list_T *const items, char_u *const str, const char_u *co if (found_match) { // Sort the list by the descending order of the match score - qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_item_compare); + qsort(ptrs, len, sizeof(fuzzyItem_T), fuzzy_match_item_compare); // For matchfuzzy(), return a list of matched strings. // ['str1', 'str2', 'str3'] @@ -5159,6 +5226,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, Callback cb = CALLBACK_NONE; const char_u *key = NULL; + bool matchseq = false; if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL) { emsg(_(e_dictreq)); @@ -5168,8 +5236,8 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, // To search a dict, either a callback function or a key can be // specified. dict_T *const d = argvars[2].vval.v_dict; - const dictitem_T *const di = tv_dict_find(d, "key", -1); - if (di != NULL) { + const dictitem_T *di; + if ((di = tv_dict_find(d, "key", -1)) != NULL) { if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL || *di->di_tv.vval.v_string == NUL) { semsg(_(e_invarg2), tv_get_string(&di->di_tv)); @@ -5180,6 +5248,9 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, semsg(_(e_invargval), "text_cb"); return; } + if ((di = tv_dict_find(d, "matchseq", -1)) != NULL) { + matchseq = true; + } } // get the fuzzy matches @@ -5192,8 +5263,8 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); } - match_fuzzy(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), key, &cb, retmatchpos, - rettv->vval.v_list); + fuzzy_match_in_list(argvars[0].vval.v_list, (char_u *)tv_get_string(&argvars[1]), matchseq, key, + &cb, retmatchpos, rettv->vval.v_list); callback_free(&cb); } diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim index 293f7387b8..28367b878d 100644 --- a/src/nvim/testdir/test_matchfuzzy.vim +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -24,16 +24,15 @@ func Test_matchfuzzy() call assert_equal(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], matchfuzzy(['aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'], 'aa')) call assert_equal(256, matchfuzzy([repeat('a', 256)], repeat('a', 256))[0]->len()) call assert_equal([], matchfuzzy([repeat('a', 300)], repeat('a', 257))) + " matches with same score should not be reordered + let l = ['abc1', 'abc2', 'abc3'] + call assert_equal(l, l->matchfuzzy('abc')) " Tests for match preferences " preference for camel case match call assert_equal(['oneTwo', 'onetwo'], ['onetwo', 'oneTwo']->matchfuzzy('onetwo')) " preference for match after a separator (_ or space) - if has("win32") - call assert_equal(['onetwo', 'one two', 'one_two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) - else - call assert_equal(['onetwo', 'one_two', 'one two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) - endif + call assert_equal(['onetwo', 'one_two', 'one two'], ['onetwo', 'one_two', 'one two']->matchfuzzy('onetwo')) " preference for leading letter match call assert_equal(['onetwo', 'xonetwo'], ['xonetwo', 'onetwo']->matchfuzzy('onetwo')) " preference for sequential match @@ -44,6 +43,17 @@ func Test_matchfuzzy() call assert_equal(['one', 'onex', 'onexx'], ['onexx', 'one', 'onex']->matchfuzzy('one')) " prefer complete matches over separator matches call assert_equal(['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c'], ['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c']->matchfuzzy('vimrc')) + " gap penalty + call assert_equal(['xxayybxxxx', 'xxayyybxxx', 'xxayyyybxx'], ['xxayyyybxx', 'xxayyybxxx', 'xxayybxxxx']->matchfuzzy('ab')) + + " match multiple words (separated by space) + call assert_equal(['foo bar baz'], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzy('baz foo')) + call assert_equal([], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzy('one two')) + call assert_equal([], ['foo bar']->matchfuzzy(" \t ")) + + " test for matching a sequence of words + call assert_equal(['bar foo'], ['foo bar', 'bar foo', 'foobar', 'barfoo']->matchfuzzy('bar foo', {'matchseq' : 1})) + call assert_equal([#{text: 'two one'}], [#{text: 'one two'}, #{text: 'two one'}]->matchfuzzy('two one', #{key: 'text', matchseq: v:true})) %bw! eval ['somebuf', 'anotherone', 'needle', 'yetanotherone']->map({_, v -> bufadd(v) + bufload(v)}) @@ -51,6 +61,7 @@ func Test_matchfuzzy() call assert_equal(1, len(l)) call assert_match('needle', l[0]) + " Test for fuzzy matching dicts let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}] call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'text_cb' : {v -> v.val}})) call assert_equal([{'id' : 6, 'val' : 'camera'}], matchfuzzy(l, 'cam', {'key' : 'val'})) @@ -72,6 +83,9 @@ func Test_matchfuzzy() call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : v:_null_string})", 'E475:') " Nvim doesn't have null functions " call assert_fails("let x = matchfuzzy(l, 'foo', {'text_cb' : test_null_function()})", 'E475:') + " matches with same score should not be reordered + let l = [#{text: 'abc', id: 1}, #{text: 'abc', id: 2}, #{text: 'abc', id: 3}] + call assert_equal(l, l->matchfuzzy('abc', #{key: 'text'})) let l = [{'id' : 5, 'name' : 'foo'}, {'id' : 6, 'name' : []}, {'id' : 7}] call assert_fails("let x = matchfuzzy(l, 'foo', {'key' : 'name'})", 'E730:') @@ -84,7 +98,7 @@ func Test_matchfuzzy() let &encoding = save_enc endfunc -" Test for the fuzzymatchpos() function +" Test for the matchfuzzypos() function func Test_matchfuzzypos() call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'curl'], 'rl')) call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'one', 'curl'], 'rl')) @@ -92,6 +106,10 @@ func Test_matchfuzzypos() \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 'hello')) call assert_equal([['aaaaaaa'], [[0, 1, 2]]], matchfuzzypos(['aaaaaaa'], 'aaa')) + call assert_equal([['a b'], [[0, 3]]], matchfuzzypos(['a b'], 'a b')) + call assert_equal([['a b'], [[0, 3]]], matchfuzzypos(['a b'], 'a b')) + call assert_equal([['a b'], [[0]]], matchfuzzypos(['a b'], ' a ')) + call assert_equal([[], []], matchfuzzypos(['a b'], ' ')) call assert_equal([[], []], matchfuzzypos(['world', 'curl'], 'ab')) let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256)) call assert_equal(range(256), x[1][0]) @@ -113,6 +131,12 @@ func Test_matchfuzzypos() " best recursive match call assert_equal([['xoone'], [[2, 3, 4]]], matchfuzzypos(['xoone'], 'one')) + " match multiple words (separated by space) + call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo')) + call assert_equal([[], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('one two')) + call assert_equal([[], []], ['foo bar']->matchfuzzypos(" \t ")) + call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]]], ['grace']->matchfuzzypos('race ace grace')) + let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}] call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]], \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}})) @@ -141,6 +165,7 @@ func Test_matchfuzzypos() call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : 'name'})", 'E730:') endfunc +" Test for matchfuzzy() with multibyte characters func Test_matchfuzzy_mbyte() CheckFeature multi_lang call assert_equal(['ンヹㄇヺヴ'], matchfuzzy(['ンヹㄇヺヴ'], 'ヹヺ')) @@ -151,19 +176,19 @@ func Test_matchfuzzy_mbyte() call assert_equal(['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'Ï€bÏ€'], \ matchfuzzy(['Ï€bÏ€', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ')) + " match multiple words (separated by space) + call assert_equal(['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€'], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzy('ë¼ì§€ 마리ì˜')) + call assert_equal([], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzy('파란 하늘')) + " preference for camel case match call assert_equal(['oneÄ„wo', 'oneÄ…wo'], \ ['oneÄ…wo', 'oneÄ„wo']->matchfuzzy('oneÄ…wo')) " preference for complete match then match after separator (_ or space) - if has("win32") - " order is different between Windows and Unix :( - " It's important that the complete match is first - call assert_equal(['â… â…¡abㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡a_bㄟㄠ'], - \ ['â… â…¡abㄟㄠ', 'â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) - else - call assert_equal(['â… â…¡abㄟㄠ'] + sort(['â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']), + call assert_equal(['â… â…¡abㄟㄠ'] + sort(['â… â…¡a_bㄟㄠ', 'â… â…¡a bㄟㄠ']), \ ['â… â…¡abㄟㄠ', 'â… â…¡a bㄟㄠ', 'â… â…¡a_bㄟㄠ']->matchfuzzy('â… â…¡abㄟㄠ')) - endif + " preference for match after a separator (_ or space) + call assert_equal(['ã„“ã„”abㄟㄠ', 'ã„“ã„”a_bㄟㄠ', 'ã„“ã„”a bㄟㄠ'], + \ ['ã„“ã„”a_bㄟㄠ', 'ã„“ã„”a bㄟㄠ', 'ã„“ã„”abㄟㄠ']->matchfuzzy('ã„“ã„”abㄟㄠ')) " preference for leading letter match call assert_equal(['Å—Åţũŵż', 'xÅ—Åţũŵż'], \ ['xÅ—Åţũŵż', 'Å—Åţũŵż']->matchfuzzy('Å—Åţũŵż')) @@ -178,6 +203,7 @@ func Test_matchfuzzy_mbyte() \ ['Å—ÅÅ£xx', 'Å—ÅÅ£', 'Å—ÅÅ£x']->matchfuzzy('Å—ÅÅ£')) endfunc +" Test for matchfuzzypos() with multibyte characters func Test_matchfuzzypos_mbyte() CheckFeature multi_lang call assert_equal([['ã“ã‚“ã«ã¡ã¯ä¸–界'], [[0, 1, 2, 3, 4]]], @@ -198,9 +224,13 @@ func Test_matchfuzzypos_mbyte() call assert_equal(range(256), x[1][0]) call assert_equal([[], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 257))) + " match multiple words (separated by space) + call assert_equal([['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€'], [[9, 10, 2, 3, 4]]], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('ë¼ì§€ 마리ì˜')) + call assert_equal([[], []], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('파란 하늘')) + " match in a long string - call assert_equal([[repeat('♪', 300) .. '✗✗✗'], [[300, 301, 302]]], - \ matchfuzzypos([repeat('♪', 300) .. '✗✗✗'], '✗✗✗')) + call assert_equal([[repeat('ã¶', 300) .. 'ẼẼẼ'], [[300, 301, 302]]], + \ matchfuzzypos([repeat('ã¶', 300) .. 'ẼẼẼ'], 'ẼẼẼ')) " preference for camel case match call assert_equal([['xѳѵÒxxѳѴÒ'], [[6, 7, 8]]], matchfuzzypos(['xѳѵÒxxѳѴÒ'], 'ѳѵÒ')) " preference for match after a separator (_ or space) -- cgit From 712c7e5d5fe8fbd93e47b8064a24ff9346720402 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 22:35:43 +0000 Subject: vim-patch:8.2.1921: fuzzy matching does not recognize path separators Problem: Fuzzy matching does not recognize path separators. Solution: Add a bonus for slash and backslash. (Yegappan Lakshmanan, closes vim/vim#7225) https://github.com/vim/vim/commit/dcdd42a8ccb9bafd857735d694b074269f337333 --- src/nvim/search.c | 15 +++++++++------ src/nvim/testdir/test_matchfuzzy.vim | 2 ++ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index a021466446..b08de3177e 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4823,8 +4823,10 @@ typedef struct { /// bonus for adjacent matches; this is higher than SEPARATOR_BONUS so that /// matching a whole word is preferred. #define SEQUENTIAL_BONUS 40 -/// bonus if match occurs after a separator -#define SEPARATOR_BONUS 30 +/// bonus if match occurs after a path separator +#define PATH_SEPARATOR_BONUS 30 +/// bonus if match occurs after a word separator +#define WORD_SEPARATOR_BONUS 25 /// bonus if match is uppercase and prev is lower #define CAMEL_BONUS 30 /// bonus if the first letter is matched @@ -4895,10 +4897,11 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, score += CAMEL_BONUS; } - // Separator - const bool neighborSeparator = neighbor == '_' || neighbor == ' '; - if (neighborSeparator) { - score += SEPARATOR_BONUS; + // Bonus if the match follows a separator character + if (neighbor == '/' || neighbor == '\\') { + score += PATH_SEPARATOR_BONUS; + } else if (neighbor == ' ' || neighbor == '_') { + score += WORD_SEPARATOR_BONUS; } } else { // First letter diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim index 28367b878d..d67d9bd893 100644 --- a/src/nvim/testdir/test_matchfuzzy.vim +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -45,6 +45,8 @@ func Test_matchfuzzy() call assert_equal(['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c'], ['.vim/vimrc', '.vim/vimrc_colors', '.vim/v_i_m_r_c']->matchfuzzy('vimrc')) " gap penalty call assert_equal(['xxayybxxxx', 'xxayyybxxx', 'xxayyyybxx'], ['xxayyyybxx', 'xxayyybxxx', 'xxayybxxxx']->matchfuzzy('ab')) + " path separator vs word separator + call assert_equal(['color/setup.vim', 'color\\setup.vim', 'color setup.vim', 'color_setup.vim', 'colorsetup.vim'], matchfuzzy(['colorsetup.vim', 'color setup.vim', 'color/setup.vim', 'color_setup.vim', 'color\\setup.vim'], 'setup.vim')) " match multiple words (separated by space) call assert_equal(['foo bar baz'], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzy('baz foo')) -- cgit From 715fbcbb8c6ec4385d1168b1260fdb991d4e6fc5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 22:40:42 +0000 Subject: vim-patch:8.2.2280: fuzzy matching doesn't give access to the scores Problem: Fuzzy matching doesn't give access to the scores. Solution: Return the scores with a third list. (Yegappan Lakshmanan, closes vim/vim#7596) https://github.com/vim/vim/commit/9d19e4f4ba55f8bef18d4991abdf740ff6472dba Remove seemingly useless NULL checks. assert that removing the li one wasn't dumb. --- src/nvim/search.c | 27 ++++++++--- src/nvim/testdir/test_matchfuzzy.vim | 88 ++++++++++++++++++------------------ 2 files changed, 64 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index b08de3177e..960c0e97c0 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -5173,10 +5173,10 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo // For matchfuzzy(), return a list of matched strings. // ['str1', 'str2', 'str3'] - // For matchfuzzypos(), return a list with two items. + // For matchfuzzypos(), return a list with three items. // The first item is a list of matched strings. The second item // is a list of lists where each list item is a list of matched - // character positions. + // character positions. The third item is a list of matching scores. // [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]] list_T *l; if (retmatchpos) { @@ -5197,7 +5197,7 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo // next copy the list of matching positions if (retmatchpos) { - const listitem_T *const li = tv_list_find(fmatchlist, -1); + const listitem_T *li = tv_list_find(fmatchlist, -2); assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); l = TV_LIST_ITEM_TV(li)->vval.v_list; for (i = 0; i < len; i++) { @@ -5206,6 +5206,17 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo } tv_list_append_list(l, ptrs[i].lmatchpos); } + + // copy the matching scores + li = tv_list_find(fmatchlist, -1); + assert(li != NULL && TV_LIST_ITEM_TV(li)->vval.v_list != NULL); + l = TV_LIST_ITEM_TV(li)->vval.v_list; + for (i = 0; i < len; i++) { + if (ptrs[i].score == SCORE_NONE) { + break; + } + tv_list_append_number(l, ptrs[i].score); + } } } xfree(ptrs); @@ -5257,11 +5268,13 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, } // get the fuzzy matches - tv_list_alloc_ret(rettv, retmatchpos ? 2 : kListLenUnknown); + tv_list_alloc_ret(rettv, retmatchpos ? 3 : kListLenUnknown); if (retmatchpos) { - // For matchfuzzypos(), a list with two items are returned. First item - // is a list of matching strings and the second item is a list of - // lists with matching positions within each string. + // For matchfuzzypos(), a list with three items are returned. First + // item is a list of matching strings, the second item is a list of + // lists with matching positions within each string and the third item + // is the list of scores of the matches. + tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); tv_list_append_list(rettv->vval.v_list, tv_list_alloc(kListLenUnknown)); } diff --git a/src/nvim/testdir/test_matchfuzzy.vim b/src/nvim/testdir/test_matchfuzzy.vim index d67d9bd893..abcc9b40c1 100644 --- a/src/nvim/testdir/test_matchfuzzy.vim +++ b/src/nvim/testdir/test_matchfuzzy.vim @@ -102,55 +102,55 @@ endfunc " Test for the matchfuzzypos() function func Test_matchfuzzypos() - call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'curl'], 'rl')) - call assert_equal([['curl', 'world'], [[2,3], [2,3]]], matchfuzzypos(['world', 'one', 'curl'], 'rl')) + call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'curl'], 'rl')) + call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], matchfuzzypos(['world', 'one', 'curl'], 'rl')) call assert_equal([['hello', 'hello world hello world'], - \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]], + \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [275, 257]], \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 'hello')) - call assert_equal([['aaaaaaa'], [[0, 1, 2]]], matchfuzzypos(['aaaaaaa'], 'aaa')) - call assert_equal([['a b'], [[0, 3]]], matchfuzzypos(['a b'], 'a b')) - call assert_equal([['a b'], [[0, 3]]], matchfuzzypos(['a b'], 'a b')) - call assert_equal([['a b'], [[0]]], matchfuzzypos(['a b'], ' a ')) - call assert_equal([[], []], matchfuzzypos(['a b'], ' ')) - call assert_equal([[], []], matchfuzzypos(['world', 'curl'], 'ab')) + call assert_equal([['aaaaaaa'], [[0, 1, 2]], [191]], matchfuzzypos(['aaaaaaa'], 'aaa')) + call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b')) + call assert_equal([['a b'], [[0, 3]], [219]], matchfuzzypos(['a b'], 'a b')) + call assert_equal([['a b'], [[0]], [112]], matchfuzzypos(['a b'], ' a ')) + call assert_equal([[], [], []], matchfuzzypos(['a b'], ' ')) + call assert_equal([[], [], []], matchfuzzypos(['world', 'curl'], 'ab')) let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256)) call assert_equal(range(256), x[1][0]) - call assert_equal([[], []], matchfuzzypos([repeat('a', 300)], repeat('a', 257))) - call assert_equal([[], []], matchfuzzypos([], 'abc')) + call assert_equal([[], [], []], matchfuzzypos([repeat('a', 300)], repeat('a', 257))) + call assert_equal([[], [], []], matchfuzzypos([], 'abc')) " match in a long string - call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]]], + call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]], [-135]], \ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc')) " preference for camel case match - call assert_equal([['xabcxxaBc'], [[6, 7, 8]]], matchfuzzypos(['xabcxxaBc'], 'abc')) + call assert_equal([['xabcxxaBc'], [[6, 7, 8]], [189]], matchfuzzypos(['xabcxxaBc'], 'abc')) " preference for match after a separator (_ or space) - call assert_equal([['xabx_ab'], [[5, 6]]], matchfuzzypos(['xabx_ab'], 'ab')) + call assert_equal([['xabx_ab'], [[5, 6]], [145]], matchfuzzypos(['xabx_ab'], 'ab')) " preference for leading letter match - call assert_equal([['abcxabc'], [[0, 1]]], matchfuzzypos(['abcxabc'], 'ab')) + call assert_equal([['abcxabc'], [[0, 1]], [150]], matchfuzzypos(['abcxabc'], 'ab')) " preference for sequential match - call assert_equal([['aobncedone'], [[7, 8, 9]]], matchfuzzypos(['aobncedone'], 'one')) + call assert_equal([['aobncedone'], [[7, 8, 9]], [158]], matchfuzzypos(['aobncedone'], 'one')) " best recursive match - call assert_equal([['xoone'], [[2, 3, 4]]], matchfuzzypos(['xoone'], 'one')) + call assert_equal([['xoone'], [[2, 3, 4]], [168]], matchfuzzypos(['xoone'], 'one')) " match multiple words (separated by space) - call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo')) - call assert_equal([[], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('one two')) - call assert_equal([[], []], ['foo bar']->matchfuzzypos(" \t ")) - call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]]], ['grace']->matchfuzzypos('race ace grace')) + call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]], [369]], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo')) + call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('one two')) + call assert_equal([[], [], []], ['foo bar']->matchfuzzypos(" \t ")) + call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]], [657]], ['grace']->matchfuzzypos('race ace grace')) let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}] - call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]], + call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]], \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}})) - call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]], + call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]], \ matchfuzzypos(l, 'cam', {'key' : 'val'})) - call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> v.val}})) - call assert_equal([[], []], matchfuzzypos(l, 'day', {'key' : 'val'})) + call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> v.val}})) + call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'key' : 'val'})) call assert_fails("let x = matchfuzzypos(l, 'cam', 'random')", 'E715:') - call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> []}})) - call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 1}})) + call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> []}})) + call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 1}})) call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 1}})", 'E119:') - call assert_equal([[], []], matchfuzzypos(l, 'cam')) + call assert_equal([[], [], []], matchfuzzypos(l, 'cam')) " Nvim's callback implementation is different, so E6000 is expected instead, " but we need v8.2.1183 to assert it " call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 'E921:') @@ -208,41 +208,41 @@ endfunc " Test for matchfuzzypos() with multibyte characters func Test_matchfuzzypos_mbyte() CheckFeature multi_lang - call assert_equal([['ã“ã‚“ã«ã¡ã¯ä¸–界'], [[0, 1, 2, 3, 4]]], + call assert_equal([['ã“ã‚“ã«ã¡ã¯ä¸–界'], [[0, 1, 2, 3, 4]], [273]], \ matchfuzzypos(['ã“ã‚“ã«ã¡ã¯ä¸–界'], 'ã“ã‚“ã«ã¡ã¯')) - call assert_equal([['ンヹㄇヺヴ'], [[1, 3]]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ')) + call assert_equal([['ンヹㄇヺヴ'], [[1, 3]], [88]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ')) " reverse the order of characters - call assert_equal([[], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ')) - call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]]], + call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ')) + call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]], [222, 113]], \ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ')) call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'Ï€bÏ€'], - \ [[0, 1], [0, 1], [0, 1], [0, 2]]], + \ [[0, 1], [0, 1], [0, 1], [0, 2]], [151, 148, 145, 110]], \ matchfuzzypos(['Ï€bÏ€', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ')) - call assert_equal([['ααααααα'], [[0, 1, 2]]], + call assert_equal([['ααααααα'], [[0, 1, 2]], [191]], \ matchfuzzypos(['ααααααα'], 'ααα')) - call assert_equal([[], []], matchfuzzypos(['ンヹㄇ', 'Å—ÅÅ£'], 'ffï¬ï¬‚')) + call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇ', 'Å—ÅÅ£'], 'ffï¬ï¬‚')) let x = matchfuzzypos([repeat('Ψ', 256)], repeat('Ψ', 256)) call assert_equal(range(256), x[1][0]) - call assert_equal([[], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 257))) + call assert_equal([[], [], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 257))) " match multiple words (separated by space) - call assert_equal([['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€'], [[9, 10, 2, 3, 4]]], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('ë¼ì§€ 마리ì˜')) - call assert_equal([[], []], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('파란 하늘')) + call assert_equal([['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€'], [[9, 10, 2, 3, 4]], [328]], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('ë¼ì§€ 마리ì˜')) + call assert_equal([[], [], []], ['세 ë§ˆë¦¬ì˜ ìž‘ì€ ë¼ì§€', '마리ì˜', 'ë§ˆë¦¬ì˜ ìž‘ì€', 'ìž‘ì€ ë¼ì§€']->matchfuzzypos('파란 하늘')) " match in a long string - call assert_equal([[repeat('ã¶', 300) .. 'ẼẼẼ'], [[300, 301, 302]]], + call assert_equal([[repeat('ã¶', 300) .. 'ẼẼẼ'], [[300, 301, 302]], [-135]], \ matchfuzzypos([repeat('ã¶', 300) .. 'ẼẼẼ'], 'ẼẼẼ')) " preference for camel case match - call assert_equal([['xѳѵÒxxѳѴÒ'], [[6, 7, 8]]], matchfuzzypos(['xѳѵÒxxѳѴÒ'], 'ѳѵÒ')) + call assert_equal([['xѳѵÒxxѳѴÒ'], [[6, 7, 8]], [189]], matchfuzzypos(['xѳѵÒxxѳѴÒ'], 'ѳѵÒ')) " preference for match after a separator (_ or space) - call assert_equal([['xã¡ã x_ã¡ã '], [[5, 6]]], matchfuzzypos(['xã¡ã x_ã¡ã '], 'ã¡ã ')) + call assert_equal([['xã¡ã x_ã¡ã '], [[5, 6]], [145]], matchfuzzypos(['xã¡ã x_ã¡ã '], 'ã¡ã ')) " preference for leading letter match - call assert_equal([['ѳѵÒxѳѵÒ'], [[0, 1]]], matchfuzzypos(['ѳѵÒxѳѵÒ'], 'ѳѵ')) + call assert_equal([['ѳѵÒxѳѵÒ'], [[0, 1]], [150]], matchfuzzypos(['ѳѵÒxѳѵÒ'], 'ѳѵ')) " preference for sequential match - call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]]], matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ')) + call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]], [158]], matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ')) " best recursive match - call assert_equal([['xффйд'], [[2, 3, 4]]], matchfuzzypos(['xффйд'], 'фйд')) + call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 'фйд')) endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From ce797e08f539dc24d16ea8c02591296bbbc83772 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 1 Jan 2022 23:06:09 +0000 Subject: vim-patch:8.2.2813: cannot grep using fuzzy matching Problem: Cannot grep using fuzzy matching. Solution: Add the "f" flag to :vimgrep. (Yegappan Lakshmanan, closes vim/vim#8152) https://github.com/vim/vim/commit/bb01a1ef3a093cdb36877ba73474719c531dc8cb --- src/nvim/ex_cmds.c | 6 +- src/nvim/quickfix.c | 121 +++++++++++++++++++++++++------------ src/nvim/quickfix.h | 1 + src/nvim/search.c | 43 ++++++------- src/nvim/search.h | 3 + src/nvim/testdir/test_quickfix.vim | 50 ++++++++++++++- 6 files changed, 158 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 3b3d4e50cc..81fce3565a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6141,12 +6141,14 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) p++; // Find the flags - while (*p == 'g' || *p == 'j') { + while (*p == 'g' || *p == 'j' || *p == 'f') { if (flags != NULL) { if (*p == 'g') { *flags |= VGR_GLOBAL; - } else { + } else if (*p == 'j') { *flags |= VGR_NOJUMP; + } else { + *flags |= VGR_FUZZY; } } p++; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 0196e05455..c609daa55c 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -5194,49 +5194,93 @@ static bool vgr_qflist_valid(win_T *wp, qf_info_T *qi, unsigned qfid, char_u *ti /// Search for a pattern in all the lines in a buffer and add the matching lines /// to a quickfix list. -static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, regmmatch_T *regmatch, - long *tomatch, int duplicate_name, int flags) - FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5) +static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, char_u *spat, + regmmatch_T *regmatch, long *tomatch, int duplicate_name, int flags) + FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5, 6) { bool found_match = false; for (long lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) { colnr_T col = 0; - while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL, - NULL) > 0) { - // Pass the buffer number so that it gets used even for a - // dummy buffer, unless duplicate_name is set, then the - // buffer will be wiped out below. - if (qf_add_entry(qfl, - NULL, // dir - fname, - NULL, - duplicate_name ? 0 : buf->b_fnum, - ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, - false), - regmatch->startpos[0].lnum + lnum, - regmatch->endpos[0].lnum + lnum, - regmatch->startpos[0].col + 1, - regmatch->endpos[0].col + 1, - false, // vis_col - NULL, // search pattern - 0, // nr - 0, // type - true) // valid - == QF_FAIL) { - got_int = true; - break; - } - found_match = true; - if (--*tomatch == 0) { - break; - } - if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) { - break; + if (!(flags & VGR_FUZZY)) { + // Regular expression match + while (vim_regexec_multi(regmatch, curwin, buf, lnum, col, NULL, NULL) > 0) { + // Pass the buffer number so that it gets used even for a + // dummy buffer, unless duplicate_name is set, then the + // buffer will be wiped out below. + if (qf_add_entry(qfl, + NULL, // dir + fname, + NULL, + duplicate_name ? 0 : buf->b_fnum, + ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, false), + regmatch->startpos[0].lnum + lnum, + regmatch->endpos[0].lnum + lnum, + regmatch->startpos[0].col + 1, + regmatch->endpos[0].col + 1, + false, // vis_col + NULL, // search pattern + 0, // nr + 0, // type + true) // valid + == QF_FAIL) { + got_int = true; + break; + } + found_match = true; + if (--*tomatch == 0) { + break; + } + if ((flags & VGR_GLOBAL) == 0 || regmatch->endpos[0].lnum > 0) { + break; + } + col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); + if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) { + break; + } } - col = regmatch->endpos[0].col + (col == regmatch->endpos[0].col); - if (col > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) { - break; + } else { + const size_t pat_len = STRLEN(spat); + char_u *const str = ml_get_buf(buf, lnum, false); + int score; + uint32_t matches[MAX_FUZZY_MATCHES]; + const size_t sz = sizeof(matches) / sizeof(matches[0]); + + // Fuzzy string match + while (fuzzy_match(str + col, spat, false, &score, matches, (int)sz) > 0) { + // Pass the buffer number so that it gets used even for a + // dummy buffer, unless duplicate_name is set, then the + // buffer will be wiped out below. + if (qf_add_entry(qfl, + NULL, // dir + fname, + NULL, + duplicate_name ? 0 : buf->b_fnum, + str, + lnum, + 0, + (colnr_T)matches[0] + col + 1, + 0, + false, // vis_col + NULL, // search pattern + 0, // nr + 0, // type + true) // valid + == QF_FAIL) { + got_int = true; + break; + } + found_match = true; + if (--*tomatch == 0) { + break; + } + if ((flags & VGR_GLOBAL) == 0) { + break; + } + col = (colnr_T)matches[pat_len - 1] + col + 1; + if (col > (colnr_T)STRLEN(str)) { + break; + } } } line_breakcheck(); @@ -5418,8 +5462,7 @@ void ex_vimgrep(exarg_T *eap) } else { // Try for a match in all lines of the buffer. // For ":1vimgrep" look for first match only. - found_match = vgr_match_buflines(qf_get_curlist(qi), - fname, buf, ®match, &tomatch, + found_match = vgr_match_buflines(qf_get_curlist(qi), fname, buf, s, ®match, &tomatch, duplicate_name, flags); if (using_dummy) { diff --git a/src/nvim/quickfix.h b/src/nvim/quickfix.h index f5178e332a..0da43e436c 100644 --- a/src/nvim/quickfix.h +++ b/src/nvim/quickfix.h @@ -7,6 +7,7 @@ // flags for skip_vimgrep_pat() #define VGR_GLOBAL 1 #define VGR_NOJUMP 2 +#define VGR_FUZZY 4 #ifdef INCLUDE_GENERATED_DECLARATIONS # include "quickfix.h.generated.h" diff --git a/src/nvim/search.c b/src/nvim/search.c index 960c0e97c0..3a2435e07a 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -48,8 +48,6 @@ #include "nvim/vim.h" #include "nvim/window.h" -typedef uint32_t matchidx_T; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "search.c.generated.h" #endif @@ -4843,13 +4841,11 @@ typedef struct { #define SCORE_NONE -9999 #define FUZZY_MATCH_RECURSION_LIMIT 10 -/// Maximum number of characters that can be fuzzy matched -#define MAXMATCHES 256 /// Compute a score for a fuzzy matched string. The matching character locations /// are in 'matches'. static int fuzzy_match_compute_score(const char_u *const str, const int strSz, - const matchidx_T *const matches, const int numMatches) + const uint32_t *const matches, const int numMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { // Initialize score @@ -4868,10 +4864,10 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, // Apply ordering bonuses for (int i = 0; i < numMatches; i++) { - const matchidx_T currIdx = matches[i]; + const uint32_t currIdx = matches[i]; if (i > 0) { - const matchidx_T prevIdx = matches[i - 1]; + const uint32_t prevIdx = matches[i - 1]; // Sequential if (currIdx == prevIdx + 1) { @@ -4887,7 +4883,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, const char_u *p = str; int neighbor; - for (matchidx_T sidx = 0; sidx < currIdx; sidx++) { + for (uint32_t sidx = 0; sidx < currIdx; sidx++) { neighbor = utf_ptr2char(p); MB_PTR_ADV(p); } @@ -4913,16 +4909,16 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, /// Perform a recursive search for fuzzy matching 'fuzpat' in 'str'. /// @return the number of matching characters. -static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchidx_T strIdx, +static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, uint32_t strIdx, int *const outScore, const char_u *const strBegin, - const int strLen, const matchidx_T *const srcMatches, - matchidx_T *const matches, const int maxMatches, int nextMatch, + const int strLen, const uint32_t *const srcMatches, + uint32_t *const matches, const int maxMatches, int nextMatch, int *const recursionCount) FUNC_ATTR_NONNULL_ARG(1, 2, 4, 5, 8, 11) FUNC_ATTR_WARN_UNUSED_RESULT { // Recursion params bool recursiveMatch = false; - matchidx_T bestRecursiveMatches[MAXMATCHES]; + uint32_t bestRecursiveMatches[MAX_FUZZY_MATCHES]; int bestRecursiveScore = 0; // Count recursions @@ -4932,7 +4928,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchi } // Detect end of strings - if (*fuzpat == '\0' || *str == '\0') { + if (*fuzpat == NUL || *str == NUL) { return 0; } @@ -4956,7 +4952,7 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchi } // Recursive call that "skips" this match - matchidx_T recursiveMatches[MAXMATCHES]; + uint32_t recursiveMatches[MAX_FUZZY_MATCHES]; int recursiveScore = 0; const char_u *const next_char = str + utfc_ptr2len(str); if (fuzzy_match_recursive(fuzpat, next_char, strIdx + 1, &recursiveScore, strBegin, strLen, @@ -4965,7 +4961,8 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchi recursionCount)) { // Pick best recursive score if (!recursiveMatch || recursiveScore > bestRecursiveScore) { - memcpy(bestRecursiveMatches, recursiveMatches, MAXMATCHES * sizeof(recursiveMatches[0])); + memcpy(bestRecursiveMatches, recursiveMatches, + MAX_FUZZY_MATCHES * sizeof(recursiveMatches[0])); bestRecursiveScore = recursiveScore; } recursiveMatch = true; @@ -5008,13 +5005,13 @@ static int fuzzy_match_recursive(const char_u *fuzpat, const char_u *str, matchi /// normalized and varies with pattern. /// Recursion is limited internally (default=10) to prevent degenerate cases /// (pat_arg="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"). -/// Uses char_u for match indices. Therefore patterns are limited to MAXMATCHES -/// characters. +/// Uses char_u for match indices. Therefore patterns are limited to +/// MAX_FUZZY_MATCHES characters. /// /// @return true if 'pat_arg' matches 'str'. Also returns the match score in /// 'outScore' and the matching character positions in 'matches'. -static bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq, - int *const outScore, matchidx_T *const matches, const int maxMatches) +bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matchseq, + int *const outScore, uint32_t *const matches, const int maxMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { const int len = mb_charlen(str); @@ -5108,7 +5105,7 @@ static void fuzzy_match_in_list(list_T *const items, char_u *const str, const bo fuzzyItem_T *const ptrs = xcalloc(len, sizeof(fuzzyItem_T)); long i = 0; bool found_match = false; - matchidx_T matches[MAXMATCHES]; + uint32_t matches[MAX_FUZZY_MATCHES]; // For all the string items in items, get the fuzzy matching score TV_LIST_ITER(items, li, { @@ -5250,8 +5247,8 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, // To search a dict, either a callback function or a key can be // specified. dict_T *const d = argvars[2].vval.v_dict; - const dictitem_T *di; - if ((di = tv_dict_find(d, "key", -1)) != NULL) { + const dictitem_T *const di = tv_dict_find(d, "key", -1); + if (di != NULL) { if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL || *di->di_tv.vval.v_string == NUL) { semsg(_(e_invarg2), tv_get_string(&di->di_tv)); @@ -5262,7 +5259,7 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv, semsg(_(e_invargval), "text_cb"); return; } - if ((di = tv_dict_find(d, "matchseq", -1)) != NULL) { + if (tv_dict_find(d, "matchseq", -1) != NULL) { matchseq = true; } } diff --git a/src/nvim/search.h b/src/nvim/search.h index 15b8d41f39..53059cc1ea 100644 --- a/src/nvim/search.h +++ b/src/nvim/search.h @@ -55,6 +55,9 @@ #define SEARCH_STAT_DEF_MAX_COUNT 99 #define SEARCH_STAT_BUF_LEN 12 +/// Maximum number of characters that can be fuzzy matched +#define MAX_FUZZY_MATCHES 256 + /// Structure containing offset definition for the last search pattern /// /// @note Only offset for the last search pattern is used, not for the last diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index f137ed5346..00679e1958 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -32,7 +32,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xnfile cnfile command! -nargs=* -bang Xpfile cpfile command! -nargs=* Xexpr cexpr - command! -count -nargs=* Xvimgrep vimgrep + command! -count=999 -nargs=* Xvimgrep vimgrep command! -nargs=* Xvimgrepadd vimgrepadd command! -nargs=* Xgrep grep command! -nargs=* Xgrepadd grepadd @@ -69,7 +69,7 @@ func s:setup_commands(cchar) command! -count -nargs=* -bang Xnfile lnfile command! -nargs=* -bang Xpfile lpfile command! -nargs=* Xexpr lexpr - command! -count -nargs=* Xvimgrep lvimgrep + command! -count=999 -nargs=* Xvimgrep lvimgrep command! -nargs=* Xvimgrepadd lvimgrepadd command! -nargs=* Xgrep lgrep command! -nargs=* Xgrepadd lgrepadd @@ -5028,6 +5028,52 @@ func Test_qfbuf_update() call Xqfbuf_update('l') endfunc +" Test for the :vimgrep 'f' flag (fuzzy match) +func Xvimgrep_fuzzy_match(cchar) + call s:setup_commands(a:cchar) + + Xvimgrep /three one/f Xfile* + let l = g:Xgetlist() + call assert_equal(2, len(l)) + call assert_equal(['Xfile1', 1, 9, 'one two three'], + \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text]) + call assert_equal(['Xfile2', 2, 1, 'three one two'], + \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text]) + + Xvimgrep /the/f Xfile* + let l = g:Xgetlist() + call assert_equal(3, len(l)) + call assert_equal(['Xfile1', 1, 9, 'one two three'], + \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text]) + call assert_equal(['Xfile2', 2, 1, 'three one two'], + \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text]) + call assert_equal(['Xfile2', 4, 4, 'aaathreeaaa'], + \ [bufname(l[2].bufnr), l[2].lnum, l[2].col, l[2].text]) + + Xvimgrep /aaa/fg Xfile* + let l = g:Xgetlist() + call assert_equal(4, len(l)) + call assert_equal(['Xfile1', 2, 1, 'aaaaaa'], + \ [bufname(l[0].bufnr), l[0].lnum, l[0].col, l[0].text]) + call assert_equal(['Xfile1', 2, 4, 'aaaaaa'], + \ [bufname(l[1].bufnr), l[1].lnum, l[1].col, l[1].text]) + call assert_equal(['Xfile2', 4, 1, 'aaathreeaaa'], + \ [bufname(l[2].bufnr), l[2].lnum, l[2].col, l[2].text]) + call assert_equal(['Xfile2', 4, 9, 'aaathreeaaa'], + \ [bufname(l[3].bufnr), l[3].lnum, l[3].col, l[3].text]) + + call assert_fails('Xvimgrep /xyz/fg Xfile*', 'E480:') +endfunc + +func Test_vimgrep_fuzzy_match() + call writefile(['one two three', 'aaaaaa'], 'Xfile1') + call writefile(['one', 'three one two', 'two', 'aaathreeaaa'], 'Xfile2') + call Xvimgrep_fuzzy_match('c') + call Xvimgrep_fuzzy_match('l') + call delete('Xfile1') + call delete('Xfile2') +endfunc + " Test for getting a specific item from a quickfix list func Xtest_getqflist_by_idx(cchar) call s:setup_commands(a:cchar) -- cgit From 64116d78502e0ca611e13adf9323ef2d3fe708c2 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 8 Feb 2022 01:19:06 +0100 Subject: chore: fix typos (#17250) Co-authored-by: zeertzjq Co-authored-by: Dani Dickstein Co-authored-by: Axel Dahlberg --- src/nvim/getchar.h | 2 +- src/nvim/testdir/test_breakindent.vim | 2 +- src/nvim/tui/input.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index be10e150e5..f24a4e7c7c 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -55,7 +55,7 @@ struct map_arguments { char_u *orig_rhs; /// The original text of the {rhs}. size_t orig_rhs_len; - char *desc; /// map escription + char *desc; /// map description }; typedef struct map_arguments MapArguments; #define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \ diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 8d592f21ea..b619f2adb6 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -20,7 +20,7 @@ func s:screen_lines2(lnums, lnume, width) abort return ScreenLines([a:lnums, a:lnume], a:width) endfunc -func! s:compare_lines(expect, actual) +func s:compare_lines(expect, actual) call assert_equal(join(a:expect, "\n"), join(a:actual, "\n")) endfunc diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 6e48df3734..6b889cf97c 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -227,7 +227,7 @@ static void forward_modified_utf8(TermInput *input, TermKeyKey *key) && !(key->modifiers & TERMKEY_KEYMOD_SHIFT) && ASCII_ISUPPER(key->code.codepoint)) { assert(len <= 62); - // Make remove for the S- + // Make room for the S- memmove(buf + 3, buf + 1, len - 1); buf[1] = 'S'; buf[2] = '-'; -- cgit From 68603998b91a7fbf9a6cb415a51526fbd88b6581 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 8 Feb 2022 14:47:23 +0800 Subject: test: add Lua functional tests for Ex mode --- src/nvim/testdir/test_ex_mode.vim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index 78663f7deb..dcec5f7cc6 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -29,12 +29,11 @@ endfunc " Test editing line in Ex mode (both Q and gQ) func Test_ex_mode() - throw 'skipped: TODO: ' + throw 'Skipped: Nvim only supports Vim Ex mode' let encoding_save = &encoding set sw=2 - " for e in ['utf8', 'latin1'] - for e in ['utf8'] + for e in ['utf8', 'latin1'] exe 'set encoding=' . e call assert_equal(['bar', 'bar'], Ex("foo bar\bar"), e) -- cgit From f8b40694b1139d01a7b6b1e5923b18198a504bf8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 8 Feb 2022 15:09:54 +0800 Subject: vim-patch:8.2.0197: some Ex commands not sufficiently tested Problem: Some Ex commands not sufficiently tested. Solution: Add more tests. (Yegappan Lakshmanan, closes vim/vim#5565) https://github.com/vim/vim/commit/ea3db914c0fa35797ad73f6d5bb3a4288d690065 --- src/nvim/testdir/test_global.vim | 30 +++++++++++++++++++ src/nvim/testdir/test_help.vim | 12 ++++++++ src/nvim/testdir/test_help_tagjump.vim | 43 ++++++++++++++++++++++++++ src/nvim/testdir/test_options.vim | 13 ++++++++ src/nvim/testdir/test_substitute.vim | 55 ++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_textformat.vim | 15 ++++++++++ src/nvim/testdir/test_writefile.vim | 4 +-- 7 files changed, 169 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim index 8edc9c2608..ad561baf4a 100644 --- a/src/nvim/testdir/test_global.vim +++ b/src/nvim/testdir/test_global.vim @@ -36,6 +36,36 @@ func Test_global_error() call assert_fails('g/\(/y', 'E476:') endfunc +" Test for printing lines using :g with different search patterns +func Test_global_print() + new + call setline(1, ['foo', 'bar', 'foo', 'foo']) + let @/ = 'foo' + let t = execute("g/")->trim()->split("\n") + call assert_equal(['foo', 'foo', 'foo'], t) + + " Test for Vi compatible patterns + let @/ = 'bar' + let t = execute('g\/')->trim()->split("\n") + call assert_equal(['bar'], t) + + normal gg + s/foo/foo/ + let t = execute('g\&')->trim()->split("\n") + call assert_equal(['foo', 'foo', 'foo'], t) + + let @/ = 'bar' + let t = execute('g?')->trim()->split("\n") + call assert_equal(['bar'], t) + + " Test for the 'Pattern found in every line' message + let v:statusmsg = '' + v/foo\|bar/p + call assert_notequal('', v:statusmsg) + + close! +endfunc + func Test_wrong_delimiter() call assert_fails('g x^bxd', 'E146:') endfunc diff --git a/src/nvim/testdir/test_help.vim b/src/nvim/testdir/test_help.vim index e91dea1040..b2d943be00 100644 --- a/src/nvim/testdir/test_help.vim +++ b/src/nvim/testdir/test_help.vim @@ -12,6 +12,18 @@ endfunc func Test_help_errors() call assert_fails('help doesnotexist', 'E149:') call assert_fails('help!', 'E478:') + if has('multi_lang') + call assert_fails('help help@xy', 'E661:') + endif + + let save_hf = &helpfile + set helpfile=help_missing + help + call assert_equal(1, winnr('$')) + call assert_notequal('help', &buftype) + let &helpfile = save_hf + + call assert_fails('help ' . repeat('a', 1048), 'E149:') new set keywordprg=:help diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index a6494c531c..a43889b57e 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -23,6 +23,11 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*bar\*') helpclose + help " + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*quote\*') + helpclose + help "* call assert_equal("help", &filetype) call assert_true(getline('.') =~ '\*quotestar\*') @@ -86,11 +91,40 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*i_^_CTRL-D\*') helpclose + help i^x^y + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*i_CTRL-X_CTRL-Y\*') + helpclose + + exe "help i\\" + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*i_CTRL-\\_CTRL-G\*') + helpclose + exec "help \" call assert_equal("help", &filetype) call assert_true(getline('.') =~ '\*CTRL-V\*') helpclose + help /\| + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*/\\bar\*') + helpclose + + help CTRL-\_CTRL-N + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*CTRL-\\_CTRL-N\*') + helpclose + + help `:pwd`, + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:pwd\*') + helpclose + + help `:ls`. + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:ls\*') + helpclose exec "help! ('textwidth'" call assert_equal("help", &filetype) @@ -122,6 +156,15 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*{address}\*') helpclose + " Use special patterns in the help tag + for h in ['/\w', '/\%^', '/\%(', '/\zs', '/\@<=', '/\_$', '[++opt]', '/\{'] + exec "help! " . h + call assert_equal("help", &filetype) + let pat = '\*' . escape(h, '\$[') . '\*' + call assert_true(getline('.') =~ pat, pat) + helpclose + endfor + exusage call assert_equal("help", &filetype) call assert_true(getline('.') =~ '\*:index\*') diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index a5adb5ff16..7d1fed3b94 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -656,6 +656,19 @@ func Test_buftype() close! endfunc +" Test for the 'shellquote' option +func Test_shellquote() + CheckUnix + set shellquote=# + set verbose=20 + redir => v + silent! !echo Hello + redir END + set verbose& + set shellquote& + call assert_match(': "#echo Hello#"', v) +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index ecd980472a..dbb792d2b0 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -51,10 +51,12 @@ func Test_substitute_variants() \ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' }, \ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' }, \ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' }, + \ { 'cmd': ':s/t/r/c', 'exp': 'Testing string', 'prompt': 'n' }, \ { 'cmd': ':s/t/r/cn', 'exp': ln }, \ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' }, \ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' }, \ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' }, + \ { 'cmd': ':s/i/I/gc', 'exp': 'TestIng string', 'prompt': 'l' }, \ { 'cmd': ':s/foo/bar/ge', 'exp': ln }, \ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' }, \ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' }, @@ -86,6 +88,7 @@ func Test_substitute_variants() \ { 'cmd': ':s//r/rp', 'exp': 'Testr string' }, \ { 'cmd': ':s//r/rl', 'exp': 'Testr string' }, \ { 'cmd': ':s//r/r', 'exp': 'Testr string' }, + \ { 'cmd': ':s/i/I/gc', 'exp': 'Testing string', 'prompt': 'q' }, \] for var in variants @@ -384,6 +387,10 @@ func Test_substitute_join() call assert_equal(["foo\tbarbar\foo"], getline(1, '$')) call assert_equal('\n', histget("search", -1)) + call setline(1, ['foo', 'bar', 'baz', 'qux']) + call execute('1,2s/\n//') + call assert_equal(['foobarbaz', 'qux'], getline(1, '$')) + bwipe! endfunc @@ -398,6 +405,11 @@ func Test_substitute_count() call assert_fails('s/foo/bar/0', 'E939:') + call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo']) + 2,4s/foo/bar/ 10 + call assert_equal(['foo foo', 'foo foo', 'foo foo', 'bar foo', 'bar foo'], + \ getline(1, '$')) + bwipe! endfunc @@ -416,6 +428,10 @@ func Test_substitute_flag_n() " No substitution should have been done. call assert_equal(lines, getline(1, '$')) + %delete _ + call setline(1, ['A', 'Bar', 'Baz']) + call assert_equal("\n1 match on 1 line", execute('s/\nB\@=//gn')) + bwipe! endfunc @@ -749,6 +765,45 @@ func Test_sub_beyond_end() bwipe! endfunc +" Test for repeating last substitution using :~ and :&r +func Test_repeat_last_sub() + new + call setline(1, ['blue green yellow orange white']) + s/blue/red/ + let @/ = 'yellow' + ~ + let @/ = 'white' + :&r + let @/ = 'green' + s//gray + call assert_equal('red gray red orange red', getline(1)) + close! +endfunc + +" Test for Vi compatible substitution: +" \/{string}/, \?{string}? and \&{string}& +func Test_sub_vi_compatibility() + new + call setline(1, ['blue green yellow orange blue']) + let @/ = 'orange' + s\/white/ + let @/ = 'blue' + s\?amber? + let @/ = 'white' + s\&green& + call assert_equal('amber green yellow white green', getline(1)) + close! +endfunc + +" Test for substitute with the new text longer than the original text +func Test_sub_expand_text() + new + call setline(1, 'abcabcabcabcabcabcabcabc') + s/b/\=repeat('B', 10)/g + call assert_equal(repeat('aBBBBBBBBBBc', 8), getline(1)) + close! +endfunc + func Test_submatch_list_concatenate() let pat = 'A\(.\)' let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])} diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 052c32214d..422daa912b 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -531,6 +531,21 @@ func Test_format_align() call assert_equal("\t\t Vim", getline(1)) q! + " align text with 'rightleft' + if has('rightleft') + new + call setline(1, 'Vim') + setlocal rightleft + left 20 + setlocal norightleft + call assert_equal("\t\t Vim", getline(1)) + setlocal rightleft + right + setlocal norightleft + call assert_equal("Vim", getline(1)) + close! + endif + set tw& endfunc diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 5ffbe82082..1d9fc6e3f7 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -169,9 +169,7 @@ endfunc " Test for ':w !' to pipe lines from the current buffer to an external " command. func Test_write_pipe_to_cmd() - if !has('unix') - return - endif + CheckUnix new call setline(1, ['L1', 'L2', 'L3', 'L4']) 2,3w !cat > Xfile -- cgit From 7813b48645bf2af11c2d18f4e4154a74d4dad662 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Tue, 8 Feb 2022 10:50:17 +0100 Subject: feat(term): use vterm_output_set_callback() --- src/nvim/channel.c | 1 - src/nvim/terminal.c | 20 ++++++-------------- 2 files changed, 6 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/channel.c b/src/nvim/channel.c index cd5134fe5f..d79c0acc4a 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -613,7 +613,6 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, size_ } else { if (chan->term) { terminal_receive(chan->term, ptr, count); - terminal_flush_output(chan->term); } rbuffer_consumed(buf, count); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index a2d855244c..1c26e46a21 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -172,6 +172,11 @@ void terminal_teardown(void) pmap_init(ptr_t, &invalidated_terminals); } +static void term_output_callback(const char *s, size_t len, void *user_data) +{ + terminal_send((Terminal *)user_data, (char *)s, len); +} + // public API {{{ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) @@ -195,6 +200,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv); vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL); vterm_screen_reset(rv->vts, 1); + vterm_output_set_callback(rv->vt, term_output_callback, rv); // force a initial refresh of the screen to ensure the buffer will always // have as many lines as screen rows when refresh_scrollback is called rv->invalid_start = 0; @@ -636,7 +642,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size) return; } vterm_keyboard_start_paste(curbuf->terminal->vt); - terminal_flush_output(curbuf->terminal); size_t buff_len = STRLEN(y_array[0]); char_u *buff = xmalloc(buff_len); for (int i = 0; i < count; i++) { // -V756 @@ -667,14 +672,6 @@ void terminal_paste(long count, char_u **y_array, size_t y_size) } xfree(buff); vterm_keyboard_end_paste(curbuf->terminal->vt); - terminal_flush_output(curbuf->terminal); -} - -void terminal_flush_output(Terminal *term) -{ - size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); - terminal_send(term, term->textbuf, len); } void terminal_send_key(Terminal *term, int c) @@ -693,8 +690,6 @@ void terminal_send_key(Terminal *term, int c) } else { vterm_keyboard_unichar(term->vt, (uint32_t)c, mod); } - - terminal_flush_output(term); } void terminal_receive(Terminal *term, char *data, size_t len) @@ -1265,9 +1260,6 @@ static bool send_mouse_event(Terminal *term, int c) } mouse_action(term, button, row, col - offset, pressed, 0); - size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); - terminal_send(term, term->textbuf, len); return false; } -- cgit From 3c75e63bf68f6ee88d1eda31caf042ed132badd9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 8 Feb 2022 21:10:32 +0800 Subject: vim-patch:8.2.4326: "o" and "O" copying comment not sufficiently tested Problem: "o" and "O" copying comment not sufficiently tested. Solution: Add a test case. (closes vim/vim#9718) https://github.com/vim/vim/commit/51ab7c7d0da08aac796acff22a6c075dac579e76 Fix a mistake when porting Vim patch 8.2.3934 --- src/nvim/change.c | 6 +++--- src/nvim/testdir/test_textformat.vim | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/change.c b/src/nvim/change.c index 54c4ba5319..6ac759d5e0 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1201,10 +1201,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Find out if the current line starts with a comment leader. // This may then be inserted in front of the new line. end_comment_pending = NUL; - if (flags & OPENLINE_DO_COM && dir == FORWARD) { - // Check for a line comment after code. + if (flags & OPENLINE_DO_COM) { lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); - if (lead_len == 0 && do_cindent) { + if (lead_len == 0 && do_cindent && dir == FORWARD) { + // Check for a line comment after code. comment_start = check_linecomment(saved_line); if (comment_start != MAXCOL) { lead_len = get_leader_len(saved_line + comment_start, diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index 052c32214d..783793984d 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -238,7 +238,33 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) - " Using "o" repeats the line comment, "O" does not. + " Using either "o" or "O" repeats a line comment occupying a whole line. + %del + let text =<< trim END + nop; + // This is a comment + val = val; + END + call setline(1, text) + normal 2Go + let expected =<< trim END + nop; + // This is a comment + // + val = val; + END + call assert_equal(expected, getline(1, '$')) + normal 2GO + let expected =<< trim END + nop; + // + // This is a comment + // + val = val; + END + call assert_equal(expected, getline(1, '$')) + + " Using "o" repeats a line comment after a statement, "O" does not. %del let text =<< trim END nop; -- cgit From cfed5baa38c21deb7b50b62876fa94a50af42fcb Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Mon, 7 Feb 2022 10:38:46 +0100 Subject: refactor(PVS/V547): expression is always true/false --- src/nvim/autocmd.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 2e7f9c5136..dfba18b11d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -285,7 +285,7 @@ void aubuflocal_remove(buf_T *buf) } // Add an autocmd group name. -// Return its ID. Returns AUGROUP_ERROR (< 0) for error. +// Return its ID. static int au_new_group(char_u *name) { int i = au_find_group(name); @@ -379,10 +379,7 @@ void do_augroup(char_u *arg, int del_group) } else if (STRICMP(arg, "end") == 0) { // ":aug end": back to group 0 current_augroup = AUGROUP_DEFAULT; } else if (*arg) { // ":aug xxx": switch to group xxx - int i = au_new_group(arg); - if (i != AUGROUP_ERROR) { - current_augroup = i; - } + current_augroup = au_new_group(arg); } else { // ":aug": list the group names msg_start(); for (int i = 0; i < augroups.ga_len; i++) { -- cgit From ef819fc05298e4a43fb0a5c7824a429677041e6b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 06:34:21 +0800 Subject: vim-patch:8.1.2297: the ex_vimgrep() function is too long Problem: The ex_vimgrep() function is too long. Solution: Split it in three parts. (Yegappan Lakshmanan, closes vim/vim#5211) https://github.com/vim/vim/commit/d6a98a3a9768568b668f91a53267b36f86b84466 Including a missing change to ex_vimgrep() from patch 8.0.1831. --- src/nvim/quickfix.c | 238 +++++++++++++++++++++++++++++----------------------- 1 file changed, 135 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index c609daa55c..d4b71994cc 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -209,6 +209,17 @@ typedef struct { bool valid; } qffields_T; +/// :vimgrep command arguments +typedef struct vgr_args_S { + long tomatch; ///< maximum number of matches to find + char_u *spat; ///< search pattern + int flags; ///< search modifier + char_u **fnames; ///< list of files to search + int fcount; ///< number of files + regmmatch_T regmatch; ///< compiled search pattern + char_u *qf_title; ///< quickfix list title +} vgr_args_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "quickfix.c.generated.h" #endif @@ -4849,11 +4860,12 @@ static qfline_T *qf_find_closest_entry(qf_list_T *qfl, int bnr, const pos_T *pos /// Get the nth quickfix entry below the specified entry. Searches forward in /// the list. If linewise is true, then treat multiple entries on a single line /// as one. -static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, bool linewise, int *errornr) +static void qf_get_nth_below_entry(qfline_T *entry_arg, linenr_T n, bool linewise, int *errornr) FUNC_ATTR_NONNULL_ALL { + qfline_T *entry = entry_arg; + while (n-- > 0 && !got_int) { - // qfline_T *first_entry = entry; int first_errornr = *errornr; if (linewise) { @@ -4864,9 +4876,6 @@ static void qf_get_nth_below_entry(qfline_T *entry, linenr_T n, bool linewise, i if (entry->qf_next == NULL || entry->qf_next->qf_fnum != entry->qf_fnum) { if (linewise) { - // If multiple entries are on the same line, then use the first - // entry - // entry = first_entry; *errornr = first_errornr; } break; @@ -5293,7 +5302,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char_u *fname, buf_T *buf, char_u } /// Jump to the first match and update the directory. -static void vgr_jump_to_match(qf_info_T *qi, int forceit, int *redraw_for_dummy, +static void vgr_jump_to_match(qf_info_T *qi, int forceit, bool *redraw_for_dummy, buf_T *first_match_buf, char_u *target_dir) { buf_T *buf = curbuf; @@ -5328,104 +5337,72 @@ static bool existing_swapfile(const buf_T *buf) return false; } -// ":vimgrep {pattern} file(s)" -// ":vimgrepadd {pattern} file(s)" -// ":lvimgrep {pattern} file(s)" -// ":lvimgrepadd {pattern} file(s)" -void ex_vimgrep(exarg_T *eap) +/// Process :vimgrep command arguments. The command syntax is: +/// +/// :{count}vimgrep /{pattern}/[g][j] {file} ... +static int vgr_process_args(exarg_T *eap, vgr_args_T *args) { - regmmatch_T regmatch; - int fcount; - char_u **fnames; - char_u *fname; - char_u *s; - char_u *p; - int fi; - qf_list_T *qfl; - win_T *wp = NULL; - buf_T *buf; - int duplicate_name = FALSE; - int using_dummy; - int redraw_for_dummy = FALSE; - int found_match; - buf_T *first_match_buf = NULL; - time_t seconds = 0; - aco_save_T aco; - int flags = 0; - long tomatch; - char_u *dirname_start = NULL; - char_u *dirname_now = NULL; - char_u *target_dir = NULL; - char_u *au_name = NULL; + memset(args, 0, sizeof(*args)); - au_name = vgr_get_auname(eap->cmdidx); - if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, - curbuf->b_fname, true, curbuf)) { - if (aborting()) { - return; - } - } - - qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); + args->regmatch.regprog = NULL; + args->qf_title = vim_strsave(qf_cmdtitle(*eap->cmdlinep)); if (eap->addr_count > 0) { - tomatch = eap->line2; + args->tomatch = eap->line2; } else { - tomatch = MAXLNUM; + args->tomatch = MAXLNUM; } // Get the search pattern: either white-separated or enclosed in // - regmatch.regprog = NULL; - char_u *title = vim_strsave(qf_cmdtitle(*eap->cmdlinep)); - p = skip_vimgrep_pat(eap->arg, &s, &flags); + char_u *p = skip_vimgrep_pat(eap->arg, &args->spat, &args->flags); if (p == NULL) { emsg(_(e_invalpat)); - goto theend; + return FAIL; } - vgr_init_regmatch(®match, s); - if (regmatch.regprog == NULL) { - goto theend; + vgr_init_regmatch(&args->regmatch, args->spat); + if (args->regmatch.regprog == NULL) { + return FAIL; } p = skipwhite(p); if (*p == NUL) { emsg(_("E683: File name missing or invalid pattern")); - goto theend; - } - - if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd - && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) - || qf_stack_empty(qi)) { - // make place for a new list - qf_new_list(qi, title); + return FAIL; } // Parse the list of arguments, wildcards have already been expanded. - if (get_arglist_exp(p, &fcount, &fnames, true) == FAIL) { - goto theend; + if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL) { + return FAIL; } - if (fcount == 0) { + if (args->fcount == 0) { emsg(_(e_nomatch)); - goto theend; + return FAIL; } - dirname_start = xmalloc(MAXPATHL); - dirname_now = xmalloc(MAXPATHL); + return OK; +} + +/// Search for a pattern in a list of files and populate the quickfix list with +/// the matches. +static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, + bool *redraw_for_dummy, buf_T **first_match_buf, + char_u **target_dir) +{ + int status = FAIL; + unsigned save_qfid = qf_get_curlist(qi)->qf_id; + bool duplicate_name = false; + + char_u *dirname_start = xmalloc(MAXPATHL); + char_u *dirname_now = xmalloc(MAXPATHL); // Remember the current directory, because a BufRead autocommand that does // ":lcd %:p:h" changes the meaning of short path names. os_dirname(dirname_start, MAXPATHL); - incr_quickfix_busy(); - - // Remember the current quickfix list identifier, so that we can check for - // autocommands changing the current quickfix list. - unsigned save_qfid = qf_get_curlist(qi)->qf_id; - - seconds = (time_t)0; - for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) { - fname = path_try_shorten_fname(fnames[fi]); + time_t seconds = (time_t)0; + for (int fi = 0; fi < cmd_args->fcount && !got_int && cmd_args->tomatch > 0; fi++) { + char_u *fname = path_try_shorten_fname(cmd_args->fnames[fi]); if (time(NULL) > seconds) { // Display the file name every second or so, show the user we are // working on it. @@ -5433,13 +5410,13 @@ void ex_vimgrep(exarg_T *eap) vgr_display_fname(fname); } - buf = buflist_findname_exp(fnames[fi]); + buf_T *buf = buflist_findname_exp(cmd_args->fnames[fi]); + bool using_dummy; if (buf == NULL || buf->b_ml.ml_mfp == NULL) { // Remember that a buffer with this name already exists. duplicate_name = (buf != NULL); - using_dummy = TRUE; - redraw_for_dummy = TRUE; - + using_dummy = true; + *redraw_for_dummy = true; buf = vgr_load_dummy_buf(fname, dirname_start, dirname_now); } else { // Use existing, loaded buffer. @@ -5448,11 +5425,10 @@ void ex_vimgrep(exarg_T *eap) // Check whether the quickfix list is still valid. When loading a // buffer above, autocommands might have changed the quickfix list. - if (!vgr_qflist_valid(wp, qi, save_qfid, *eap->cmdlinep)) { - FreeWild(fcount, fnames); - decr_quickfix_busy(); + if (!vgr_qflist_valid(wp, qi, save_qfid, cmd_args->qf_title)) { goto theend; } + save_qfid = qf_get_curlist(qi)->qf_id; if (buf == NULL) { @@ -5462,12 +5438,18 @@ void ex_vimgrep(exarg_T *eap) } else { // Try for a match in all lines of the buffer. // For ":1vimgrep" look for first match only. - found_match = vgr_match_buflines(qf_get_curlist(qi), fname, buf, s, ®match, &tomatch, - duplicate_name, flags); + bool found_match = vgr_match_buflines(qf_get_curlist(qi), + fname, + buf, + cmd_args->spat, + &cmd_args->regmatch, + &cmd_args->tomatch, + duplicate_name, + cmd_args->flags); if (using_dummy) { - if (found_match && first_match_buf == NULL) { - first_match_buf = buf; + if (found_match && *first_match_buf == NULL) { + *first_match_buf = buf; } if (duplicate_name) { // Never keep a dummy buffer if there is another buffer @@ -5487,8 +5469,8 @@ void ex_vimgrep(exarg_T *eap) if (!found_match) { wipe_dummy_buffer(buf, dirname_start); buf = NULL; - } else if (buf != first_match_buf - || (flags & VGR_NOJUMP) + } else if (buf != *first_match_buf + || (cmd_args->flags & VGR_NOJUMP) || existing_swapfile(buf)) { unload_dummy_buffer(buf, dirname_start); // Keeping the buffer, remove the dummy flag. @@ -5503,16 +5485,17 @@ void ex_vimgrep(exarg_T *eap) // If the buffer is still loaded we need to use the // directory we jumped to below. - if (buf == first_match_buf - && target_dir == NULL + if (buf == *first_match_buf + && *target_dir == NULL && STRCMP(dirname_start, dirname_now) != 0) { - target_dir = vim_strsave(dirname_now); + *target_dir = vim_strsave(dirname_now); } // The buffer is still loaded, the Filetype autocommands // need to be done now, in that buffer. And the modelines // need to be done (again). But not the window-local // options! + aco_save_T aco; aucmd_prepbuf(&aco, buf); apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, true, buf); do_modelines(OPT_NOWIN); @@ -5522,9 +5505,58 @@ void ex_vimgrep(exarg_T *eap) } } - FreeWild(fcount, fnames); + status = OK; - qfl = qf_get_curlist(qi); +theend: + xfree(dirname_now); + xfree(dirname_start); + return status; +} + +/// ":vimgrep {pattern} file(s)" +/// ":vimgrepadd {pattern} file(s)" +/// ":lvimgrep {pattern} file(s)" +/// ":lvimgrepadd {pattern} file(s)" +void ex_vimgrep(exarg_T *eap) +{ + char_u *au_name = vgr_get_auname(eap->cmdidx); + if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, + curbuf->b_fname, true, curbuf)) { + if (aborting()) { + return; + } + } + + win_T *wp = NULL; + qf_info_T *qi = qf_cmd_get_or_alloc_stack(eap, &wp); + char_u *target_dir = NULL; + vgr_args_T args; + if (vgr_process_args(eap, &args) == FAIL) { + goto theend; + } + + if ((eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd + && eap->cmdidx != CMD_vimgrepadd && eap->cmdidx != CMD_lvimgrepadd) + || qf_stack_empty(qi)) { + // make place for a new list + qf_new_list(qi, args.qf_title); + } + + incr_quickfix_busy(); + + bool redraw_for_dummy = false; + buf_T *first_match_buf = NULL; + int status = vgr_process_files(wp, qi, &args, &redraw_for_dummy, &first_match_buf, &target_dir); + + if (status != OK) { + FreeWild(args.fcount, args.fnames); + decr_quickfix_busy(); + goto theend; + } + + FreeWild(args.fcount, args.fnames); + + qf_list_T *qfl = qf_get_curlist(qi); qfl->qf_nonevalid = false; qfl->qf_ptr = qfl->qf_start; qfl->qf_index = 1; @@ -5532,26 +5564,28 @@ void ex_vimgrep(exarg_T *eap) qf_update_buffer(qi, NULL); + // Remember the current quickfix list identifier, so that we can check for + // autocommands changing the current quickfix list. + unsigned save_qfid = qf_get_curlist(qi)->qf_id; + if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, true, curbuf); } // The QuickFixCmdPost autocmd may free the quickfix list. Check the list // is still valid. - if (!qflist_valid(wp, save_qfid) - || qf_restore_list(qi, save_qfid) == FAIL) { + if (!qflist_valid(wp, save_qfid) || qf_restore_list(qi, save_qfid) == FAIL) { decr_quickfix_busy(); goto theend; } // Jump to first match. if (!qf_list_empty(qf_get_curlist(qi))) { - if ((flags & VGR_NOJUMP) == 0) { - vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf, - target_dir); + if ((args.flags & VGR_NOJUMP) == 0) { + vgr_jump_to_match(qi, eap->forceit, &redraw_for_dummy, first_match_buf, target_dir); } } else { - semsg(_(e_nomatch2), s); + semsg(_(e_nomatch2), args.spat); } decr_quickfix_busy(); @@ -5563,11 +5597,9 @@ void ex_vimgrep(exarg_T *eap) } theend: - xfree(title); - xfree(dirname_now); - xfree(dirname_start); + xfree(args.qf_title); xfree(target_dir); - vim_regfree(regmatch.regprog); + vim_regfree(args.regmatch.regprog); } // Restore current working directory to "dirname_start" if they differ, taking -- cgit From 4ce5d27f46599c844eb7ad52b126bb43b97f1c9e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 08:54:54 +0800 Subject: chore(clang): suppress "result of operation is garbage" --- src/nvim/search.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/search.c b/src/nvim/search.c index 3a2435e07a..682fa417a9 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -4848,6 +4848,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz, const uint32_t *const matches, const int numMatches) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { + assert(numMatches > 0); // suppress clang "result of operation is garbage" // Initialize score int score = 100; -- cgit From ff81725ff03e8885df3c292162acfb6305fda14f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 09:52:57 +0800 Subject: refactor(mbyte.c): add const qualifiers This only touches functions that do not return a pointer. Also add a note about the differences between mb_head_off() and utf_head_off(). --- src/nvim/buffer.c | 2 +- src/nvim/mbyte.c | 41 +++++++++++++++++------------------------ src/nvim/ops.c | 2 +- 3 files changed, 19 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bb8483f644..96ddd9a2f5 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3327,7 +3327,7 @@ void maketitle(void) len = (int)STRLEN(buf_p); if (len > 100) { len -= 100; - len += (*mb_tail_off)(buf_p, buf_p + len) + 1; + len += mb_tail_off(buf_p, buf_p + len) + 1; buf_p += len; } STRCPY(icon_str, buf_p); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 7fa2562be0..e5fa80a242 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1821,12 +1821,10 @@ void mb_copy_char(const char_u **const fp, char_u **const tp) *fp += l; } -/* - * Return the offset from "p" to the first byte of a character. When "p" is - * at the start of a character 0 is returned, otherwise the offset to the next - * character. Can start anywhere in a stream of bytes. - */ -int mb_off_next(char_u *base, char_u *p) +/// Return the offset from "p" to the first byte of a character. When "p" is +/// at the start of a character 0 is returned, otherwise the offset to the next +/// character. Can start anywhere in a stream of bytes. +int mb_off_next(const char_u *base, const char_u *p) { int i; int j; @@ -1854,7 +1852,7 @@ int mb_off_next(char_u *base, char_u *p) /// Return the offset from "p" to the last byte of the character it points /// into. Can start anywhere in a stream of bytes. /// Composing characters are not included. -int mb_tail_off(char_u *base, char_u *p) +int mb_tail_off(const char_u *base, const char_u *p) { int i; int j; @@ -1882,12 +1880,13 @@ int mb_tail_off(char_u *base, char_u *p) /// Return the offset from "p" to the first byte of the character it points /// into. Can start anywhere in a stream of bytes. +/// Unlike utf_head_off() this doesn't include composing characters and returns a negative value. /// /// @param[in] base Pointer to start of string /// @param[in] p Pointer to byte for which to return the offset to the previous codepoint // /// @return 0 if invalid sequence, else offset to previous codepoint -int mb_head_off(char_u *base, char_u *p) +int mb_head_off(const char_u *base, const char_u *p) { int i; int j; @@ -2037,13 +2036,11 @@ char_u *mb_prevptr(char_u *line, char_u *p) return p; } -/* - * Return the character length of "str". Each multi-byte character (with - * following composing characters) counts as one. - */ -int mb_charlen(char_u *str) +/// Return the character length of "str". Each multi-byte character (with +/// following composing characters) counts as one. +int mb_charlen(const char_u *str) { - char_u *p = str; + const char_u *p = str; int count; if (p == NULL) { @@ -2057,12 +2054,10 @@ int mb_charlen(char_u *str) return count; } -/* - * Like mb_charlen() but for a string with specified length. - */ -int mb_charlen_len(char_u *str, int len) +/// Like mb_charlen() but for a string with specified length. +int mb_charlen_len(const char_u *str, int len) { - char_u *p = str; + const char_u *p = str; int count; for (count = 0; *p != NUL && p < str + len; count++) { @@ -2201,11 +2196,9 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET return r; } -/* - * Search for an encoding alias of "name". - * Returns -1 when not found. - */ -static int enc_alias_search(char_u *name) +/// Search for an encoding alias of "name". +/// Returns -1 when not found. +static int enc_alias_search(const char_u *name) { int i; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b8b639265c..1115bb2845 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -566,7 +566,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def if (b_insert) { off = utf_head_off(oldp, oldp + offset + spaces); } else { - off = (*mb_off_next)(oldp, oldp + offset); + off = mb_off_next(oldp, oldp + offset); offset += off; } spaces -= off; -- cgit From b9732e555b116e8b6b037d107722ce39add4952f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 13:18:37 +0800 Subject: vim-patch:8.2.4329: no support for end line number and column in 'errorformat' Problem: No support for end line number and column in 'errorformat'. Solution: Add %e and %k. (closes vim/vim#9624) https://github.com/vim/vim/commit/e023d499378942a6c3a3855cbe461ec2cb570f63 Use "\t" to represent a Tab as it looks better. --- src/nvim/quickfix.c | 74 ++++++++++++++++++++++++++++---------- src/nvim/testdir/test_quickfix.vim | 23 ++++++++++++ 2 files changed, 78 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d4b71994cc..b12f407460 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -131,7 +131,7 @@ struct qf_info_S { static qf_info_T ql_info; // global quickfix list static unsigned last_qf_id = 0; // Last Used quickfix list id -#define FMT_PATTERNS 11 // maximum number of % recognized +#define FMT_PATTERNS 13 // maximum number of % recognized // Structure used to hold the info of one part of 'errorformat' typedef struct efm_S efm_T; @@ -332,22 +332,27 @@ int qf_init(win_T *wp, const char_u *restrict efile, char_u *restrict errorforma // Maximum number of bytes allowed per line while reading an errorfile. static const size_t LINE_MAXLEN = 4096; +/// Patterns used. Keep in sync with qf_parse_fmt[]. static struct fmtpattern { char_u convchar; char *pattern; } fmt_pat[FMT_PATTERNS] = { - { 'f', ".\\+" }, // only used when at end - { 'n', "\\d\\+" }, - { 'l', "\\d\\+" }, - { 'c', "\\d\\+" }, - { 't', "." }, - { 'm', ".\\+" }, - { 'r', ".*" }, - { 'p', "[- .]*"}, // NOLINT(whitespace/tab) - { 'v', "\\d\\+" }, - { 's', ".\\+" }, - { 'o', ".\\+" } + { 'f', ".\\+" }, // only used when at end + { 'n', "\\d\\+" }, // 1 + { 'l', "\\d\\+" }, // 2 + { 'e', "\\d\\+" }, // 3 + { 'c', "\\d\\+" }, // 4 + { 'k', "\\d\\+" }, // 5 + { 't', "." }, // 6 +#define FMT_PATTERN_M 7 + { 'm', ".\\+" }, // 7 +#define FMT_PATTERN_R 8 + { 'r', ".*" }, // 8 + { 'p', "[- \t.]*" }, // 9 + { 'v', "\\d\\+" }, // 10 + { 's', ".\\+" }, // 11 + { 'o', ".\\+" } // 12 }; /// Convert an errorformat pattern to a regular expression pattern. @@ -363,9 +368,9 @@ static char_u *efmpat_to_regpat(const char_u *efmpat, char_u *regpat, efm_T *efm semsg(_("E372: Too many %%%c in format string"), *efmpat); return NULL; } - if ((idx && idx < 6 + if ((idx && idx < FMT_PATTERN_R && vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL) - || (idx == 6 + || (idx == FMT_PATTERN_R && vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL)) { semsg(_("E373: Unexpected %%%c in format string"), *efmpat); return NULL; @@ -1288,7 +1293,7 @@ static int qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields) return QF_OK; } -/// Parse the match for line number (%l') pattern in regmatch. +/// Parse the match for line number ('%l') pattern in regmatch. /// Return the matched value in "fields->lnum". static int qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields) { @@ -1299,6 +1304,17 @@ static int qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields) return QF_OK; } +/// Parse the match for end line number ('%e') pattern in regmatch. +/// Return the matched value in "fields->end_lnum". +static int qf_parse_fmt_e(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) { + return QF_FAIL; + } + fields->end_lnum = atol((char *)rmp->startp[midx]); + return QF_OK; +} + /// Parse the match for column number ('%c') pattern in regmatch. /// Return the matched value in "fields->col". static int qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields) @@ -1310,6 +1326,17 @@ static int qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields) return QF_OK; } +/// Parse the match for end line number ('%e') pattern in regmatch. +/// Return the matched value in "fields->end_lnum". +static int qf_parse_fmt_k(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) { + return QF_FAIL; + } + fields->end_col = (int)atol((char *)rmp->startp[midx]); + return QF_OK; +} + /// Parse the match for error type ('%t') pattern in regmatch. /// Return the matched value in "fields->type". static int qf_parse_fmt_t(regmatch_T *rmp, int midx, qffields_T *fields) @@ -1442,14 +1469,17 @@ static int qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) /// 'errorformat' format pattern parser functions. /// The '%f' and '%r' formats are parsed differently from other formats. /// See qf_parse_match() for details. +/// Keep in sync with fmt_pat[]. static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = { - NULL, + NULL, // %f qf_parse_fmt_n, qf_parse_fmt_l, + qf_parse_fmt_e, qf_parse_fmt_c, + qf_parse_fmt_k, qf_parse_fmt_t, qf_parse_fmt_m, - NULL, + NULL, // %r qf_parse_fmt_p, qf_parse_fmt_v, qf_parse_fmt_s, @@ -1485,13 +1515,13 @@ static int qf_parse_match(char_u *linebuf, size_t linelen, efm_T *fmt_ptr, regma midx = (int)fmt_ptr->addr[i]; if (i == 0 && midx > 0) { // %f status = qf_parse_fmt_f(regmatch, midx, fields, idx); - } else if (i == 5) { + } else if (i == FMT_PATTERN_M) { if (fmt_ptr->flags == '+' && !qf_multiscan) { // %+ qf_parse_fmt_plus(linebuf, linelen, fields); } else if (midx > 0) { // %m status = qf_parse_fmt_m(regmatch, midx, fields); } - } else if (i == 6 && midx > 0) { // %r + } else if (i == FMT_PATTERN_R && midx > 0) { // %r status = qf_parse_fmt_r(regmatch, midx, tail); } else if (midx > 0) { // others status = (qf_parse_fmt[i])(regmatch, midx, fields); @@ -1636,10 +1666,16 @@ static int qf_parse_multiline_pfx(int idx, qf_list_T *qfl, qffields_T *fields) if (!qfprev->qf_lnum) { qfprev->qf_lnum = fields->lnum; } + if (!qfprev->qf_end_lnum) { + qfprev->qf_end_lnum = fields->end_lnum; + } if (!qfprev->qf_col) { qfprev->qf_col = fields->col; qfprev->qf_viscol = fields->use_viscol; } + if (!qfprev->qf_end_col) { + qfprev->qf_end_col = fields->end_col; + } if (!qfprev->qf_fnum) { qfprev->qf_fnum = qf_get_fnum(qfl, qfl->qf_directory, *fields->namebuf || qfl->qf_directory diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 00679e1958..c4d70fb1de 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1384,6 +1384,29 @@ func Test_efm_error_type() let &efm = save_efm endfunc +" Test for end_lnum ('%e') and end_col ('%k') fields in 'efm' +func Test_efm_end_lnum_col() + let save_efm = &efm + + " single line + set efm=%f:%l-%e:%c-%k:%t:%m + cexpr ["Xfile1:10-20:1-2:E:msg1", "Xfile1:20-30:2-3:W:msg2",] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10-20 col 1-2 error: msg1', + \ ' 2 Xfile1:20-30 col 2-3 warning: msg2'], output) + + " multiple lines + set efm=%A%n)%m,%Z%f:%l-%e:%c-%k + cexpr ["1)msg1", "Xfile1:14-24:1-2", + \ "2)msg2", "Xfile1:24-34:3-4"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:14-24 col 1-2 error 1: msg1', + \ ' 2 Xfile1:24-34 col 3-4 error 2: msg2'], output) + let &efm = save_efm +endfunc + func XquickfixChangedByAutocmd(cchar) call s:setup_commands(a:cchar) if a:cchar == 'c' -- cgit From d9cb3fba9228aed5109a8e6069c50e4f265be9f3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 14:21:04 +0800 Subject: vim-patch:8.2.4242: put in Visual mode cannot be repeated Problem: Put in Visual mode cannot be repeated. Solution: Use "P" to put without yanking the deleted text into the unnamed register. (Shougo Matsushita, closes vim/vim#9591) https://github.com/vim/vim/commit/fb55207ed17918c8a2a6cadf5ad9d5fcf686a7ab Cherry-pick get_y_previous() and set_y_previous() from patch 8.1.1736. Nvim has removed y_current, so code related to it is N/A. --- src/nvim/normal.c | 12 ++++++++++-- src/nvim/ops.c | 16 ++++++++++++---- src/nvim/testdir/test_visual.vim | 24 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 225c66aae1..21c465434a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -7509,9 +7509,9 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // overwrites if the old contents is being put. was_visual = true; regname = cap->oap->regname; + bool save_unnamed = cap->cmdchar == 'P'; // '+' and '*' could be the same selection - bool clipoverwrite = (regname == '+' || regname == '*') - && (cb_flags & CB_UNNAMEDMASK); + bool clipoverwrite = (regname == '+' || regname == '*') && (cb_flags & CB_UNNAMEDMASK); if (regname == 0 || regname == '"' || clipoverwrite || ascii_isdigit(regname) || regname == '-') { // The delete might overwrite the register we want to put, save it first @@ -7524,6 +7524,10 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) // do_put(), which requires the visual selection to still be active. if (!VIsual_active || VIsual_mode == 'V' || regname != '.') { // Now delete the selected text. Avoid messages here. + yankreg_T *old_y_previous; + if (save_unnamed) { + old_y_previous = get_y_previous(); + } cap->cmdchar = 'd'; cap->nchar = NUL; cap->oap->regname = NUL; @@ -7533,6 +7537,10 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent) empty = (curbuf->b_ml.ml_flags & ML_EMPTY); msg_silent--; + if (save_unnamed) { + set_y_previous(old_y_previous); + } + // delete PUT_LINE_BACKWARD; cap->oap->regname = regname; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b8b639265c..f8ab6b2556 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -135,10 +135,18 @@ static char opchars[][3] = { Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB }; -/* - * Translate a command name into an operator type. - * Must only be called with a valid operator name! - */ +yankreg_T *get_y_previous(void) +{ + return y_previous; +} + +void set_y_previous(yankreg_T *yreg) +{ + y_previous = yreg; +} + +/// Translate a command name into an operator type. +/// Must only be called with a valid operator name! int get_op_type(int char1, int char2) { int i; diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 76274fb038..e931e06175 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1184,8 +1184,32 @@ func Test_visual_undo_deletes_last_line() exe "normal ggvjfxO" undo normal gNU + bwipe! endfunc +func Test_visual_paste() + new + + " v_p overwrites unnamed register. + call setline(1, ['xxxx']) + call setreg('"', 'foo') + call setreg('-', 'bar') + normal 1Gvp + call assert_equal(@", 'x') + call assert_equal(@-, 'x') + + if has('clipboard') + " v_P does not overwrite unnamed register. + call setline(1, ['xxxx']) + call setreg('"', 'foo') + call setreg('-', 'bar') + normal 1GvP + call assert_equal(@", 'foo') + call assert_equal(@-, 'x') + endif + + bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 3fe47647c7b902d882d3d72158f2712490506a75 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 14:21:04 +0800 Subject: vim-patch:8.2.4315: put in Visual mode not fully tested Problem: Put in Visual mode not fully tested. Solution: Add a few more test cases. (closes vim/vim#9708) https://github.com/vim/vim/commit/6bf821e8abe1da24e5d0624f032d7eda745756e8 --- src/nvim/testdir/test_visual.vim | 46 ++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index e931e06175..099a90643f 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1195,18 +1195,52 @@ func Test_visual_paste() call setline(1, ['xxxx']) call setreg('"', 'foo') call setreg('-', 'bar') - normal 1Gvp - call assert_equal(@", 'x') - call assert_equal(@-, 'x') + normal gg0vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + normal $vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + " Test with a different register as unnamed register. + call setline(2, ['baz']) + normal 2gg0"rD + call assert_equal('baz', @") + normal gg0vp + call assert_equal('f', @") + call assert_equal('f', @-) + call assert_equal('bazooxxx', getline(1)) + normal $vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('bazooxxf', getline(1)) if has('clipboard') " v_P does not overwrite unnamed register. call setline(1, ['xxxx']) call setreg('"', 'foo') call setreg('-', 'bar') - normal 1GvP - call assert_equal(@", 'foo') - call assert_equal(@-, 'x') + normal gg0vP + call assert_equal('foo', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + normal $vP + call assert_equal('foo', @") + call assert_equal('x', @-) + call assert_equal('fooxxfoo', getline(1)) + " Test with a different register as unnamed register. + call setline(2, ['baz']) + normal 2gg0"rD + call assert_equal('baz', @") + normal gg0vP + call assert_equal('baz', @") + call assert_equal('f', @-) + call assert_equal('bazooxxfoo', getline(1)) + normal $vP + call assert_equal('baz', @") + call assert_equal('o', @-) + call assert_equal('bazooxxfobaz', getline(1)) endif bwipe! -- cgit From a2a37effc2144860174310b354ba289297cc482f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 15:23:45 +0800 Subject: refactor(PVS/V547): p == NULL is always false --- src/nvim/screen.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 6b2a2afa41..0644a08210 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3753,34 +3753,30 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } c = wp->w_p_lcs_chars.tab1; p = xmalloc(len + 1); - if (p == NULL) { - n_extra = 0; - } else { - memset(p, ' ', len); - p[len] = NUL; - xfree(p_extra_free); - p_extra_free = p; - for (i = 0; i < tab_len; i++) { - if (*p == NUL) { - tab_len = i; - break; - } - int lcs = wp->w_p_lcs_chars.tab2; - - // if tab3 is given, use it for the last char - if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { - lcs = wp->w_p_lcs_chars.tab3; - } - p += utf_char2bytes(lcs, p); - n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); + memset(p, ' ', len); + p[len] = NUL; + xfree(p_extra_free); + p_extra_free = p; + for (i = 0; i < tab_len; i++) { + if (*p == NUL) { + tab_len = i; + break; } - p_extra = p_extra_free; + int lcs = wp->w_p_lcs_chars.tab2; - // n_extra will be increased by FIX_FOX_BOGUSCOLS - // macro below, so need to adjust for that here - if (vcol_off > 0) { - n_extra -= vcol_off; + // if tab3 is given, use it for the last char + if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) { + lcs = wp->w_p_lcs_chars.tab3; } + p += utf_char2bytes(lcs, p); + n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0); + } + p_extra = p_extra_free; + + // n_extra will be increased by FIX_FOX_BOGUSCOLS + // macro below, so need to adjust for that here + if (vcol_off > 0) { + n_extra -= vcol_off; } } -- cgit From 92e43945627ac71ef160e3ecde1bf2913385c7d1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 19:24:41 +0800 Subject: vim-patch:8.2.1316: test 42 is still old style Problem: Test 42 is still old style. Solution: Turn it into a new style test. (Yegappan Lakshmanan, closes vim/vim#6561) https://github.com/vim/vim/commit/b61ef01cce2afd70fbfa2805336a26643109dfb7 Including the Xtest2 -> Xfile2 change from Vim patch 8.2.1498. --- src/nvim/testdir/test_writefile.vim | 128 ++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 1d9fc6e3f7..c69e8612c6 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -369,6 +369,134 @@ func Test_write_file_encoding() %bw! endfunc +" Test for writing and reading a file starting with a BOM. +" Byte Order Mark (BOM) character for various encodings is below: +" UTF-8 : EF BB BF +" UTF-16 (BE): FE FF +" UTF-16 (LE): FF FE +" UTF-32 (BE): 00 00 FE FF +" UTF-32 (LE): FF FE 00 00 +func Test_readwrite_file_with_bom() + let utf8_bom = "\xEF\xBB\xBF" + let utf16be_bom = "\xFE\xFF" + let utf16le_bom = "\xFF\xFE" + let utf32be_bom = "\n\n\xFE\xFF" + let utf32le_bom = "\xFF\xFE\n\n" + let save_fileencoding = &fileencoding + set cpoptions+=S + + " Check that editing a latin1 file doesn't see a BOM + call writefile(["\xFE\xFElatin-1"], 'Xtest1') + edit Xtest1 + call assert_equal('latin1', &fileencoding) + call assert_equal(0, &bomb) + set fenc=latin1 + write Xfile2 + call assert_equal(["\xFE\xFElatin-1", ''], readfile('Xfile2', 'b')) + set bomb fenc=latin1 + write Xtest3 + call assert_equal(["\xFE\xFElatin-1", ''], readfile('Xtest3', 'b')) + set bomb& + + " Check utf-8 BOM + %bw! + call writefile([utf8_bom .. "utf-8"], 'Xtest1') + edit! Xtest1 + call assert_equal('utf-8', &fileencoding) + call assert_equal(1, &bomb) + call assert_equal('utf-8', getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal(['utf-8', ''], readfile('Xfile2', 'b')) + set fenc=utf-8 + w! Xtest3 + call assert_equal([utf8_bom .. "utf-8", ''], readfile('Xtest3', 'b')) + + " Check utf-8 with an error (will fall back to latin-1) + %bw! + call writefile([utf8_bom .. "utf-8\x80err"], 'Xtest1') + edit! Xtest1 + call assert_equal('latin1', &fileencoding) + call assert_equal(0, &bomb) + call assert_equal("\xC3\xAF\xC2\xBB\xC2\xBFutf-8\xC2\x80err", getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal([utf8_bom .. "utf-8\x80err", ''], readfile('Xfile2', 'b')) + set fenc=utf-8 + w! Xtest3 + call assert_equal(["\xC3\xAF\xC2\xBB\xC2\xBFutf-8\xC2\x80err", ''], + \ readfile('Xtest3', 'b')) + + " Check ucs-2 BOM + %bw! + call writefile([utf16be_bom .. "\nu\nc\ns\n-\n2\n"], 'Xtest1') + edit! Xtest1 + call assert_equal('utf-16', &fileencoding) + call assert_equal(1, &bomb) + call assert_equal('ucs-2', getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal(["ucs-2", ''], readfile('Xfile2', 'b')) + set fenc=ucs-2 + w! Xtest3 + call assert_equal([utf16be_bom .. "\nu\nc\ns\n-\n2\n", ''], + \ readfile('Xtest3', 'b')) + + " Check ucs-2le BOM + %bw! + call writefile([utf16le_bom .. "u\nc\ns\n-\n2\nl\ne\n"], 'Xtest1') + " Need to add a NUL byte after the NL byte + call writefile(0z00, 'Xtest1', 'a') + edit! Xtest1 + call assert_equal('utf-16le', &fileencoding) + call assert_equal(1, &bomb) + call assert_equal('ucs-2le', getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal(["ucs-2le", ''], readfile('Xfile2', 'b')) + set fenc=ucs-2le + w! Xtest3 + call assert_equal([utf16le_bom .. "u\nc\ns\n-\n2\nl\ne\n", "\n"], + \ readfile('Xtest3', 'b')) + + " Check ucs-4 BOM + %bw! + call writefile([utf32be_bom .. "\n\n\nu\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\n"], 'Xtest1') + edit! Xtest1 + call assert_equal('ucs-4', &fileencoding) + call assert_equal(1, &bomb) + call assert_equal('ucs-4', getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal(["ucs-4", ''], readfile('Xfile2', 'b')) + set fenc=ucs-4 + w! Xtest3 + call assert_equal([utf32be_bom .. "\n\n\nu\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\n", ''], readfile('Xtest3', 'b')) + + " Check ucs-4le BOM + %bw! + call writefile([utf32le_bom .. "u\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\nl\n\n\ne\n\n\n"], 'Xtest1') + " Need to add three NUL bytes after the NL byte + call writefile(0z000000, 'Xtest1', 'a') + edit! Xtest1 + call assert_equal('ucs-4le', &fileencoding) + call assert_equal(1, &bomb) + call assert_equal('ucs-4le', getline(1)) + set fenc=latin1 + write! Xfile2 + call assert_equal(["ucs-4le", ''], readfile('Xfile2', 'b')) + set fenc=ucs-4le + w! Xtest3 + call assert_equal([utf32le_bom .. "u\n\n\nc\n\n\ns\n\n\n-\n\n\n4\n\n\nl\n\n\ne\n\n\n", "\n\n\n"], readfile('Xtest3', 'b')) + + set cpoptions-=S + let &fileencoding = save_fileencoding + call delete('Xtest1') + call delete('Xfile2') + call delete('Xtest3') + %bw! +endfunc + " Check that buffer is written before triggering QuitPre func Test_wq_quitpre_autocommand() edit Xsomefile -- cgit From 47a98ab3941fbb664879f67aa0270a9f93d2bed4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 19:24:41 +0800 Subject: vim-patch:8.2.2199: first write after setting 'eol' does not have NL added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: First write after setting 'eol' does not have NL added. (Tomáš JanouÅ¡ek) Solution: Only use b_no_eol_lnum when doing a binary write. (closes vim/vim#7535) https://github.com/vim/vim/commit/b3c8b1d25414f2e24ad03551cdf125b3e2c142b1 --- src/nvim/fileio.c | 2 +- src/nvim/testdir/test_writefile.vim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f28ee1bfcb..8e1be3bbf7 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3318,7 +3318,7 @@ restore_backup: if (end == 0 || (lnum == end && (write_bin || !buf->b_p_fixeol) - && (lnum == buf->b_no_eol_lnum + && ((write_bin && lnum == buf->b_no_eol_lnum) || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) { lnum++; // written the line, count it no_eol = true; diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index c69e8612c6..18e6f5e211 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -497,6 +497,24 @@ func Test_readwrite_file_with_bom() %bw! endfunc +func Test_read_write_bin() + " write file missing EOL + call writefile(['noeol'], "XNoEolSetEol", 'bS') + call assert_equal(0z6E6F656F6C, readfile('XNoEolSetEol', 'B')) + + " when file is read 'eol' is off + set ff=unix nofixeol + e XNoEolSetEol + call assert_equal(0, &eol) + + " writing with 'eol' set adds the newline + setlocal eol + w + call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B')) + + call delete('XNoEolSetEol') +endfunc + " Check that buffer is written before triggering QuitPre func Test_wq_quitpre_autocommand() edit Xsomefile -- cgit From 2863fac61f8855a4feea275079f01765c0017df1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 19:24:41 +0800 Subject: vim-patch:8.2.2201: write file test fails on MS-Windows Problem: Write file test fails on MS-Windows. Solution: Force edit after setting 'fileformat'. https://github.com/vim/vim/commit/bd318559cf8dea210e943100536011473f25bf68 --- src/nvim/testdir/test_writefile.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 18e6f5e211..18281cd52f 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -504,7 +504,7 @@ func Test_read_write_bin() " when file is read 'eol' is off set ff=unix nofixeol - e XNoEolSetEol + e! XNoEolSetEol call assert_equal(0, &eol) " writing with 'eol' set adds the newline @@ -513,6 +513,8 @@ func Test_read_write_bin() call assert_equal(0z6E6F656F6C0A, readfile('XNoEolSetEol', 'B')) call delete('XNoEolSetEol') + set ff& + bwipe! XNoEolSetEol endfunc " Check that buffer is written before triggering QuitPre -- cgit From 0675c7de751cd4efe5c699b5d4a04c333fb17d9a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 9 Feb 2022 19:24:41 +0800 Subject: vim-patch:8.2.2202: write file test still fails on MS-Windows Problem: Write file test still fails on MS-Windows. Solution: Set fileformat with the :edit command https://github.com/vim/vim/commit/16204962c7ad7380a40f0855443303ad16114e2b --- src/nvim/testdir/test_writefile.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim index 18281cd52f..b42665c9b5 100644 --- a/src/nvim/testdir/test_writefile.vim +++ b/src/nvim/testdir/test_writefile.vim @@ -503,8 +503,8 @@ func Test_read_write_bin() call assert_equal(0z6E6F656F6C, readfile('XNoEolSetEol', 'B')) " when file is read 'eol' is off - set ff=unix nofixeol - e! XNoEolSetEol + set nofixeol + e! ++ff=unix XNoEolSetEol call assert_equal(0, &eol) " writing with 'eol' set adds the newline -- cgit From aff0ddd784bed586e359fc4b16e86ab214cf6039 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 9 Feb 2022 18:55:01 +0100 Subject: vim-patch:8.2.4337: part of condition is always true (#17352) Problem: Part of condition is always true. Solution: Remove that part of the condition. (closes vim/vim#9729) https://github.com/vim/vim/commit/78a8404f8b6ad0152614d5fdc3ec277444c1eee5 --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 926c385892..c197754685 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -10700,7 +10700,7 @@ repeat: pbuf = NULL; // Need full path first (use expand_env() to remove a "~/") if (!has_fullname && !has_homerelative) { - if ((c == '.' || c == '~') && **fnamep == '~') { + if (**fnamep == '~') { p = pbuf = expand_env_save(*fnamep); } else { p = pbuf = (char_u *)FullName_save((char *)*fnamep, FALSE); -- cgit From aea889fc06f0efb691cab92ebe2e46c52fe259b1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Feb 2022 07:28:54 +0800 Subject: vim-patch:8.1.2221: cannot filter :disp output Problem: Cannot filter :disp output. Solution: Support filtereing :disp output. (Andi Massimino, closes vim/vim#5117) https://github.com/vim/vim/commit/8fc42964363087025a27e8c80276c706536fc4e3 --- src/nvim/ops.c | 99 ++++++++++++++++++------------------ src/nvim/testdir/test_filter_cmd.vim | 27 ++++++++++ 2 files changed, 76 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index cf1fd29c9e..f999b68236 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3732,7 +3732,7 @@ void ex_display(exarg_T *eap) int name; char_u *arg = eap->arg; int clen; - char_u type[2]; + int type; if (arg != NULL && *arg == NUL) { arg = NULL; @@ -3745,11 +3745,11 @@ void ex_display(exarg_T *eap) name = get_register_name(i); switch (get_reg_type(name, NULL)) { case kMTLineWise: - type[0] = 'l'; break; + type = 'l'; break; case kMTCharWise: - type[0] = 'c'; break; + type = 'c'; break; default: - type[0] = 'b'; break; + type = 'b'; break; } if (arg != NULL && vim_strchr(arg, name) == NULL) { @@ -3776,88 +3776,87 @@ void ex_display(exarg_T *eap) } if (yb->y_array != NULL) { - msg_putchar('\n'); - msg_puts(" "); - msg_putchar(type[0]); - msg_puts(" "); - msg_putchar('"'); - msg_putchar(name); - msg_puts(" "); - - int n = Columns - 11; - for (size_t j = 0; j < yb->y_size && n > 1; j++) { - if (j) { - msg_puts_attr("^J", attr); - n -= 2; + bool do_show = false; + + for (size_t j = 0; !do_show && j < yb->y_size; j++) { + do_show = !message_filtered(yb->y_array[j]); + } + + if (do_show || yb->y_size == 0) { + msg_putchar('\n'); + msg_puts(" "); + msg_putchar(type); + msg_puts(" "); + msg_putchar('"'); + msg_putchar(name); + msg_puts(" "); + + int n = Columns - 11; + for (size_t j = 0; j < yb->y_size && n > 1; j++) { + if (j) { + msg_puts_attr("^J", attr); + n -= 2; + } + for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { + clen = utfc_ptr2len(p); + msg_outtrans_len(p, clen); + p += clen - 1; + } } - for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 NOLINT(whitespace/line_length) - clen = utfc_ptr2len(p); - msg_outtrans_len(p, clen); - p += clen - 1; + if (n > 1 && yb->y_type == kMTLineWise) { + msg_puts_attr("^J", attr); } + ui_flush(); // show one line at a time } - if (n > 1 && yb->y_type == kMTLineWise) { - msg_puts_attr("^J", attr); - } - ui_flush(); // show one line at a time + os_breakcheck(); } - os_breakcheck(); } - /* - * display last inserted text - */ + // display last inserted text if ((p = get_last_insert()) != NULL - && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int) { + && (arg == NULL || vim_strchr(arg, '.') != NULL) && !got_int + && !message_filtered(p)) { msg_puts("\n c \". "); dis_msg(p, true); } - /* - * display last command line - */ + // display last command line if (last_cmdline != NULL && (arg == NULL || vim_strchr(arg, ':') != NULL) - && !got_int) { + && !got_int && !message_filtered(last_cmdline)) { msg_puts("\n c \": "); dis_msg(last_cmdline, false); } - /* - * display current file name - */ + // display current file name if (curbuf->b_fname != NULL - && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) { + && (arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int + && !message_filtered(curbuf->b_fname)) { msg_puts("\n c \"% "); dis_msg(curbuf->b_fname, false); } - /* - * display alternate file name - */ + // display alternate file name if ((arg == NULL || vim_strchr(arg, '%') != NULL) && !got_int) { char_u *fname; linenr_T dummy; - if (buflist_name_nr(0, &fname, &dummy) != FAIL) { + if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered(fname)) { msg_puts("\n c \"# "); dis_msg(fname, false); } } - /* - * display last search pattern - */ + // display last search pattern if (last_search_pat() != NULL - && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int) { + && (arg == NULL || vim_strchr(arg, '/') != NULL) && !got_int + && !message_filtered(last_search_pat())) { msg_puts("\n c \"/ "); dis_msg(last_search_pat(), false); } - /* - * display last used expression - */ + // display last used expression if (expr_line != NULL && (arg == NULL || vim_strchr(arg, '=') != NULL) - && !got_int) { + && !got_int && !message_filtered(expr_line)) { msg_puts("\n c \"= "); dis_msg(expr_line, false); } diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index 0c45db049b..144c7fa863 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -145,3 +145,30 @@ func Test_filter_commands() bwipe! file.h bwipe! file.hs endfunc + +func Test_filter_display() + edit Xdoesnotmatch + let @a = '!!willmatch' + let @b = '!!doesnotmatch' + let @c = "oneline\ntwoline\nwillmatch\n" + let @/ = '!!doesnotmatch' + call feedkeys(":echo '!!doesnotmatch:'\", 'ntx') + let lines = map(split(execute('filter /willmatch/ display'), "\n"), 'v:val[5:6]') + + call assert_true(index(lines, '"a') >= 0) + call assert_false(index(lines, '"b') >= 0) + call assert_true(index(lines, '"c') >= 0) + call assert_false(index(lines, '"/') >= 0) + call assert_false(index(lines, '":') >= 0) + call assert_false(index(lines, '"%') >= 0) + + let lines = map(split(execute('filter /doesnotmatch/ display'), "\n"), 'v:val[5:6]') + call assert_true(index(lines, '"a') < 0) + call assert_false(index(lines, '"b') < 0) + call assert_true(index(lines, '"c') < 0) + call assert_false(index(lines, '"/') < 0) + call assert_false(index(lines, '":') < 0) + call assert_false(index(lines, '"%') < 0) + + bwipe! +endfunc -- cgit From c415e764d424251bb8139dcdd793b7b716aec563 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Feb 2022 07:28:54 +0800 Subject: vim-patch:8.2.4336: using :filter for :scriptnames does not work Problem: Using :filter for :scriptnames does not work. (Ben Jackson) Solution: Call message_filtered(). (closes vim/vim#9720) https://github.com/vim/vim/commit/769f5895ebfd10535a0ad978f071da8f20178fc6 Cherry-pick a modeline from Vim patch 8.2.1432. --- src/nvim/ex_cmds2.c | 9 ++++++--- src/nvim/testdir/test_filter_cmd.vim | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e5cec0e060..090422088a 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -12,6 +12,7 @@ #include #include "nvim/ascii.h" +#include "nvim/globals.h" #include "nvim/vim.h" #ifdef HAVE_LOCALE_H # include @@ -2190,9 +2191,11 @@ void ex_scriptnames(exarg_T *eap) if (SCRIPT_ITEM(i).sn_name != NULL) { home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true); vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff); - msg_putchar('\n'); - msg_outtrans(IObuff); - line_breakcheck(); + if (!message_filtered(IObuff)) { + msg_putchar('\n'); + msg_outtrans(IObuff); + line_breakcheck(); + } } } } diff --git a/src/nvim/testdir/test_filter_cmd.vim b/src/nvim/testdir/test_filter_cmd.vim index 144c7fa863..d465e48c7b 100644 --- a/src/nvim/testdir/test_filter_cmd.vim +++ b/src/nvim/testdir/test_filter_cmd.vim @@ -172,3 +172,11 @@ func Test_filter_display() bwipe! endfunc + +func Test_filter_scriptnames() + let lines = split(execute('filter /test_filter_cmd/ scriptnames'), "\n") + call assert_equal(1, len(lines)) + call assert_match('filter_cmd', lines[0]) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 23c3f7f572d3a1f3816a9a9ae1b3632c53856923 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Feb 2022 09:41:25 +0800 Subject: fix(api): use changedir_func() in nvim_set_current_dir() Co-Authored-By: smolck <46855713+smolck@users.noreply.github.com> --- src/nvim/api/vim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ada041bab2..f7c55344f5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -31,6 +31,7 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/lua/executor.h" @@ -545,20 +546,19 @@ void nvim_set_current_dir(String dir, Error *err) return; } - char string[MAXPATHL]; + char_u string[MAXPATHL]; memcpy(string, dir.data, dir.size); string[dir.size] = NUL; try_start(); - if (vim_chdir((char_u *)string)) { + if (!changedir_func(string, kCdScopeGlobal)) { if (!try_end(err)) { api_set_error(err, kErrorTypeException, "Failed to change directory"); } return; } - post_chdir(kCdScopeGlobal, true); try_end(err); } -- cgit From de328de35bd90ce34d5771b1e85f4afddd3f179d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Feb 2022 14:06:33 +0800 Subject: test(old): comment out WinBar instead of skipping Test_screenpos() --- src/nvim/testdir/test_cursor_func.vim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index f2ffd50726..47e74a24d6 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -75,7 +75,6 @@ func Test_curswant_with_cursorline() endfunc func Test_screenpos() - throw 'skipped: TODO: ' rightbelow new rightbelow 20vsplit call setline(1, ["\tsome text", "long wrapping line here", "next line"]) @@ -103,9 +102,10 @@ func Test_screenpos() bwipe! call assert_equal({'col': 1, 'row': 1, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) - nmenu WinBar.TEST : - call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) - nunmenu WinBar.TEST + " Needs WinBar + " nmenu WinBar.TEST : + " call assert_equal({'col': 1, 'row': 2, 'endcol': 1, 'curscol': 1}, screenpos(win_getid(), 1, 1)) + " nunmenu WinBar.TEST endfunc func Test_screenpos_number() -- cgit From 85ae04dbfd405343b10c400d40e95334a44cc978 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Sat, 17 Apr 2021 17:33:59 -0400 Subject: fix: close floating windows when calling win_close() --- src/nvim/api/window.c | 2 +- src/nvim/buffer.c | 12 ++++++------ src/nvim/ex_cmds.c | 2 +- src/nvim/ex_docmd.c | 12 ++++++------ src/nvim/ex_getln.c | 2 +- src/nvim/main.c | 6 +++--- src/nvim/quickfix.c | 8 ++++---- src/nvim/tag.c | 2 +- src/nvim/window.c | 43 +++++++++++++++++++++++++++++++++++-------- 9 files changed, 58 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 9fd4de4bb2..fc7823a070 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -395,7 +395,7 @@ void nvim_win_hide(Window window, Error *err) TryState tstate; try_enter(&tstate); if (tabpage == curtab) { - win_close(win, false); + win_close(win, false, false); } else { win_close_othertab(win, false, tabpage); } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 96ddd9a2f5..a9addc0e68 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -846,7 +846,7 @@ void goto_buffer(exarg_T *eap, int start, int dir, int count) enter_cleanup(&cs); // Quitting means closing the split window, nothing else. - win_close(curwin, true); + win_close(curwin, true, false); swap_exists_action = SEA_NONE; swap_exists_did_quit = true; @@ -1237,7 +1237,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) while (buf == curbuf && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) { - if (win_close(curwin, false) == FAIL) { + if (win_close(curwin, false, false) == FAIL) { break; } } @@ -4822,7 +4822,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) && (first_tabpage->tp_next == NULL || !had_tab)) { use_firstwin = true; } else { - win_close(wp, !buf_hide(buf) && !bufIsChanged(buf)); + win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false); // check if autocommands removed the next window if (!win_valid(wpnext)) { // start all over... @@ -5013,7 +5013,7 @@ void ex_buffer_all(exarg_T *eap) && !ONE_WINDOW && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { - win_close(wp, false); + win_close(wp, false, false); wpnext = firstwin; // just in case an autocommand does // something strange with windows tpnext = first_tabpage; // start all over... @@ -5094,7 +5094,7 @@ void ex_buffer_all(exarg_T *eap) enter_cleanup(&cs); // User selected Quit at ATTENTION prompt; close this window. - win_close(curwin, true); + win_close(curwin, true, false); open_wins--; swap_exists_action = SEA_NONE; swap_exists_did_quit = true; @@ -5136,7 +5136,7 @@ void ex_buffer_all(exarg_T *eap) // BufWrite Autocommands made the window invalid, start over wp = lastwin; } else if (r) { - win_close(wp, !buf_hide(wp->w_buffer)); + win_close(wp, !buf_hide(wp->w_buffer), false); open_wins--; wp = lastwin; } else { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 81fce3565a..fd1e34803f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5856,7 +5856,7 @@ void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (bt_help(win->w_buffer)) { - win_close(win, false); + win_close(win, false, eap->forceit); return; } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d884838136..4dba0b97ed 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6619,7 +6619,7 @@ static void ex_quit(exarg_T *eap) } not_exiting(); // close window; may free buffer - win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit); + win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit, eap->forceit); } } @@ -6734,7 +6734,7 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp) // free buffer when not hiding it or when it's a scratch buffer if (tp == NULL) { - win_close(win, !need_hide && !buf_hide(buf)); + win_close(win, !need_hide && !buf_hide(buf), forceit); } else { win_close_othertab(win, !need_hide && !buf_hide(buf), tp); } @@ -6898,7 +6898,7 @@ static void ex_hide(exarg_T *eap) // ":hide" or ":hide | cmd": hide current window if (!eap->skip) { if (eap->addr_count == 0) { - win_close(curwin, false); // don't free buffer + win_close(curwin, false, eap->forceit); // don't free buffer } else { int winnr = 0; win_T *win = NULL; @@ -6913,7 +6913,7 @@ static void ex_hide(exarg_T *eap) if (win == NULL) { win = lastwin; } - win_close(win, false); + win_close(win, false, eap->forceit); } } } @@ -6971,7 +6971,7 @@ static void ex_exit(exarg_T *eap) } not_exiting(); // Quit current window, may free the buffer. - win_close(curwin, !buf_hide(curwin->w_buffer)); + win_close(curwin, !buf_hide(curwin->w_buffer), eap->forceit); } } @@ -7572,7 +7572,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) // Reset the error/interrupt/exception state here so that // aborting() returns FALSE when closing a window. enter_cleanup(&cs); - win_close(curwin, !need_hide && !buf_hide(curbuf)); + win_close(curwin, !need_hide && !buf_hide(curbuf), false); // Restore the error/interrupt/exception state if not // discarded by a new aborting error, interrupt, or diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f81f49a174..ed4475eb1a 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6563,7 +6563,7 @@ static int open_cmdwin(void) set_bufref(&bufref, curbuf); win_goto(old_curwin); if (win_valid(wp) && wp != curwin) { - win_close(wp, true); + win_close(wp, true, false); } // win_close() may have already wiped the buffer when 'bh' is diff --git a/src/nvim/main.c b/src/nvim/main.c index 748f5098fd..8991ae7f00 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1560,7 +1560,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { - win_close(curwin, true); + win_close(curwin, true, false); advance = false; } @@ -1572,7 +1572,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { arg_idx++; - win_close(curwin, true); + win_close(curwin, true, false); advance = false; continue; } @@ -1619,7 +1619,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) did_emsg = FALSE; // avoid hit-enter prompt getout(1); } - win_close(curwin, true); + win_close(curwin, true, false); advance = false; } if (arg_idx == GARGCOUNT - 1) { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index b12f407460..7e29aed51b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3022,7 +3022,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo if (retval != OK) { if (opened_window) { - win_close(curwin, true); // Close opened window + win_close(curwin, true, false); // Close opened window } if (qf_ptr != NULL && qf_ptr->qf_fnum != 0) { // Couldn't open file, so put index back where it was. This could @@ -3577,7 +3577,7 @@ void ex_cclose(exarg_T *eap) // Find existing quickfix window and close it. win = qf_find_win(qi); if (win != NULL) { - win_close(win, false); + win_close(win, false, false); } } @@ -3651,7 +3651,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) // win_split, so add a check to ensure that the win is still here if (IS_LL_STACK(qi) && !win_valid(win)) { // close the window that was supposed to be for the loclist - win_close(curwin, false); + win_close(curwin, false, false); return FAIL; } @@ -5767,7 +5767,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start) if (firstwin->w_next != NULL) { for (win_T *wp = firstwin; wp != NULL; wp = wp->w_next) { if (wp->w_buffer == buf) { - if (win_close(wp, false) == OK) { + if (win_close(wp, false, false) == OK) { did_one = true; } break; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 54d7e54fb4..32d72218c8 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2960,7 +2960,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) } else { RedrawingDisabled--; if (postponed_split) { // close the window - win_close(curwin, false); + win_close(curwin, false, false); postponed_split = 0; } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 6996a93928..83d5910400 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -304,7 +304,7 @@ newwindow: newtab = curtab; goto_tabpage_tp(oldtab, true, true); if (curwin == wp) { - win_close(curwin, false); + win_close(curwin, false, false); } if (valid_tabpage(newtab)) { goto_tabpage_tp(newtab, true, true); @@ -449,7 +449,7 @@ wingotofile: RESET_BINDING(curwin); if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { // Failed to open the file, close the window opened for it. - win_close(curwin, false); + win_close(curwin, false, false); goto_tabpage_win(oldtab, oldwin); } else if (nchar == 'F' && lnum >= 0) { curwin->w_cursor.lnum = lnum; @@ -2290,7 +2290,7 @@ void close_windows(buf_T *buf, int keep_curwin) for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW;) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { - if (win_close(wp, false) == FAIL) { + if (win_close(wp, false, false) == FAIL) { // If closing the window fails give up, to avoid looping forever. break; } @@ -2368,6 +2368,22 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating); } +/// Check if floating windows can be closed. +/// +/// @return true if all floating windows can be closed +static bool can_close_floating_windows(tabpage_T *tab) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, tab) { + buf_T *buf = wp->w_buffer; + int need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); + + if (need_hide && !buf_hide(buf)) { + return false; + } + } + return true; +} + /// Close the possibly last window in a tab page. /// /// @param win window to close @@ -2432,7 +2448,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev // // Called by :quit, :close, :xit, :wq and findtag(). // Returns FAIL when the window was not closed. -int win_close(win_T *win, bool free_buf) +int win_close(win_T *win, bool free_buf, bool force) { win_T *wp; bool other_buffer = false; @@ -2462,9 +2478,18 @@ int win_close(win_T *win, bool free_buf) } if ((firstwin == win && lastwin_nofloating() == win) && lastwin->w_floating) { - // TODO(bfredl): we might close the float also instead - emsg(e_floatonly); - return FAIL; + if (force || can_close_floating_windows(curtab)) { + win_T *nextwp; + for (win_T *wpp = firstwin; wpp != NULL; wpp = nextwp) { + nextwp = wpp->w_next; + if (wpp->w_floating) { + win_close(wpp, free_buf, force); + } + } + } else { + emsg(e_floatonly); + return FAIL; + } } // When closing the last window in a tab page first go to another tab page @@ -3611,7 +3636,9 @@ void close_others(int message, int forceit) continue; } } - win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); + win_close(wp, + !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), + false); } if (message && !ONE_WINDOW) { -- cgit From 059d36e326e31fc9bc6055d7c999f86d94fa9bd5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 11 Feb 2022 12:44:47 +0800 Subject: feat(events): add DirChangedPre In Nvim, like DirChanged, this also triggers when switching windows. This marks Vim patch 8.2.4335 as ported. vim-patch:8.2.4335: no autocommand event triggered before changing directory Problem: No autocommand event triggered before changing directory. (Ronnie Magatti) Solution: Add DirChangedPre. (closes vim/vim#9721) https://github.com/vim/vim/commit/28e8f73ae2d90009fd62cd60f97c2643ba44de68 --- src/nvim/auevents.lua | 5 +---- src/nvim/autocmd.c | 14 +++++++------- src/nvim/ex_docmd.c | 15 +++++++++------ src/nvim/file_search.c | 21 +++++++++++++++------ src/nvim/testdir/test_autocmd.vim | 12 ++++++++---- src/nvim/window.c | 23 ++++++++++++++++------- 6 files changed, 56 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 8fe623fc96..518d0b52b2 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -40,6 +40,7 @@ return { 'DiagnosticChanged', -- diagnostics in a buffer were modified 'DiffUpdated', -- diffs have been updated 'DirChanged', -- directory changed + 'DirChangedPre', -- directory is going to change 'EncodingChanged', -- after changing the 'encoding' option 'ExitPre', -- before exiting 'FileAppendCmd', -- append to a file using command @@ -132,18 +133,14 @@ return { nvim_specific = { BufModifiedSet=true, DiagnosticChanged=true, - DirChanged=true, RecordingEnter=true, RecordingLeave=true, Signal=true, - TabClosed=true, - TabNew=true, TabNewEntered=true, TermClose=true, TermOpen=true, UIEnter=true, UILeave=true, - WinClosed=true, WinScrolled=true, }, } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index dfba18b11d..6dc614813b 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1517,13 +1517,13 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, || event == EVENT_CMDLINELEAVE || event == EVENT_CMDWINENTER || event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED || event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE - || event == EVENT_DIRCHANGED || event == EVENT_FILETYPE - || event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED - || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST - || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY - || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX - || event == EVENT_SIGNAL || event == EVENT_TABCLOSED - || event == EVENT_WINCLOSED) { + || event == EVENT_DIRCHANGED || event == EVENT_DIRCHANGEDPRE + || event == EVENT_FILETYPE || event == EVENT_FUNCUNDEFINED + || event == EVENT_MODECHANGED || event == EVENT_OPTIONSET + || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE + || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING + || event == EVENT_SYNTAX || event == EVENT_SIGNAL + || event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) { fname = vim_strsave(fname); } else { fname = (char_u *)FullName_save((char *)fname, false); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d884838136..8b6d7b91a8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7784,7 +7784,7 @@ static char_u *get_prevdir(CdScope scope) /// Deal with the side effects of changing the current directory. /// /// @param scope Scope of the function call (global, tab or window). -void post_chdir(CdScope scope, bool trigger_dirchanged) +static void post_chdir(CdScope scope, bool trigger_dirchanged) { // Always overwrite the window-local CWD. XFREE_CLEAR(curwin->w_localdir); @@ -7825,7 +7825,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) shorten_fnames(true); if (trigger_dirchanged) { - do_autocmd_dirchanged(cwd, scope, kCdCauseManual); + do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false); } } @@ -7869,10 +7869,13 @@ bool changedir_func(char_u *new_dir, CdScope scope) } bool dir_differs = pdir == NULL || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; - if (dir_differs && vim_chdir(new_dir) != 0) { - emsg(_(e_failed)); - xfree(pdir); - return false; + if (dir_differs) { + do_autocmd_dirchanged((char *)new_dir, scope, kCdCauseManual, true); + if (vim_chdir(new_dir) != 0) { + emsg(_(e_failed)); + xfree(pdir); + return false; + } } char_u **pp; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 25dbf680de..d0f7a91d6c 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1600,11 +1600,13 @@ theend: return file_name; } -void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) +void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre) { static bool recursive = false; - if (recursive || !has_event(EVENT_DIRCHANGED)) { + event_T event = pre ? EVENT_DIRCHANGEDPRE : EVENT_DIRCHANGED; + + if (recursive || !has_event(event)) { // No autocommand was defined or we changed // the directory from this autocommand. return; @@ -1638,8 +1640,12 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) new_dir = new_dir_buf; #endif + if (pre) { + tv_dict_add_str(dict, S_LEN("directory"), new_dir); + } else { + tv_dict_add_str(dict, S_LEN("cwd"), new_dir); + } tv_dict_add_str(dict, S_LEN("scope"), buf); // -V614 - tv_dict_add_str(dict, S_LEN("cwd"), new_dir); tv_dict_add_bool(dict, S_LEN("changed_window"), cause == kCdCauseWindow); tv_dict_set_keys_readonly(dict); @@ -1655,8 +1661,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause) abort(); } - apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, - curbuf); + apply_autocmds(event, (char_u *)buf, (char_u *)new_dir, false, curbuf); restore_v_event(dict, &save_v_event); @@ -1682,12 +1687,16 @@ int vim_chdirfile(char_u *fname, CdCause cause) return OK; } + if (cause != kCdCauseOther) { + do_autocmd_dirchanged(dir, kCdScopeWindow, cause, true); + } + if (os_chdir(dir) != 0) { return FAIL; } if (cause != kCdCauseOther) { - do_autocmd_dirchanged(dir, kCdScopeWindow, cause); + do_autocmd_dirchanged(dir, kCdScopeWindow, cause, false); } return OK; diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index a8d51ef598..82e8f9ba77 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1850,14 +1850,16 @@ endfunc function Test_dirchanged_global() call s:Before_test_dirchanged() + autocmd test_dirchanged DirChangedPre global call add(s:li, "pre cd " .. v:event.directory) autocmd test_dirchanged DirChanged global call add(s:li, "cd:") autocmd test_dirchanged DirChanged global call add(s:li, expand("")) call chdir(s:dir_foo) - call assert_equal(["cd:", s:dir_foo], s:li) + let expected = ["pre cd " .. s:dir_foo, "cd:", s:dir_foo] + call assert_equal(expected, s:li) call chdir(s:dir_foo) - call assert_equal(["cd:", s:dir_foo], s:li) + call assert_equal(expected, s:li) exe 'lcd ' .. fnameescape(s:dir_bar) - call assert_equal(["cd:", s:dir_foo], s:li) + call assert_equal(expected, s:li) call s:After_test_dirchanged() endfunc @@ -1879,6 +1881,7 @@ function Test_dirchanged_auto() CheckOption autochdir call s:Before_test_dirchanged() call test_autochdir() + autocmd test_dirchanged DirChangedPre auto call add(s:li, "pre cd " .. v:event.directory) autocmd test_dirchanged DirChanged auto call add(s:li, "auto:") autocmd test_dirchanged DirChanged auto call add(s:li, expand("")) set acd @@ -1886,7 +1889,8 @@ function Test_dirchanged_auto() call assert_equal([], s:li) exe 'edit ' . s:dir_foo . '/Xfile' call assert_equal(s:dir_foo, getcwd()) - call assert_equal(["auto:", s:dir_foo], s:li) + let expected = ["pre cd " .. s:dir_foo, "auto:", s:dir_foo] + call assert_equal(expected, s:li) set noacd bwipe! call s:After_test_dirchanged() diff --git a/src/nvim/window.c b/src/nvim/window.c index 6996a93928..83495801e8 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4645,20 +4645,29 @@ void fix_current_dir(void) globaldir = (char_u *)xstrdup(cwd); } } + bool dir_differs = pathcmp(new_dir, cwd, -1) != 0; + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage, + kCdCauseWindow, true); + } if (os_chdir(new_dir) == 0) { - if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) { - do_autocmd_dirchanged(new_dir, curwin->w_localdir - ? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow); + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage, + kCdCauseWindow, false); } - last_chdir_reason = NULL; - shorten_fnames(true); } + last_chdir_reason = NULL; + shorten_fnames(true); } else if (globaldir != NULL) { // Window doesn't have a local directory and we are not in the global // directory: Change to the global directory. + bool dir_differs = pathcmp((char *)globaldir, cwd, -1) != 0; + if (!p_acd && dir_differs) { + do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow, true); + } if (os_chdir((char *)globaldir) == 0) { - if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) { - do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow); + if (!p_acd && dir_differs) { + do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow, false); } } XFREE_CLEAR(globaldir); -- cgit From 5546492c4449ee0d6262a23b0b26f49d1b43cf27 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 11 Feb 2022 20:32:44 +0800 Subject: test: convert Test_file_changed_dialog() to Lua functional test --- src/nvim/testdir/test_filechanged.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index b95cd5faf8..b4c32fcc16 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -91,7 +91,7 @@ func Test_FileChangedShell_reload() endfunc func Test_file_changed_dialog() - throw 'skipped: TODO: ' + throw 'Skipped: requires a UI to a active' if !has('unix') || has('gui_running') return endif -- cgit From 612696bedc8f178cb08645dfb056f01efacf5122 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Feb 2022 06:36:17 +0800 Subject: vim-patch:8.1.2073: when editing a buffer 'colorcolumn' may not work Problem: When editing a buffer 'colorcolumn' may not work. Solution: Set the buffer before copying option values. Call check_colorcolumn() after copying window options. https://github.com/vim/vim/commit/010ee9657acf1a9f799079d718998c94e50ccadc --- src/nvim/buffer.c | 10 +++++----- src/nvim/option.c | 4 ++-- src/nvim/testdir/test_highlight.vim | 25 +++++++++++++++++++++++++ src/nvim/window.c | 2 -- 4 files changed, 32 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a9addc0e68..ee704bd1bd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1497,6 +1497,11 @@ void set_curbuf(buf_T *buf, int action) */ void enter_buffer(buf_T *buf) { + // Get the buffer in the current window. + curwin->w_buffer = buf; + curbuf = buf; + curbuf->b_nwindows++; + // Copy buffer and window local option values. Not for a help buffer. buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); if (!buf->b_help) { @@ -1507,11 +1512,6 @@ void enter_buffer(buf_T *buf) } foldUpdateAll(curwin); // update folds (later). - // Get the buffer in the current window. - curwin->w_buffer = buf; - curbuf = buf; - curbuf->b_nwindows++; - if (curwin->w_p_diff) { diff_buf_add(curbuf); } diff --git a/src/nvim/option.c b/src/nvim/option.c index b9fed8b378..dddf926b9a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1984,10 +1984,9 @@ static void didset_options(void) (void)did_set_spell_option(true); // set cedit_key (void)check_cedit(); - briopt_check(curwin); // initialize the table for 'breakat'. fill_breakat_flags(); - fill_culopt_flags(NULL, curwin); + didset_window_options(curwin); } // More side effects of setting options. @@ -6174,6 +6173,7 @@ void win_copy_options(win_T *wp_from, win_T *wp_to) { copy_winopt(&wp_from->w_onebuf_opt, &wp_to->w_onebuf_opt); copy_winopt(&wp_from->w_allbuf_opt, &wp_to->w_allbuf_opt); + didset_window_options(wp_to); } /// Copy the options from one winopt_T to another. diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 899eb530ec..6971ecd357 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -597,6 +597,31 @@ func Test_cursorline_with_visualmode() call delete('Xtest_cursorline_with_visualmode') endfunc +func Test_colorcolumn() + CheckScreendump + + " check that setting 'colorcolumn' when entering a buffer works + let lines =<< trim END + split + edit X + call setline(1, ["1111111111","22222222222","3333333333"]) + set nomodified + set colorcolumn=3,9 + set number cursorline cursorlineopt=number + wincmd w + buf X + END + call writefile(lines, 'Xtest_colorcolumn') + let buf = RunVimInTerminal('-S Xtest_colorcolumn', {'rows': 10}) + call term_sendkeys(buf, ":\") + call term_wait(buf) + call VerifyScreenDump(buf, 'Test_colorcolumn_1', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_colorcolumn') +endfunc + func Test_colorcolumn_bri() CheckScreendump diff --git a/src/nvim/window.c b/src/nvim/window.c index 83d5910400..4861b71995 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1482,8 +1482,6 @@ static void win_init(win_T *newp, win_T *oldp, int flags) copyFoldingState(oldp, newp); win_init_some(newp, oldp); - - didset_window_options(newp); } /* -- cgit From 3b13c7fc8b15d2ab90c070131c0268711fcf4f10 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Feb 2022 12:03:02 +0100 Subject: vim-patch:8.2.4352: ReScript files are not recognized Problem: ReScript files are not recognized. Solution: Add the *.res and *.resi patterns. (Ananda Umamil, closes vim/vim#9752) https://github.com/vim/vim/commit/0c3cc2fec31521b0697edc406f85b7a43e979860 --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 4ef35b3a46..b663032c24 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -435,6 +435,7 @@ let s:filename_checks = { \ 'readline': ['.inputrc', 'inputrc'], \ 'remind': ['.reminders', 'file.remind', 'file.rem', '.reminders-file'], \ 'rego': ['file.rego'], + \ 'rescript': ['file.res', 'file.resi'], \ 'resolv': ['resolv.conf'], \ 'reva': ['file.frt'], \ 'rexx': ['file.rex', 'file.orx', 'file.rxo', 'file.rxj', 'file.jrexx', 'file.rexxj', 'file.rexx', 'file.testGroup', 'file.testUnit'], -- cgit From cb18545253259af339957316ab8361fb0cca48e5 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 12 Feb 2022 11:25:01 +0100 Subject: feat(api): add strikethrough, nocombine to set_hl --- src/nvim/api/keysets.lua | 4 ++++ src/nvim/highlight.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 075e2c48d2..f6dce1905e 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -81,10 +81,12 @@ return { highlight = { "bold"; "standout"; + "strikethrough"; "underline"; "undercurl"; "italic"; "reverse"; + "nocombine"; "default"; "global"; "cterm"; @@ -100,10 +102,12 @@ return { highlight_cterm = { "bold"; "standout"; + "strikethrough"; "underline"; "undercurl"; "italic"; "reverse"; + "nocombine"; }; } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 87c090e594..fcd91cdf16 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -815,6 +815,8 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL); CHECK_FLAG(dict, mask, italic, , HL_ITALIC); CHECK_FLAG(dict, mask, reverse, , HL_INVERSE); + CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH); + CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE); CHECK_FLAG(dict, mask, default, _, HL_DEFAULT); CHECK_FLAG(dict, mask, global, , HL_GLOBAL); @@ -871,6 +873,8 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(cterm, cterm_mask, undercurl, , HL_UNDERCURL); CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC); CHECK_FLAG(cterm, cterm_mask, reverse, , HL_INVERSE); + CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH); + CHECK_FLAG(cterm, cterm_mask, nocombine, , HL_NOCOMBINE); } else if (HAS_KEY(dict->cterm)) { api_set_error(err, kErrorTypeValidation, "'cterm' must be a Dictionary."); -- cgit From cdb2c100118ab788772a6a0a1d60f555370fd201 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 1 Feb 2022 12:44:14 +0000 Subject: vim-patch:8.2.0915: search() cannot skip over matches like searchpair() can Problem: Search() cannot skip over matches like searchpair() can. Solution: Add an optional "skip" argument. (Christian Brabandt, closes vim/vim#861) https://github.com/vim/vim/commit/adc17a5f9d207fd1623fd923457a46efc9214777 Enable skip arg usage in autoload/freebasic.vim evalarg_T doesn't really matter because it's deleted in v8.2.0918 (and reincarnated for Vim9 script in v8.2.1047), but I found out too late :P Anyway: - Port evalarg_T into eval.h and use const char * and Callback fields - Use EVALARG_INIT to initialize - Return bool over OK/FAIL from evalarg functions - Remove check from evalarg_clean as callback_free ignores None callbacks anyway - Move eva_buf field into evalarg_get as a local (not sure what reason it has being in the struct) N/A patches for version.c: vim-patch:8.2.4355: unnecessary call to check_colorcolumn() Problem: Unnecessary call to check_colorcolumn(). Solution: Remove the call. (Sean Dewar, closes vim/vim#9748) https://github.com/vim/vim/commit/0f7ff851cb721bb3c07261adbf82b591229f530d --- src/nvim/eval.c | 61 ++++++++++++++++++++++++++++++++++++++++ src/nvim/eval.h | 16 +++++++++++ src/nvim/eval.lua | 4 +-- src/nvim/eval/funcs.c | 46 ++++++++++++++++++++++++++++-- src/nvim/testdir/test_syntax.vim | 50 ++++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c197754685..2d8d1694d1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3381,6 +3381,67 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len, return ret; } +/// Process a function argument that can be a string expression or a function +/// reference. +/// "tv" must remain valid until calling evalarg_clean()! +/// @return false when the argument is invalid. +bool evalarg_get(typval_T *const tv, evalarg_T *const eva) + FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_STRING || tv->v_type == VAR_NUMBER || tv->v_type == VAR_BOOL + || tv->v_type == VAR_SPECIAL) { + char numbuf[NUMBUFLEN]; + eva->eva_string = tv_get_string_buf(tv, numbuf); + return true; + } + + return callback_from_typval(&eva->eva_callback, tv); +} + +/// @return whether "eva" has a valid expression or callback. +bool evalarg_valid(const evalarg_T *const eva) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST +{ + return eva->eva_string != NULL || eva->eva_callback.type != kCallbackNone; +} + +/// Invoke the expression or callback "eva" and return the result in "tv". +/// @return false if something failed +bool evalarg_call(evalarg_T *const eva, typval_T *const tv) + FUNC_ATTR_NONNULL_ALL +{ + if (eva->eva_string != NULL) { + return eval0((char_u *)eva->eva_string, tv, NULL, true); + } + + typval_T argv[1]; + argv[0].v_type = VAR_UNKNOWN; + return callback_call(&eva->eva_callback, 0, argv, tv); +} + +/// Like evalarg_call(), but just return true or false. +/// Sets "error" to true if evaluation failed. +bool evalarg_call_bool(evalarg_T *const eva, bool *const error) + FUNC_ATTR_NONNULL_ALL +{ + typval_T tv; + if (!evalarg_call(eva, &tv)) { + *error = true; + return false; + } + + const bool r = tv_get_number(&tv); + tv_clear(&tv); + *error = false; + return r; +} + +void evalarg_clean(evalarg_T *const eva) + FUNC_ATTR_NONNULL_ALL +{ + callback_free(&eva->eva_callback); +} + // TODO(ZyX-I): move to eval/expressions /* diff --git a/src/nvim/eval.h b/src/nvim/eval.h index a9ec5d47a6..f74f23d084 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -272,6 +272,22 @@ typedef int (*ex_unletlock_callback)(lval_T *, char_u *, exarg_T *, int); // Used for checking if local variables or arguments used in a lambda. extern bool *eval_lavars_used; +/// Function argument that can be a string, funcref or partial. +/// - declare: evalarg_T name; +/// - init: name = EVALARG_INIT; +/// - set: evalarg_get(&argvars[3], &name); +/// - use: if (evalarg_valid(&name)) res = evalarg_call(&name); +/// - cleanup: evalarg_clean(&name); +typedef struct { + const char *eva_string; + Callback eva_callback; +} evalarg_T; + +#define EVALARG_INIT (evalarg_T) { \ + .eva_string = NULL, \ + .eva_callback = CALLBACK_NONE, \ +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.h.generated.h" #endif diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 1e39854c86..05e91a658f 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -307,12 +307,12 @@ return { screenpos={args=3, base=1}, screenrow={}, screenstring={args=2, base=1}, - search={args={1, 4}, base=1}, + search={args={1, 5}, base=1}, searchcount={args={0, 1}, base=1}, searchdecl={args={1, 3}, base=1}, searchpair={args={3, 7}}, searchpairpos={args={3, 7}}, - searchpos={args={1, 4}, base=1}, + searchpos={args={1, 5}, base=1}, serverlist={}, serverstart={args={0, 1}}, serverstop={args=1}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index db4fb06a73..8004c1d32e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8237,6 +8237,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; searchit_arg_T sia; + evalarg_T skip = EVALARG_INIT; const char *const pat = tv_get_string(&argvars[0]); dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. @@ -8254,7 +8255,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) options |= SEARCH_COL; } - // Optional arguments: line number to stop searching and timeout. + // Optional arguments: line number to stop searching, timeout and skip. if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[2], NULL); if (lnum_stop < 0) { @@ -8265,6 +8266,9 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (time_limit < 0) { goto theend; } + if (argvars[4].v_type != VAR_UNKNOWN && !evalarg_get(&argvars[4], &skip)) { + goto theend; + } } } @@ -8284,11 +8288,46 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } pos = save_cursor = curwin->w_cursor; + pos_T firstpos = { 0 }; memset(&sia, 0, sizeof(sia)); sia.sa_stop_lnum = (linenr_T)lnum_stop; sia.sa_tm = &tm; - subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, - options, RE_SEARCH, &sia); + + // Repeat until {skip} returns false. + for (;;) { + subpatnum + = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); + // finding the first match again means there is no match where {skip} + // evaluates to zero. + if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { + subpatnum = FAIL; + } + + if (subpatnum == FAIL || !evalarg_valid(&skip)) { + // didn't find it or no skip argument + break; + } + firstpos = pos; + + // If the skip pattern matches, ignore this match. + { + bool err; + const pos_T save_pos = curwin->w_cursor; + + curwin->w_cursor = pos; + const bool do_skip = evalarg_call_bool(&skip, &err); + curwin->w_cursor = save_pos; + if (err) { + // Evaluating {skip} caused an error, break here. + subpatnum = FAIL; + break; + } + if (!do_skip) { + break; + } + } + } + if (subpatnum != FAIL) { if (flags & SP_SUBPAT) { retval = subpatnum; @@ -8317,6 +8356,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } theend: p_ws = save_p_ws; + evalarg_clean(&skip); return retval; } diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 757866f5dc..b047b53b6f 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -728,6 +728,56 @@ func Test_syntax_foldlevel() quit! endfunc +func Test_search_syntax_skip() + new + let lines =<< trim END + + /* This is VIM */ + Another Text for VIM + let a = "VIM" + END + call setline(1, lines) + syntax on + syntax match Comment "^/\*.*\*/" + syntax match String '".*"' + + " Skip argument using string evaluation. + 1 + call search('VIM', 'w', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"') + call assert_equal('Another Text for VIM', getline('.')) + 1 + call search('VIM', 'w', '', 0, 'synIDattr(synID(line("."), col("."), 1), "name") !~? "string"') + call assert_equal(' let a = "VIM"', getline('.')) + + " Skip argument using Lambda. + 1 + call search('VIM', 'w', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") =~? "comment"}) + call assert_equal('Another Text for VIM', getline('.')) + + 1 + call search('VIM', 'w', '', 0, { -> synIDattr(synID(line("."), col("."), 1), "name") !~? "string"}) + call assert_equal(' let a = "VIM"', getline('.')) + + " Skip argument using funcref. + func InComment() + return synIDattr(synID(line("."), col("."), 1), "name") =~? "comment" + endfunc + func InString() + return synIDattr(synID(line("."), col("."), 1), "name") !~? "string" + endfunc + 1 + call search('VIM', 'w', '', 0, function('InComment')) + call assert_equal('Another Text for VIM', getline('.')) + + 1 + call search('VIM', 'w', '', 0, function('InString')) + call assert_equal(' let a = "VIM"', getline('.')) + + delfunc InComment + delfunc InString + bwipe! +endfunc + func Test_syn_include_contains_TOP() let l:case = "TOP in included syntax means its group list name" new -- cgit From 0511a31ca28e76b12c05622719fc6797d59fb19a Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 1 Feb 2022 15:07:33 +0000 Subject: vim-patch:8.2.0918: duplicate code for evaluating expression argument Problem: Duplicate code for evaluating expression argument. Solution: Merge the code and make the use more flexible. https://github.com/vim/vim/commit/a9c010494767e43a51c443cac35ebc80d0831d0b --- src/nvim/eval.c | 70 +++++++-------------------------------------------- src/nvim/eval.h | 16 ------------ src/nvim/eval/funcs.c | 28 +++++++-------------- 3 files changed, 18 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2d8d1694d1..4b83bda804 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -766,6 +766,15 @@ static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) return ret; } +/// @return whether a typval is a valid expression to pass to eval_expr_typval() +/// or eval_expr_to_bool(). An empty string returns false; +bool eval_expr_valid_arg(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST +{ + return tv->v_type != VAR_UNKNOWN + && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); +} + int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { @@ -3381,67 +3390,6 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len, return ret; } -/// Process a function argument that can be a string expression or a function -/// reference. -/// "tv" must remain valid until calling evalarg_clean()! -/// @return false when the argument is invalid. -bool evalarg_get(typval_T *const tv, evalarg_T *const eva) - FUNC_ATTR_NONNULL_ALL -{ - if (tv->v_type == VAR_STRING || tv->v_type == VAR_NUMBER || tv->v_type == VAR_BOOL - || tv->v_type == VAR_SPECIAL) { - char numbuf[NUMBUFLEN]; - eva->eva_string = tv_get_string_buf(tv, numbuf); - return true; - } - - return callback_from_typval(&eva->eva_callback, tv); -} - -/// @return whether "eva" has a valid expression or callback. -bool evalarg_valid(const evalarg_T *const eva) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST -{ - return eva->eva_string != NULL || eva->eva_callback.type != kCallbackNone; -} - -/// Invoke the expression or callback "eva" and return the result in "tv". -/// @return false if something failed -bool evalarg_call(evalarg_T *const eva, typval_T *const tv) - FUNC_ATTR_NONNULL_ALL -{ - if (eva->eva_string != NULL) { - return eval0((char_u *)eva->eva_string, tv, NULL, true); - } - - typval_T argv[1]; - argv[0].v_type = VAR_UNKNOWN; - return callback_call(&eva->eva_callback, 0, argv, tv); -} - -/// Like evalarg_call(), but just return true or false. -/// Sets "error" to true if evaluation failed. -bool evalarg_call_bool(evalarg_T *const eva, bool *const error) - FUNC_ATTR_NONNULL_ALL -{ - typval_T tv; - if (!evalarg_call(eva, &tv)) { - *error = true; - return false; - } - - const bool r = tv_get_number(&tv); - tv_clear(&tv); - *error = false; - return r; -} - -void evalarg_clean(evalarg_T *const eva) - FUNC_ATTR_NONNULL_ALL -{ - callback_free(&eva->eva_callback); -} - // TODO(ZyX-I): move to eval/expressions /* diff --git a/src/nvim/eval.h b/src/nvim/eval.h index f74f23d084..a9ec5d47a6 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -272,22 +272,6 @@ typedef int (*ex_unletlock_callback)(lval_T *, char_u *, exarg_T *, int); // Used for checking if local variables or arguments used in a lambda. extern bool *eval_lavars_used; -/// Function argument that can be a string, funcref or partial. -/// - declare: evalarg_T name; -/// - init: name = EVALARG_INIT; -/// - set: evalarg_get(&argvars[3], &name); -/// - use: if (evalarg_valid(&name)) res = evalarg_call(&name); -/// - cleanup: evalarg_clean(&name); -typedef struct { - const char *eva_string; - Callback eva_callback; -} evalarg_T; - -#define EVALARG_INIT (evalarg_T) { \ - .eva_string = NULL, \ - .eva_callback = CALLBACK_NONE, \ -} - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.h.generated.h" #endif diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8004c1d32e..8beacc9988 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8237,7 +8237,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; searchit_arg_T sia; - evalarg_T skip = EVALARG_INIT; + bool use_skip = false; const char *const pat = tv_get_string(&argvars[0]); dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. @@ -8266,9 +8266,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (time_limit < 0) { goto theend; } - if (argvars[4].v_type != VAR_UNKNOWN && !evalarg_get(&argvars[4], &skip)) { - goto theend; - } + use_skip = eval_expr_valid_arg(&argvars[4]); } } @@ -8303,19 +8301,19 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) subpatnum = FAIL; } - if (subpatnum == FAIL || !evalarg_valid(&skip)) { + if (subpatnum == FAIL || !use_skip) { // didn't find it or no skip argument break; } firstpos = pos; - // If the skip pattern matches, ignore this match. + // If the skip expression matches, ignore this match. { - bool err; const pos_T save_pos = curwin->w_cursor; curwin->w_cursor = pos; - const bool do_skip = evalarg_call_bool(&skip, &err); + bool err = false; + const bool do_skip = eval_expr_to_bool(&argvars[4], &err); curwin->w_cursor = save_pos; if (err) { // Evaluating {skip} caused an error, break here. @@ -8356,7 +8354,6 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) } theend: p_ws = save_p_ws; - evalarg_clean(&skip); return retval; } @@ -8788,13 +8785,9 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) || argvars[4].v_type == VAR_UNKNOWN) { skip = NULL; } else { + // Type is checked later. skip = &argvars[4]; - if (skip->v_type != VAR_FUNC - && skip->v_type != VAR_PARTIAL - && skip->v_type != VAR_STRING) { - semsg(_(e_invarg2), tv_get_string(&argvars[4])); - goto theend; // Type error. - } + if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[5], NULL); if (lnum_stop < 0) { @@ -8905,10 +8898,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir } if (skip != NULL) { - // Empty string means to not use the skip expression. - if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC) { - use_skip = skip->vval.v_string != NULL && *skip->vval.v_string != NUL; - } + use_skip = eval_expr_valid_arg(skip); } save_cursor = curwin->w_cursor; -- cgit From db06fb47b9ae2fd69570d68a667c9d1695fd123f Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 1 Feb 2022 15:54:34 +0000 Subject: vim-patch:8.2.0922: search test fails Problem: Search test fails. Solution: Remove failure tests for calls that no longer fail. https://github.com/vim/vim/commit/48af321a3382008dc642362d3f54bb6a61ff36e4 --- src/nvim/testdir/test_search.vim | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 2a1a93b5e3..a3d5ca96a1 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -378,7 +378,6 @@ func Test_searchpair_errors() call assert_fails("call searchpair('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String') call assert_fails("call searchpair('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String') call assert_fails("call searchpair('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags') - call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 0, 99, 100)", 'E475: Invalid argument: 0') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') call assert_fails("call searchpair('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') call assert_fails("call searchpair('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') @@ -390,7 +389,6 @@ func Test_searchpairpos_errors() call assert_fails("call searchpairpos('start', {-> 0}, 'end', 'bW', 'skip', 99, 100)", 'E729: using Funcref as a String') call assert_fails("call searchpairpos('start', 'middle', {'one': 1}, 'bW', 'skip', 99, 100)", 'E731: using Dictionary as a String') call assert_fails("call searchpairpos('start', 'middle', 'end', 'flags', 'skip', 99, 100)", 'E475: Invalid argument: flags') - call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 0, 99, 100)", 'E475: Invalid argument: 0') call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', -99, 100)", 'E475: Invalid argument: -99') call assert_fails("call searchpairpos('start', 'middle', 'end', 'bW', 'func', 99, -100)", 'E475: Invalid argument: -100') call assert_fails("call searchpairpos('start', 'middle', 'end', 'e')", 'E475: Invalid argument: e') -- cgit From c23ec9d86e41e9dd03ab33ef8a80640e1366c476 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Feb 2022 21:16:53 +0800 Subject: vim-patch:8.2.0580: window size wrong if 'ea' is off and 'splitright' is on Problem: Window size wrong if 'ea' is off and 'splitright' is on and splitting then closing a window. Solution: Put abandoned window space in the right place. (Mark Waggoner) https://github.com/vim/vim/commit/edd327cc070d9a05c12e88bc5c43a1e2a3086ae6 --- src/nvim/testdir/test_winbuf_close.vim | 19 +++++++++++++++++++ src/nvim/window.c | 14 +++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim index 7f5b80e8d3..f4878c2397 100644 --- a/src/nvim/testdir/test_winbuf_close.vim +++ b/src/nvim/testdir/test_winbuf_close.vim @@ -194,3 +194,22 @@ func Test_tabwin_close() call assert_true(v:true) %bwipe! endfunc + +" Test when closing a split window (above/below) restores space to the window +" below when 'noequalalways' and 'splitright' are set. +func Test_window_close_splitright_noequalalways() + set noequalalways + set splitright + new + let w1 = win_getid() + new + let w2 = win_getid() + execute "normal \b" + let h = winheight(0) + let w = win_getid() + new + q + call assert_equal(h, winheight(0), "Window height does not match eight before opening and closing another window") + call assert_equal(w, win_getid(), "Did not return to original window after opening and closing a window") +endfunc + diff --git a/src/nvim/window.c b/src/nvim/window.c index 4861b71995..e09af7a7bb 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -3079,9 +3079,21 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp) return frp->fr_prev; } + // By default the next window will get the space that was abandoned by this + // window frame_T *target_fr = frp->fr_next; frame_T *other_fr = frp->fr_prev; - if (p_spr || p_sb) { + + // If this is part of a column of windows and 'splitbelow' is true then the + // previous window will get the space. + if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb) { + target_fr = frp->fr_prev; + other_fr = frp->fr_next; + } + + // If this is part of a row of windows, and 'splitright' is true then the + // previous window will get the space. + if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr) { target_fr = frp->fr_prev; other_fr = frp->fr_next; } -- cgit From ed169d89979caf8e67dbdf74491367d2e0cfad58 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 12 Feb 2022 21:25:44 +0800 Subject: vim-patch:8.2.2342: "char" functions may return wrong column in Insert mode Problem: "char" functions return the wront column in Insert mode when the cursor is beyond the end of the line. Solution: Compute the column correctly. (Yegappan Lakshmanan, closes vim/vim#7669) https://github.com/vim/vim/commit/9145846b6aa411e3ab5c0d145b37808654352877 --- src/nvim/eval.c | 34 ++++++++++++------ src/nvim/eval/funcs.c | 4 +-- src/nvim/testdir/test_cursor_func.vim | 67 ++++++++++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4b83bda804..07f0799bc4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8192,7 +8192,7 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) /// Convert the specified byte index of line 'lnum' in buffer 'buf' to a /// character index. Works only for loaded buffers. Returns -1 on failure. -/// The index of the first character is one. +/// The index of the first byte and the first character is zero. int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) { if (buf == NULL || buf->b_ml.ml_mfp == NULL) { @@ -8206,15 +8206,29 @@ int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) char_u *str = ml_get_buf(buf, lnum, false); if (*str == NUL) { - return 1; + return 0; + } + + // count the number of characters + char_u *t = str; + int count; + for (count = 0; *t != NUL && t <= str + byteidx; count++) { + t += utfc_ptr2len(t); + } + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) { + count++; } - return mb_charlen_len(str, byteidx + 1); + return count - 1; } /// Convert the specified character index of line 'lnum' in buffer 'buf' to a -/// byte index. Works only for loaded buffers. Returns -1 on failure. The index -/// of the first byte and the first character is one. +/// byte index. Works only for loaded buffers. Returns -1 on failure. +/// The index of the first byte and the first character is zero. int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) { if (buf == NULL || buf->b_ml.ml_mfp == NULL) { @@ -8233,7 +8247,7 @@ int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) t += utfc_ptr2len(t); } - return t - str + 1; + return t - str; } /// Translate a VimL object into a position @@ -8315,7 +8329,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (name[0] == '.') { // Cursor. pos = curwin->w_cursor; if (charcol) { - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); } return &pos; } @@ -8326,7 +8340,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos = curwin->w_cursor; } if (charcol) { - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); } return &pos; } @@ -8336,7 +8350,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret return NULL; } if (charcol) { - pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1; + pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col); } return pp; } @@ -8423,7 +8437,7 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c if (buf == NULL || buf->b_ml.ml_mfp == NULL) { return FAIL; } - n = buf_charidx_to_byteidx(buf, posp->lnum, n); + n = buf_charidx_to_byteidx(buf, posp->lnum, n) + 1; } posp->col = n; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8beacc9988..7772d9ffc2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1588,7 +1588,7 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) line = tv_get_lnum(argvars); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) { - col = buf_charidx_to_byteidx(curbuf, line, col); + col = buf_charidx_to_byteidx(curbuf, line, col) + 1; } if (argvars[2].v_type != VAR_UNKNOWN) { coladd = (long)tv_get_number_chk(&argvars[2], NULL); @@ -3327,7 +3327,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool } if (fp != NULL && charcol) { pos = *fp; - pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); fp = &pos; } } else { diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 47e74a24d6..57825b4551 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -123,11 +123,18 @@ func Test_screenpos_number() bwipe! endfunc +" Save the visual start character position func SaveVisualStartCharPos() call add(g:VisualStartPos, getcharpos('v')) return '' endfunc +" Save the current cursor character position in insert mode +func SaveInsertCurrentCharPos() + call add(g:InsertCurrentPos, getcharpos('.')) + return '' +endfunc + " Test for the getcharpos() function func Test_getcharpos() call assert_fails('call getcharpos({})', 'E731:') @@ -156,16 +163,29 @@ func Test_getcharpos() vnoremap SaveVisualStartCharPos() let g:VisualStartPos = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos) + call assert_equal([[0, 2, 7, 0], [0, 2, 10, 0], [0, 2, 5, 0]], g:VisualStartPos) call assert_equal([0, 2, 9, 0], getcharpos('v')) let g:VisualStartPos = [] exe "normal 3Gv$\o\" - call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos) + call assert_equal([[0, 3, 1, 0], [0, 3, 2, 0]], g:VisualStartPos) let g:VisualStartPos = [] exe "normal 1Gv$\o\" call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos) vunmap + " Test for getting the position in insert mode with the cursor after the + " last character in a line + inoremap SaveInsertCurrentCharPos() + let g:InsertCurrentPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0], [0, 2, 10, 0], [0, 3, 2, 0], [0, 4, 10, 0], + \ [0, 2, 7, 0]], g:InsertCurrentPos) + iunmap + %bw! endfunc @@ -192,6 +212,10 @@ func Test_setcharpos() call setcharpos("'m", [0, 2, 9, 0]) normal `m call assert_equal([2, 11], [line('.'), col('.')]) + " unload the buffer and try to set the mark + let bnr = bufnr() + enew! + call assert_equal(-1, setcharpos("'m", [bnr, 2, 2, 0])) %bw! call assert_equal(-1, setcharpos('.', [10, 3, 1, 0])) @@ -202,6 +226,11 @@ func SaveVisualStartCharCol() return '' endfunc +func SaveInsertCurrentCharCol() + call add(g:InsertCurrentCol, charcol('.')) + return '' +endfunc + " Test for the charcol() function func Test_charcol() call assert_fails('call charcol({})', 'E731:') @@ -239,19 +268,36 @@ func Test_charcol() vnoremap SaveVisualStartCharCol() let g:VisualStartCol = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([7, 9, 5], g:VisualStartCol) + call assert_equal([7, 10, 5], g:VisualStartCol) call assert_equal(9, charcol('v')) let g:VisualStartCol = [] exe "normal 3Gv$\o\" - call assert_equal([1, 1], g:VisualStartCol) + call assert_equal([1, 2], g:VisualStartCol) let g:VisualStartCol = [] exe "normal 1Gv$\o\" call assert_equal([1, 1], g:VisualStartCol) vunmap + " Test for getting the column number in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCurrentCharCol() + let g:InsertCurrentCol = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) + iunmap + %bw! endfunc +func SaveInsertCursorCharPos() + call add(g:InsertCursorPos, getcursorcharpos('.')) + return '' +endfunc + " Test for getcursorcharpos() func Test_getcursorcharpos() call assert_equal(getcursorcharpos(), getcursorcharpos(0)) @@ -269,6 +315,19 @@ func Test_getcursorcharpos() normal 4G9l call assert_equal([0, 4, 9, 0, 9], getcursorcharpos()) + " Test for getting the cursor position in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCursorCharPos() + let g:InsertCursorPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0, 1], [0, 2, 10, 0, 15], [0, 3, 2, 0, 2], + \ [0, 4, 10, 0, 10], [0, 2, 7, 0, 12]], g:InsertCursorPos) + iunmap + let winid = win_getid() normal 2G5l wincmd w -- cgit From 50250542c346473dd3a91ce63cd989033dae4471 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 12 Feb 2022 17:12:09 +0000 Subject: refactor(signs): more efficient signcol calc When iterating signs to calculate the sign column, stop iterating when we reach the maximum configured from 'signcolumn'. --- src/nvim/buffer.c | 49 +++++++++++++++++++++++++++++++------------------ src/nvim/option.c | 7 ++++--- src/nvim/screen.c | 2 +- 3 files changed, 36 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ee704bd1bd..38b045b31c 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5455,30 +5455,43 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) return false; } -int buf_signcols(buf_T *buf) +static int buf_signcols_inner(buf_T *buf, int maximum) { - if (!buf->b_signcols_valid) { - sign_entry_T *sign; // a sign in the sign list - int signcols = 0; - int linesum = 0; - linenr_T curline = 0; - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->se_lnum > curline) { - if (linesum > signcols) { - signcols = linesum; + sign_entry_T *sign; // a sign in the sign list + int signcols = 0; + int linesum = 0; + linenr_T curline = 0; + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->se_lnum > curline) { + if (linesum > signcols) { + signcols = linesum; + if (signcols >= maximum) { + return maximum; } - curline = sign->se_lnum; - linesum = 0; - } - if (sign->se_has_text_or_icon) { - linesum++; } + curline = sign->se_lnum; + linesum = 0; + } + if (sign->se_has_text_or_icon) { + linesum++; } - if (linesum > signcols) { - signcols = linesum; + } + + if (linesum > signcols) { + signcols = linesum; + if (signcols >= maximum) { + return maximum; } + } + + return signcols; +} +int buf_signcols(buf_T *buf, int maximum) +{ + if (!buf->b_signcols_valid) { + int signcols = buf_signcols_inner(buf, maximum); // Check if we need to redraw if (signcols != buf->b_signcols) { buf->b_signcols = signcols; diff --git a/src/nvim/option.c b/src/nvim/option.c index dddf926b9a..df5f352258 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -8046,7 +8046,6 @@ int win_signcol_count(win_T *wp) /// Return the number of requested sign columns, based on user / configuration. int win_signcol_configured(win_T *wp, int *is_fixed) { - int minimum = 0, maximum = 1, needed_signcols; const char *scl = (const char *)wp->w_p_scl; if (is_fixed) { @@ -8059,7 +8058,6 @@ int win_signcol_configured(win_T *wp, int *is_fixed) && (wp->w_p_nu || wp->w_p_rnu)))) { return 0; } - needed_signcols = buf_signcols(wp->w_buffer); // yes or yes if (!strncmp(scl, "yes:", 4)) { @@ -8075,6 +8073,8 @@ int win_signcol_configured(win_T *wp, int *is_fixed) *is_fixed = 0; } + int minimum = 0, maximum = 1; + if (!strncmp(scl, "auto:", 5)) { // Variable depending on a configuration maximum = scl[5] - '0'; @@ -8085,7 +8085,8 @@ int win_signcol_configured(win_T *wp, int *is_fixed) } } - int ret = MAX(minimum, MIN(maximum, needed_signcols)); + int needed_signcols = buf_signcols(wp->w_buffer, maximum); + int ret = MAX(minimum, needed_signcols); assert(ret <= SIGN_SHOW_MAX); return ret; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0644a08210..6dcabffb7d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -794,7 +794,7 @@ static void win_update(win_T *wp, Providers *providers) // If we can compute a change in the automatic sizing of the sign column // under 'signcolumn=auto:X' and signs currently placed in the buffer, better // figuring it out here so we can redraw the entire screen for it. - buf_signcols(buf); + win_signcol_count(wp); type = wp->w_redr_type; -- cgit From 1b0d6bcd53291d9a9d3e1252976bd22564db4fe5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 12 Feb 2022 18:23:38 +0000 Subject: vim-patch:8.2.4359: crash when repeatedly using :retab Problem: crash when repeatedly using :retab. Solution: Bail out when the line is getting too long. https://github.com/vim/vim/commit/6e28703a8e41f775f64e442c5d11ce1ff599aa3f Cherry-pick e_resulting_text_too_long from v8.2.3492; put it in globals.h as it will eventually be used in other files. Add a modeline to test_retab.vim --- src/nvim/ex_cmds.c | 4 ++++ src/nvim/globals.h | 2 ++ src/nvim/testdir/test_retab.vim | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index fd1e34803f..f6ef259dbd 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -847,6 +847,10 @@ void ex_retab(exarg_T *eap) 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 diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f6fbe98ff0..98a38c5fe2 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1000,6 +1000,8 @@ EXTERN char e_non_empty_string_required[] INIT(= N_("E1142: Non-empty string req EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cannot define autocommands for ALL events")); +EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long")); + EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim index e7b8946ccf..1650a03876 100644 --- a/src/nvim/testdir/test_retab.vim +++ b/src/nvim/testdir/test_retab.vim @@ -69,6 +69,8 @@ func Test_retab() call assert_equal(" a b c ", Retab('!', 3)) call assert_equal(" a b c ", Retab('', 5)) call assert_equal(" a b c ", Retab('!', 5)) + + set tabstop& expandtab& endfunc func Test_retab_error() @@ -78,3 +80,22 @@ func Test_retab_error() call assert_fails('ret 10000', 'E475:') call assert_fails('ret 80000000000000000000', 'E475:') endfunc + +func Test_retab_endless() + new + call setline(1, "\t0\t") + let caught = 'no' + try + while 1 + set ts=4000 + retab 4 + endwhile + catch /E1240/ + let caught = 'yes' + endtry + bwipe! + set tabstop& +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From f292dd2126f8dacd6446799ac750ab368b852f81 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:54:25 +0600 Subject: fix: autoload variables not loaded with vim.g & nvim_get_var --- src/nvim/api/vim.c | 14 +++++++++++++- src/nvim/lua/stdlib.c | 7 +++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f7c55344f5..f4909b0801 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -601,7 +601,19 @@ void nvim_del_current_line(Error *err) Object nvim_get_var(String name, Error *err) FUNC_API_SINCE(1) { - return dict_get_value(&globvardict, name, err); + dictitem_T *di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); + if (di == NULL) { // try to autoload script + if (!script_autoload(name.data, name.size, false) || aborting()) { + api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data); + return (Object)OBJECT_INIT; + } + di = tv_dict_find(&globvardict, name.data, (ptrdiff_t)name.size); + } + if (di == NULL) { + api_set_error(err, kErrorTypeValidation, "Key not found: %s", name.data); + return (Object)OBJECT_INIT; + } + return vim_to_object(&di->di_tv); } /// Sets a global (g:) variable. diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 18a579ed0f..55b23cf0c8 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -25,6 +25,7 @@ #include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" +#include "nvim/globals.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" #include "nvim/lua/stdlib.h" @@ -408,6 +409,12 @@ int nlua_getvar(lua_State *lstate) const char *name = luaL_checklstring(lstate, 3, &len); dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len); + if (di == NULL && dict == &globvardict) { // try to autoload script + if (!script_autoload(name, len, false) || aborting()) { + return 0; // nil + } + di = tv_dict_find(dict, name, (ptrdiff_t)len); + } if (di == NULL) { return 0; // nil } -- cgit From 700af0ab1d8e85f1f191f4b97f3dda456b5a9551 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 12 Feb 2022 21:20:29 +0000 Subject: vim-patch:8.2.4362: :retab may allocate too much memory Problem: :retab may allocate too much memory. Solution: Bail out when allocating more than MAXCOL bytes. https://github.com/vim/vim/commit/33f3c5985491032d5bdfc30e722e85d5a0285e64 --- src/nvim/ex_cmds.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f6ef259dbd..306da0ec68 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -814,7 +814,11 @@ void ex_retab(exarg_T *eap) // len is actual number of white characters used len = num_spaces + num_tabs; old_len = (long)STRLEN(ptr); - long new_len = old_len - col + start_col + len + 1; + const long new_len = old_len - col + start_col + len + 1; + if (new_len >= MAXCOL) { + emsg(_(e_resulting_text_too_long)); + break; + } new_line = xmalloc(new_len); if (start_col > 0) { -- cgit From 72e3d2c9baabfbcf3f6505097754472c0e88b317 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 12 Feb 2022 21:33:09 +0000 Subject: vim-patch:8.2.4363: MS-Windows: running out of memory for a very long line Problem: MS-Windows: running out of memory for a very long line. Solution: Use a 32 bit value for MAXCOL also when ints are 64 bits. https://github.com/vim/vim/commit/8e38555ece7d3fe1edc6681ec70fe5586a524862 This still fails Vim's Windows CI, so let's see what happens... --- src/nvim/pos.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/pos.h b/src/nvim/pos.h index d17e27906e..51991ed314 100644 --- a/src/nvim/pos.h +++ b/src/nvim/pos.h @@ -16,7 +16,9 @@ typedef int colnr_T; /// Maximal (invalid) line number enum { MAXLNUM = 0x7fffffff, }; /// Maximal column number -enum { MAXCOL = INT_MAX, }; +/// MAXCOL used to be INT_MAX, but with 64 bit ints that results in running +/// out of memory when trying to allocate a very long line. +enum { MAXCOL = 0x7fffffff, }; // Minimum line number enum { MINLNUM = 1, }; // minimum column number -- cgit From dc415ce2986c2d09ae9c4c4d01c142f5e780cd47 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 05:40:13 +0800 Subject: vim-patch:8.1.2184: option context is not copied when splitting a window Problem: Option context is not copied when splitting a window. (Daniel Hahler) Solution: Copy the option context, so that ":verbose set" works. (closes vim/vim#5066) https://github.com/vim/vim/commit/cfb381421f8be7d6cb4e7dac5b827b23467d3e53 vim-patch:8.2.3804: script context not set when copying 'swf' and 'ts' Problem: Script context not set when copying 'swf' and 'ts'. Solution: Use COPY_OPT_SCTX with the right argument. (closes vim/vim#9347) https://github.com/vim/vim/commit/6206877c511c636cbeb2a2b911451af316b62d00 --- src/nvim/option.c | 93 ++++++++++++++++++++++++++++++++++++--- src/nvim/testdir/test_options.vim | 37 ++++++++++++++-- 2 files changed, 119 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index df5f352258..c8e50d4494 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -6232,6 +6232,9 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_fcs = vim_strsave(from->wo_fcs); to->wo_lcs = vim_strsave(from->wo_lcs); to->wo_winbl = from->wo_winbl; + + // Copy the script context so that we know were the value was last set. + memmove(to->wo_script_ctx, from->wo_script_ctx, sizeof(to->wo_script_ctx)); check_winopt(to); // don't want NULL pointers } @@ -6304,11 +6307,30 @@ void didset_window_options(win_T *wp) wp->w_grid_alloc.blending = wp->w_p_winbl > 0; } +/// Index into the options table for a buffer-local option enum. +static int buf_opt_idx[BV_COUNT]; +#define COPY_OPT_SCTX(buf, bv) buf->b_p_script_ctx[bv] = options[buf_opt_idx[bv]].last_set + +/// Initialize buf_opt_idx[] if not done already. +static void init_buf_opt_idx(void) +{ + static int did_init_buf_opt_idx = false; + + if (did_init_buf_opt_idx) { + return; + } + did_init_buf_opt_idx = true; + for (int i = 0; options[i].fullname != NULL; i++) { + if (options[i].indir & PV_BUF) { + buf_opt_idx[options[i].indir & PV_MASK] = i; + } + } +} /// Copy global option values to local options for one buffer. /// Used when creating a new buffer and sometimes when entering a buffer. /// flags: -/// BCO_ENTER We will enter the buf buffer. +/// BCO_ENTER We will enter the buffer "buf". /// BCO_ALWAYS Always copy the options, but only set b_p_initialized when /// appropriate. /// BCO_NOHELP Don't copy the values to a help buffer. @@ -6344,11 +6366,12 @@ void buf_copy_options(buf_T *buf, int flags) } if (should_copy || (flags & BCO_ALWAYS)) { - /* Don't copy the options specific to a help buffer when - * BCO_NOHELP is given or the options were initialized already - * (jumping back to a help file with CTRL-T or CTRL-O) */ - dont_do_help = ((flags & BCO_NOHELP) && buf->b_help) - || buf->b_p_initialized; + memset(buf->b_p_script_ctx, 0, sizeof(buf->b_p_script_ctx)); + init_buf_opt_idx(); + // Don't copy the options specific to a help buffer when + // BCO_NOHELP is given or the options were initialized already + // (jumping back to a help file with CTRL-T or CTRL-O) + dont_do_help = ((flags & BCO_NOHELP) && buf->b_help) || buf->b_p_initialized; if (dont_do_help) { // don't free b_p_isk save_p_isk = buf->b_p_isk; buf->b_p_isk = NULL; @@ -6380,35 +6403,58 @@ void buf_copy_options(buf_T *buf, int flags) } buf->b_p_ai = p_ai; + COPY_OPT_SCTX(buf, BV_AI); buf->b_p_ai_nopaste = p_ai_nopaste; buf->b_p_sw = p_sw; + COPY_OPT_SCTX(buf, BV_SW); buf->b_p_scbk = p_scbk; + COPY_OPT_SCTX(buf, BV_SCBK); buf->b_p_tw = p_tw; + COPY_OPT_SCTX(buf, BV_TW); buf->b_p_tw_nopaste = p_tw_nopaste; buf->b_p_tw_nobin = p_tw_nobin; buf->b_p_wm = p_wm; + COPY_OPT_SCTX(buf, BV_WM); buf->b_p_wm_nopaste = p_wm_nopaste; buf->b_p_wm_nobin = p_wm_nobin; buf->b_p_bin = p_bin; + COPY_OPT_SCTX(buf, BV_BIN); buf->b_p_bomb = p_bomb; + COPY_OPT_SCTX(buf, BV_BOMB); buf->b_p_et = p_et; + COPY_OPT_SCTX(buf, BV_ET); buf->b_p_fixeol = p_fixeol; + COPY_OPT_SCTX(buf, BV_FIXEOL); buf->b_p_et_nobin = p_et_nobin; buf->b_p_et_nopaste = p_et_nopaste; buf->b_p_ml = p_ml; + COPY_OPT_SCTX(buf, BV_ML); buf->b_p_ml_nobin = p_ml_nobin; buf->b_p_inf = p_inf; - buf->b_p_swf = cmdmod.noswapfile ? false : p_swf; + COPY_OPT_SCTX(buf, BV_INF); + if (cmdmod.noswapfile) { + buf->b_p_swf = false; + } else { + buf->b_p_swf = p_swf; + COPY_OPT_SCTX(buf, BV_SWF); + } buf->b_p_cpt = vim_strsave(p_cpt); + COPY_OPT_SCTX(buf, BV_CPT); #ifdef BACKSLASH_IN_FILENAME buf->b_p_csl = vim_strsave(p_csl); + COPY_OPT_SCTX(buf, BV_CSL); #endif buf->b_p_cfu = vim_strsave(p_cfu); + COPY_OPT_SCTX(buf, BV_CFU); buf->b_p_ofu = vim_strsave(p_ofu); + COPY_OPT_SCTX(buf, BV_OFU); buf->b_p_tfu = vim_strsave(p_tfu); + COPY_OPT_SCTX(buf, BV_TFU); buf->b_p_sts = p_sts; + COPY_OPT_SCTX(buf, BV_STS); buf->b_p_sts_nopaste = p_sts_nopaste; buf->b_p_vsts = vim_strsave(p_vsts); + COPY_OPT_SCTX(buf, BV_VSTS); if (p_vsts && p_vsts != empty_option) { (void)tabstop_set(p_vsts, &buf->b_p_vsts_array); } else { @@ -6418,42 +6464,68 @@ void buf_copy_options(buf_T *buf, int flags) ? vim_strsave(p_vsts_nopaste) : NULL; buf->b_p_com = vim_strsave(p_com); + COPY_OPT_SCTX(buf, BV_COM); buf->b_p_cms = vim_strsave(p_cms); + COPY_OPT_SCTX(buf, BV_CMS); buf->b_p_fo = vim_strsave(p_fo); + COPY_OPT_SCTX(buf, BV_FO); buf->b_p_flp = vim_strsave(p_flp); + COPY_OPT_SCTX(buf, BV_FLP); buf->b_p_nf = vim_strsave(p_nf); + COPY_OPT_SCTX(buf, BV_NF); buf->b_p_mps = vim_strsave(p_mps); + COPY_OPT_SCTX(buf, BV_MPS); buf->b_p_si = p_si; + COPY_OPT_SCTX(buf, BV_SI); buf->b_p_channel = 0; buf->b_p_ci = p_ci; + COPY_OPT_SCTX(buf, BV_CI); buf->b_p_cin = p_cin; + COPY_OPT_SCTX(buf, BV_CIN); buf->b_p_cink = vim_strsave(p_cink); + COPY_OPT_SCTX(buf, BV_CINK); buf->b_p_cino = vim_strsave(p_cino); + COPY_OPT_SCTX(buf, BV_CINO); // Don't copy 'filetype', it must be detected buf->b_p_ft = empty_option; buf->b_p_pi = p_pi; + COPY_OPT_SCTX(buf, BV_PI); buf->b_p_cinw = vim_strsave(p_cinw); + COPY_OPT_SCTX(buf, BV_CINW); buf->b_p_lisp = p_lisp; + COPY_OPT_SCTX(buf, BV_LISP); // Don't copy 'syntax', it must be set buf->b_p_syn = empty_option; buf->b_p_smc = p_smc; + COPY_OPT_SCTX(buf, BV_SMC); buf->b_s.b_syn_isk = empty_option; buf->b_s.b_p_spc = vim_strsave(p_spc); + COPY_OPT_SCTX(buf, BV_SPC); (void)compile_cap_prog(&buf->b_s); buf->b_s.b_p_spf = vim_strsave(p_spf); + COPY_OPT_SCTX(buf, BV_SPF); buf->b_s.b_p_spl = vim_strsave(p_spl); + COPY_OPT_SCTX(buf, BV_SPL); buf->b_s.b_p_spo = vim_strsave(p_spo); + COPY_OPT_SCTX(buf, BV_SPO); buf->b_p_inde = vim_strsave(p_inde); + COPY_OPT_SCTX(buf, BV_INDE); buf->b_p_indk = vim_strsave(p_indk); + COPY_OPT_SCTX(buf, BV_INDK); buf->b_p_fp = empty_option; buf->b_p_fex = vim_strsave(p_fex); + COPY_OPT_SCTX(buf, BV_FEX); buf->b_p_sua = vim_strsave(p_sua); + COPY_OPT_SCTX(buf, BV_SUA); buf->b_p_keymap = vim_strsave(p_keymap); + COPY_OPT_SCTX(buf, BV_KMAP); buf->b_kmap_state |= KEYMAP_INIT; // This isn't really an option, but copying the langmap and IME // state from the current buffer is better than resetting it. buf->b_p_iminsert = p_iminsert; + COPY_OPT_SCTX(buf, BV_IMI); buf->b_p_imsearch = p_imsearch; + COPY_OPT_SCTX(buf, BV_IMS); // options that are normally global but also have a local value // are not copied, start using the global value @@ -6473,11 +6545,14 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_def = empty_option; buf->b_p_inc = empty_option; buf->b_p_inex = vim_strsave(p_inex); + COPY_OPT_SCTX(buf, BV_INEX); buf->b_p_dict = empty_option; buf->b_p_tsr = empty_option; buf->b_p_tsrfu = empty_option; buf->b_p_qe = vim_strsave(p_qe); + COPY_OPT_SCTX(buf, BV_QE); buf->b_p_udf = p_udf; + COPY_OPT_SCTX(buf, BV_UDF); buf->b_p_lw = empty_option; buf->b_p_menc = empty_option; @@ -6496,9 +6571,12 @@ void buf_copy_options(buf_T *buf, int flags) } } else { buf->b_p_isk = vim_strsave(p_isk); + COPY_OPT_SCTX(buf, BV_ISK); did_isk = true; buf->b_p_ts = p_ts; + COPY_OPT_SCTX(buf, BV_TS); buf->b_p_vts = vim_strsave(p_vts); + COPY_OPT_SCTX(buf, BV_VTS); if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) { (void)tabstop_set(p_vts, &buf->b_p_vts_array); } else { @@ -6509,6 +6587,7 @@ void buf_copy_options(buf_T *buf, int flags) clear_string_option(&buf->b_p_bt); } buf->b_p_ma = p_ma; + COPY_OPT_SCTX(buf, BV_MA); } } diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 7d1fed3b94..8612b7013b 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -314,21 +314,50 @@ func Test_set_errors() set modifiable& endfunc +func CheckWasSet(name) + let verb_cm = execute('verbose set ' .. a:name .. '?') + call assert_match('Last set from.*test_options.vim', verb_cm) +endfunc +func CheckWasNotSet(name) + let verb_cm = execute('verbose set ' .. a:name .. '?') + call assert_notmatch('Last set from', verb_cm) +endfunc + " Must be executed before other tests that set 'term'. func Test_000_term_option_verbose() if has('nvim') || has('gui_running') return endif - let verb_cm = execute('verbose set t_cm') - call assert_notmatch('Last set from', verb_cm) + + call CheckWasNotSet('t_cm') let term_save = &term set term=ansi - let verb_cm = execute('verbose set t_cm') - call assert_match('Last set from.*test_options.vim', verb_cm) + call CheckWasSet('t_cm') let &term = term_save endfunc +func Test_copy_context() + setlocal list + call CheckWasSet('list') + split + call CheckWasSet('list') + quit + setlocal nolist + + set ai + call CheckWasSet('ai') + set filetype=perl + call CheckWasSet('filetype') + set fo=tcroq + call CheckWasSet('fo') + + split Xsomebuf + call CheckWasSet('ai') + call CheckWasNotSet('filetype') + call CheckWasSet('fo') +endfunc + func Test_set_ttytype() " Nvim does not support 'ttytype'. if !has('nvim') && !has('gui_running') && has('unix') -- cgit From 5c62bce7c12638c217f8297212698ed10bb5543b Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 12 Feb 2022 22:24:08 +0000 Subject: vim-patch:8.2.4364: MS-Windows: still running out of memory for a very long line Problem: MS-Windows: still running out of memory for a very long line. Solution: Check for negative length. https://github.com/vim/vim/commit/45491660787043ea412719544881db691338d730 --- src/nvim/ex_cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 306da0ec68..49bf9972b1 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -815,7 +815,7 @@ void ex_retab(exarg_T *eap) len = num_spaces + num_tabs; old_len = (long)STRLEN(ptr); const long new_len = old_len - col + start_col + len + 1; - if (new_len >= MAXCOL) { + if (new_len <= 0 || new_len >= MAXCOL) { emsg(_(e_resulting_text_too_long)); break; } -- cgit From 2a6a93b66544ec9ab68c8ed05b217017b6db86d0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 08:36:15 +0800 Subject: test(old): move some tests from assert_spec.lua to test_assert.vim --- src/nvim/testdir/test_assert.vim | 174 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 52f243aaea..28c5948142 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -1,5 +1,55 @@ " Test that the methods used for testing work. +func Test_assert_false() + call assert_equal(0, assert_false(0)) + call assert_equal(0, assert_false(v:false)) + call assert_equal(0, v:false->assert_false()) + + call assert_equal(1, assert_false(123)) + call assert_match("Expected False but got 123", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, 123->assert_false()) + call assert_match("Expected False but got 123", v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_assert_true() + call assert_equal(0, assert_true(1)) + call assert_equal(0, assert_true(123)) + call assert_equal(0, assert_true(v:true)) + call assert_equal(0, v:true->assert_true()) + + call assert_equal(1, assert_true(0)) + call assert_match("Expected True but got 0", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, 0->assert_true()) + call assert_match("Expected True but got 0", v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_assert_equal() + let s = 'foo' + call assert_equal(0, assert_equal('foo', s)) + let n = 4 + call assert_equal(0, assert_equal(4, n)) + let l = [1, 2, 3] + call assert_equal(0, assert_equal([1, 2, 3], l)) + call assert_equal(v:_null_list, v:_null_list) + call assert_equal(v:_null_list, []) + call assert_equal([], v:_null_list) + + let s = 'foo' + call assert_equal(1, assert_equal('bar', s)) + call assert_match("Expected 'bar' but got 'foo'", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal('XxxxxxxxxxxxxxxxxxxxxxX', 'XyyyyyyyyyyyyyyyyyyyyyyyyyX') + call assert_match("Expected 'X\\\\\\[x occurs 21 times]X' but got 'X\\\\\\[y occurs 25 times]X'", v:errors[0]) + call remove(v:errors, 0) +endfunc + func Test_assert_equalfile() call assert_equal(1, assert_equalfile('abcabc', 'xyzxyz')) call assert_match("E485: Can't read file abcabc", v:errors[0]) @@ -46,17 +96,129 @@ func Test_assert_equalfile() call delete('Xtwo') endfunc +func Test_assert_notequal() + let n = 4 + call assert_equal(0, assert_notequal('foo', n)) + let s = 'foo' + call assert_equal(0, assert_notequal([1, 2, 3], s)) + + call assert_equal(1, assert_notequal('foo', s)) + call assert_match("Expected not equal to 'foo'", v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_assert_report() + call assert_equal(1, assert_report('something is wrong')) + call assert_match('something is wrong', v:errors[0]) + call remove(v:errors, 0) + call assert_equal(1, 'also wrong'->assert_report()) + call assert_match('also wrong', v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_assert_exception() + try + nocommand + catch + call assert_equal(0, assert_exception('E492:')) + endtry + + try + nocommand + catch + try + " illegal argument, get NULL for error + call assert_equal(1, assert_exception([])) + catch + call assert_equal(0, assert_exception('E730:')) + endtry + endtry +endfunc + +func Test_wrong_error_type() + let save_verrors = v:errors + let v:['errors'] = {'foo': 3} + call assert_equal('yes', 'no') + let verrors = v:errors + let v:errors = save_verrors + call assert_equal(type([]), type(verrors)) +endfunc + +func Test_match() + call assert_equal(0, assert_match('^f.*b.*r$', 'foobar')) + + call assert_equal(1, assert_match('bar.*foo', 'foobar')) + call assert_match("Pattern 'bar.*foo' does not match 'foobar'", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, assert_match('bar.*foo', 'foobar', 'wrong')) + call assert_match('wrong', v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, 'foobar'->assert_match('bar.*foo', 'wrong')) + call assert_match('wrong', v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_notmatch() + call assert_equal(0, assert_notmatch('foo', 'bar')) + call assert_equal(0, assert_notmatch('^foobar$', 'foobars')) + + call assert_equal(1, assert_notmatch('foo', 'foobar')) + call assert_match("Pattern 'foo' does match 'foobar'", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, 'foobar'->assert_notmatch('foo')) + call assert_match("Pattern 'foo' does match 'foobar'", v:errors[0]) + call remove(v:errors, 0) +endfunc + +func Test_assert_fail_fails() + call assert_equal(1, assert_fails('xxx', 'E12345')) + call assert_match("Expected 'E12345' but got 'E492:", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, assert_fails('xxx', 'E9876', 'stupid')) + call assert_match("stupid: Expected 'E9876' but got 'E492:", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, assert_fails('echo', '', 'echo command')) + call assert_match("command did not fail: echo command", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(1, 'echo'->assert_fails('', 'echo command')) + call assert_match("command did not fail: echo command", v:errors[0]) + call remove(v:errors, 0) +endfunc + func Test_assert_fails_in_try_block() try call assert_equal(0, assert_fails('throw "error"')) endtry endfunc +func Test_assert_beeps() + new + call assert_equal(0, assert_beeps('normal h')) + + call assert_equal(1, assert_beeps('normal 0')) + call assert_match("command did not beep: normal 0", v:errors[0]) + call remove(v:errors, 0) + + call assert_equal(0, 'normal h'->assert_beeps()) + call assert_equal(1, 'normal 0'->assert_beeps()) + call assert_match("command did not beep: normal 0", v:errors[0]) + call remove(v:errors, 0) + + bwipe +endfunc + func Test_assert_inrange() call assert_equal(0, assert_inrange(7, 7, 7)) call assert_equal(0, assert_inrange(5, 7, 5)) call assert_equal(0, assert_inrange(5, 7, 6)) call assert_equal(0, assert_inrange(5, 7, 7)) + call assert_equal(1, assert_inrange(5, 7, 4)) call assert_match("Expected range 5 - 7, but got 4", v:errors[0]) call remove(v:errors, 0) @@ -64,6 +226,12 @@ func Test_assert_inrange() call assert_match("Expected range 5 - 7, but got 8", v:errors[0]) call remove(v:errors, 0) + call assert_equal(0, 5->assert_inrange(5, 7)) + call assert_equal(0, 7->assert_inrange(5, 7)) + call assert_equal(1, 8->assert_inrange(5, 7)) + call assert_match("Expected range 5 - 7, but got 8", v:errors[0]) + call remove(v:errors, 0) + call assert_fails('call assert_inrange(1, 1)', 'E119:') if has('float') @@ -83,6 +251,12 @@ func Test_assert_inrange() endif endfunc +func Test_assert_with_msg() + call assert_equal('foo', 'bar', 'testing') + call assert_match("testing: Expected 'foo' but got 'bar'", v:errors[0]) + call remove(v:errors, 0) +endfunc + " Must be last. func Test_zz_quit_detected() " Verify that if a test function ends Vim the test script detects this. -- cgit From b16fae0f26b378ccc3d2c88355bb639fda8eaa96 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 10:26:11 +0800 Subject: test(old): add test_cdo.vim and test_packadd.vim test_cdo.vim is copied from Vim v8.1.1483. test_packadd.vim is copied from Vim v8.2.0174. --- src/nvim/testdir/test_cdo.vim | 205 ++++++++++++++++++++++ src/nvim/testdir/test_packadd.vim | 361 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 566 insertions(+) create mode 100644 src/nvim/testdir/test_cdo.vim create mode 100644 src/nvim/testdir/test_packadd.vim (limited to 'src') diff --git a/src/nvim/testdir/test_cdo.vim b/src/nvim/testdir/test_cdo.vim new file mode 100644 index 0000000000..aa2e4f1a8c --- /dev/null +++ b/src/nvim/testdir/test_cdo.vim @@ -0,0 +1,205 @@ +" Tests for the :cdo, :cfdo, :ldo and :lfdo commands + +if !has('quickfix') + throw 'Skipped: quickfix feature missing' +endif + +" Create the files used by the tests +function SetUp() + call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1') + call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2') + call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3') +endfunction + +" Remove the files used by the tests +function TearDown() + call delete('Xtestfile1') + call delete('Xtestfile2') + call delete('Xtestfile3') +endfunction + +" Returns the current line in ' L C' format +function GetRuler() + return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C' +endfunction + +" Tests for the :cdo and :ldo commands +function XdoTests(cchar) + enew + + " Shortcuts for calling the cdo and ldo commands + let Xdo = a:cchar . 'do' + let Xgetexpr = a:cchar . 'getexpr' + let Xprev = a:cchar. 'prev' + let XdoCmd = Xdo . ' call add(l, GetRuler())' + + " Try with an empty list + let l = [] + exe XdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Run command only on selected error lines + let l = [] + enew + exe "2,3" . XdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + " Boundary condition tests + let l = [] + enew + exe "1,1" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C'], l) + + let l = [] + enew + exe "3" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Range test commands + let l = [] + enew + exe "%" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe "1,$" . XdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 3L 1C'], l) + + let l = [] + enew + exe Xprev + exe "." . XdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + let l = [] + enew + exe "+" . XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + + " Invalid error lines test + let l = [] + enew + exe "silent! 27" . XdoCmd + exe "silent! 4,5" . XdoCmd + call assert_equal([], l) + + " Run commands from an unsaved buffer + let v:errmsg='' + let l = [] + enew + setlocal modified + exe "silent! 2,2" . XdoCmd + if v:errmsg !~# 'No write since last change' + call add(v:errors, 'Unsaved file change test failed') + endif + + " If the executed command fails, then the operation should be aborted + enew! + let subst_count = 0 + exe "silent!" . Xdo . " s/Line/xLine/ | let subst_count += 1" + if subst_count != 1 || getline('.') != 'xLine1' + call add(v:errors, 'Abort command on error test failed') + endif + + let l = [] + exe "2,2" . Xdo . "! call add(l, GetRuler())" + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with no valid error entries + let l = [] + edit! +2 Xtestfile1 + exe Xgetexpr . " ['non-error 1', 'non-error 2', 'non-error 3']" + exe XdoCmd + call assert_equal([], l) + exe "silent! 2" . XdoCmd + call assert_equal([], l) + let v:errmsg='' + exe "%" . XdoCmd + exe "1,$" . XdoCmd + exe "." . XdoCmd + call assert_equal('', v:errmsg) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile3:3:1:Line3']" + exe XdoCmd + call assert_equal(['Xtestfile3 3L 1C'], l) + +endfunction + +" Tests for the :cfdo and :lfdo commands +function XfdoTests(cchar) + enew + + " Shortcuts for calling the cfdo and lfdo commands + let Xfdo = a:cchar . 'fdo' + let Xgetexpr = a:cchar . 'getexpr' + let XfdoCmd = Xfdo . ' call add(l, GetRuler())' + let Xpfile = a:cchar. 'pfile' + + " Clear the quickfix/location list + exe Xgetexpr . " []" + + " Try with an empty list + let l = [] + exe XfdoCmd + call assert_equal([], l) + + " Populate the list and then try + exe Xgetexpr . " ['non-error 1', 'Xtestfile1:1:3:Line1', 'Xtestfile1:2:1:Line2', 'non-error 2', 'Xtestfile2:2:2:Line2', 'non-error 3', 'Xtestfile3:2:3:Line2', 'Xtestfile3:3:1:Line3']" + + let l = [] + exe XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Run command only on selected error lines + let l = [] + exe "2,3" . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + " Boundary condition tests + let l = [] + exe "3" . XfdoCmd + call assert_equal(['Xtestfile3 2L 3C'], l) + + " Range test commands + let l = [] + exe "%" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe "1,$" . XfdoCmd + call assert_equal(['Xtestfile1 1L 3C', 'Xtestfile2 2L 2C', 'Xtestfile3 2L 3C'], l) + + let l = [] + exe Xpfile + exe "." . XfdoCmd + call assert_equal(['Xtestfile2 2L 2C'], l) + + " List with only one valid entry + let l = [] + exe Xgetexpr . " ['Xtestfile2:2:5:Line2']" + exe XfdoCmd + call assert_equal(['Xtestfile2 2L 5C'], l) + +endfunction + +" Tests for cdo and cfdo +function Test_cdo() + call XdoTests('c') + call XfdoTests('c') +endfunction + +" Tests for ldo and lfdo +function Test_ldo() + call XdoTests('l') + call XfdoTests('l') +endfunction diff --git a/src/nvim/testdir/test_packadd.vim b/src/nvim/testdir/test_packadd.vim new file mode 100644 index 0000000000..fcb8b8033b --- /dev/null +++ b/src/nvim/testdir/test_packadd.vim @@ -0,0 +1,361 @@ +" Tests for 'packpath' and :packadd + + +func SetUp() + let s:topdir = getcwd() . '/Xdir' + exe 'set packpath=' . s:topdir + let s:plugdir = s:topdir . '/pack/mine/opt/mytest' +endfunc + +func TearDown() + call delete(s:topdir, 'rf') +endfunc + +func Test_packadd() + if !exists('s:plugdir') + echomsg 'when running this test manually, call SetUp() first' + return + endif + + call mkdir(s:plugdir . '/plugin/also', 'p') + call mkdir(s:plugdir . '/ftdetect', 'p') + call mkdir(s:plugdir . '/after', 'p') + set rtp& + let rtp = &rtp + filetype on + + let rtp_entries = split(rtp, ',') + for entry in rtp_entries + if entry =~? '\' + let first_after_entry = entry + break + endif + endfor + + exe 'split ' . s:plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 42') + wq + + exe 'split ' . s:plugdir . '/plugin/also/loaded.vim' + call setline(1, 'let g:plugin_also_works = 77') + wq + + exe 'split ' . s:plugdir . '/ftdetect/test.vim' + call setline(1, 'let g:ftdetect_works = 17') + wq + + packadd mytest + + call assert_equal(42, g:plugin_works) + call assert_equal(77, g:plugin_also_works) + call assert_equal(17, g:ftdetect_works) + call assert_true(len(&rtp) > len(rtp)) + call assert_match('/testdir/Xdir/pack/mine/opt/mytest\($\|,\)', &rtp) + + let new_after = match(&rtp, '/testdir/Xdir/pack/mine/opt/mytest/after,') + let forwarded = substitute(first_after_entry, '\\', '[/\\\\]', 'g') + let old_after = match(&rtp, ',' . forwarded . '\>') + call assert_true(new_after > 0, 'rtp is ' . &rtp) + call assert_true(old_after > 0, 'match ' . forwarded . ' in ' . &rtp) + call assert_true(new_after < old_after, 'rtp is ' . &rtp) + + " NOTE: '/.../opt/myte' forwardly matches with '/.../opt/mytest' + call mkdir(fnamemodify(s:plugdir, ':h') . '/myte', 'p') + let rtp = &rtp + packadd myte + + " Check the path of 'myte' is added + call assert_true(len(&rtp) > len(rtp)) + call assert_match('/testdir/Xdir/pack/mine/opt/myte\($\|,\)', &rtp) + + " Check exception + call assert_fails("packadd directorynotfound", 'E919:') + call assert_fails("packadd", 'E471:') +endfunc + +func Test_packadd_start() + let plugdir = s:topdir . '/pack/mine/start/other' + call mkdir(plugdir . '/plugin', 'p') + set rtp& + let rtp = &rtp + filetype on + + exe 'split ' . plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 24') + wq + + packadd other + + call assert_equal(24, g:plugin_works) + call assert_true(len(&rtp) > len(rtp)) + call assert_match('/testdir/Xdir/pack/mine/start/other\($\|,\)', &rtp) +endfunc + +func Test_packadd_noload() + call mkdir(s:plugdir . '/plugin', 'p') + call mkdir(s:plugdir . '/syntax', 'p') + set rtp& + let rtp = &rtp + + exe 'split ' . s:plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 42') + wq + let g:plugin_works = 0 + + packadd! mytest + + call assert_true(len(&rtp) > len(rtp)) + call assert_match('testdir/Xdir/pack/mine/opt/mytest\($\|,\)', &rtp) + call assert_equal(0, g:plugin_works) + + " check the path is not added twice + let new_rtp = &rtp + packadd! mytest + call assert_equal(new_rtp, &rtp) +endfunc + +func Test_packadd_symlink_dir() + if !has('unix') + return + endif + let top2_dir = s:topdir . '/Xdir2' + let real_dir = s:topdir . '/Xsym' + call mkdir(real_dir, 'p') + exec "silent !ln -s Xsym" top2_dir + let &rtp = top2_dir . ',' . top2_dir . '/after' + let &packpath = &rtp + + let s:plugdir = top2_dir . '/pack/mine/opt/mytest' + call mkdir(s:plugdir . '/plugin', 'p') + + exe 'split ' . s:plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 44') + wq + let g:plugin_works = 0 + + packadd mytest + + " Must have been inserted in the middle, not at the end + call assert_match('/pack/mine/opt/mytest,', &rtp) + call assert_equal(44, g:plugin_works) + + " No change when doing it again. + let rtp_before = &rtp + packadd mytest + call assert_equal(rtp_before, &rtp) + + set rtp& + let rtp = &rtp + exec "silent !rm" top2_dir +endfunc + +func Test_packadd_symlink_dir2() + if !has('unix') + return + endif + let top2_dir = s:topdir . '/Xdir2' + let real_dir = s:topdir . '/Xsym/pack' + call mkdir(top2_dir, 'p') + call mkdir(real_dir, 'p') + let &rtp = top2_dir . ',' . top2_dir . '/after' + let &packpath = &rtp + + exec "silent !ln -s ../Xsym/pack" top2_dir . '/pack' + let s:plugdir = top2_dir . '/pack/mine/opt/mytest' + call mkdir(s:plugdir . '/plugin', 'p') + + exe 'split ' . s:plugdir . '/plugin/test.vim' + call setline(1, 'let g:plugin_works = 48') + wq + let g:plugin_works = 0 + + packadd mytest + + " Must have been inserted in the middle, not at the end + call assert_match('/Xdir2/pack/mine/opt/mytest,', &rtp) + call assert_equal(48, g:plugin_works) + + " No change when doing it again. + let rtp_before = &rtp + packadd mytest + call assert_equal(rtp_before, &rtp) + + set rtp& + let rtp = &rtp + exec "silent !rm" top2_dir . '/pack' + exec "silent !rmdir" top2_dir +endfunc + +" Check command-line completion for 'packadd' +func Test_packadd_completion() + let optdir1 = &packpath . '/pack/mine/opt' + let optdir2 = &packpath . '/pack/candidate/opt' + + call mkdir(optdir1 . '/pluginA', 'p') + call mkdir(optdir1 . '/pluginC', 'p') + call mkdir(optdir2 . '/pluginB', 'p') + call mkdir(optdir2 . '/pluginC', 'p') + + let li = [] + call feedkeys(":packadd \')\call add(li, '\", 't') + call feedkeys(":packadd " . repeat("\", 2) . "')\call add(li, '\", 't') + call feedkeys(":packadd " . repeat("\", 3) . "')\call add(li, '\", 't') + call feedkeys(":packadd " . repeat("\", 4) . "')\call add(li, '\", 'tx') + call assert_equal("packadd pluginA", li[0]) + call assert_equal("packadd pluginB", li[1]) + call assert_equal("packadd pluginC", li[2]) + call assert_equal("packadd ", li[3]) +endfunc + +func Test_packloadall() + " plugin foo with an autoload directory + let fooplugindir = &packpath . '/pack/mine/start/foo/plugin' + call mkdir(fooplugindir, 'p') + call writefile(['let g:plugin_foo_number = 1234', + \ 'let g:plugin_foo_auto = bbb#value', + \ 'let g:plugin_extra_auto = extra#value'], fooplugindir . '/bar.vim') + let fooautodir = &packpath . '/pack/mine/start/foo/autoload' + call mkdir(fooautodir, 'p') + call writefile(['let bar#value = 77'], fooautodir . '/bar.vim') + + " plugin aaa with an autoload directory + let aaaplugindir = &packpath . '/pack/mine/start/aaa/plugin' + call mkdir(aaaplugindir, 'p') + call writefile(['let g:plugin_aaa_number = 333', + \ 'let g:plugin_aaa_auto = bar#value'], aaaplugindir . '/bbb.vim') + let aaaautodir = &packpath . '/pack/mine/start/aaa/autoload' + call mkdir(aaaautodir, 'p') + call writefile(['let bbb#value = 55'], aaaautodir . '/bbb.vim') + + " plugin extra with only an autoload directory + let extraautodir = &packpath . '/pack/mine/start/extra/autoload' + call mkdir(extraautodir, 'p') + call writefile(['let extra#value = 99'], extraautodir . '/extra.vim') + + packloadall + call assert_equal(1234, g:plugin_foo_number) + call assert_equal(55, g:plugin_foo_auto) + call assert_equal(99, g:plugin_extra_auto) + call assert_equal(333, g:plugin_aaa_number) + call assert_equal(77, g:plugin_aaa_auto) + + " only works once + call writefile(['let g:plugin_bar_number = 4321'], fooplugindir . '/bar2.vim') + packloadall + call assert_false(exists('g:plugin_bar_number')) + + " works when ! used + packloadall! + call assert_equal(4321, g:plugin_bar_number) +endfunc + +func Test_helptags() + let docdir1 = &packpath . '/pack/mine/start/foo/doc' + let docdir2 = &packpath . '/pack/mine/start/bar/doc' + call mkdir(docdir1, 'p') + call mkdir(docdir2, 'p') + call writefile(['look here: *look-here*'], docdir1 . '/bar.txt') + call writefile(['look away: *look-away*'], docdir2 . '/foo.txt') + exe 'set rtp=' . &packpath . '/pack/mine/start/foo,' . &packpath . '/pack/mine/start/bar' + + helptags ALL + + let tags1 = readfile(docdir1 . '/tags') + call assert_match('look-here', tags1[0]) + let tags2 = readfile(docdir2 . '/tags') + call assert_match('look-away', tags2[0]) + + call assert_fails('helptags abcxyz', 'E150:') +endfunc + +func Test_colorscheme() + let colordirrun = &packpath . '/runtime/colors' + let colordirstart = &packpath . '/pack/mine/start/foo/colors' + let colordiropt = &packpath . '/pack/mine/opt/bar/colors' + call mkdir(colordirrun, 'p') + call mkdir(colordirstart, 'p') + call mkdir(colordiropt, 'p') + call writefile(['let g:found_one = 1'], colordirrun . '/one.vim') + call writefile(['let g:found_two = 1'], colordirstart . '/two.vim') + call writefile(['let g:found_three = 1'], colordiropt . '/three.vim') + exe 'set rtp=' . &packpath . '/runtime' + + colorscheme one + call assert_equal(1, g:found_one) + colorscheme two + call assert_equal(1, g:found_two) + colorscheme three + call assert_equal(1, g:found_three) +endfunc + +func Test_colorscheme_completion() + let colordirrun = &packpath . '/runtime/colors' + let colordirstart = &packpath . '/pack/mine/start/foo/colors' + let colordiropt = &packpath . '/pack/mine/opt/bar/colors' + call mkdir(colordirrun, 'p') + call mkdir(colordirstart, 'p') + call mkdir(colordiropt, 'p') + call writefile(['let g:found_one = 1'], colordirrun . '/one.vim') + call writefile(['let g:found_two = 1'], colordirstart . '/two.vim') + call writefile(['let g:found_three = 1'], colordiropt . '/three.vim') + exe 'set rtp=' . &packpath . '/runtime' + + let li=[] + call feedkeys(":colorscheme " . repeat("\", 1) . "')\call add(li, '\", 't') + call feedkeys(":colorscheme " . repeat("\", 2) . "')\call add(li, '\", 't') + call feedkeys(":colorscheme " . repeat("\", 3) . "')\call add(li, '\", 't') + call feedkeys(":colorscheme " . repeat("\", 4) . "')\call add(li, '\", 'tx') + call assert_equal("colorscheme one", li[0]) + call assert_equal("colorscheme three", li[1]) + call assert_equal("colorscheme two", li[2]) + call assert_equal("colorscheme ", li[3]) +endfunc + +func Test_runtime() + let rundir = &packpath . '/runtime/extra' + let startdir = &packpath . '/pack/mine/start/foo/extra' + let optdir = &packpath . '/pack/mine/opt/bar/extra' + call mkdir(rundir, 'p') + call mkdir(startdir, 'p') + call mkdir(optdir, 'p') + call writefile(['let g:sequence .= "run"'], rundir . '/bar.vim') + call writefile(['let g:sequence .= "start"'], startdir . '/bar.vim') + call writefile(['let g:sequence .= "foostart"'], startdir . '/foo.vim') + call writefile(['let g:sequence .= "opt"'], optdir . '/bar.vim') + call writefile(['let g:sequence .= "xxxopt"'], optdir . '/xxx.vim') + exe 'set rtp=' . &packpath . '/runtime' + + let g:sequence = '' + runtime extra/bar.vim + call assert_equal('run', g:sequence) + let g:sequence = '' + runtime START extra/bar.vim + call assert_equal('start', g:sequence) + let g:sequence = '' + runtime OPT extra/bar.vim + call assert_equal('opt', g:sequence) + let g:sequence = '' + runtime PACK extra/bar.vim + call assert_equal('start', g:sequence) + let g:sequence = '' + runtime! PACK extra/bar.vim + call assert_equal('startopt', g:sequence) + let g:sequence = '' + runtime PACK extra/xxx.vim + call assert_equal('xxxopt', g:sequence) + + let g:sequence = '' + runtime ALL extra/bar.vim + call assert_equal('run', g:sequence) + let g:sequence = '' + runtime ALL extra/foo.vim + call assert_equal('foostart', g:sequence) + let g:sequence = '' + runtime! ALL extra/xxx.vim + call assert_equal('xxxopt', g:sequence) + let g:sequence = '' + runtime! ALL extra/bar.vim + call assert_equal('runstartopt', g:sequence) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 03348e5b9db3f057057a70581ef71180c3cb6527 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 21:33:28 +0800 Subject: vim-patch:8.2.3510: changes are only detected with one second accuracy Problem: Changes are only detected with one second accuracy. Solution: Use the nanosecond time if possible. (Leah Neukirchen, closes vim/vim#8873, closes vim/vim#8875) https://github.com/vim/vim/commit/0a7984af5601323fae7b3398f05a48087db7b767 In Nvim Test_checktime_fast() is also flaky. Add a delay to avoid that. --- src/nvim/buffer_defs.h | 2 ++ src/nvim/eval/funcs.c | 1 + src/nvim/fileio.c | 27 +++++++++++++++------------ src/nvim/memline.c | 4 ++++ src/nvim/testdir/test_stat.vim | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7b17c5b506..7ae5df164f 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -587,7 +587,9 @@ struct file_buffer { // where invoked long b_mtime; // last change time of original file + long b_mtime_ns; // nanoseconds of last change time long b_mtime_read; // last change time when reading + long b_mtime_read_ns; // nanoseconds of last read time uint64_t b_orig_size; // size of original file in bytes int b_orig_mode; // mode of original file time_t b_last_used; // time when the buffer was last used; used diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7772d9ffc2..c6baa105b0 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4539,6 +4539,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "mouse", "multi_byte", "multi_lang", + "nanotime", "num64", "packages", "path_extra", diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 8e1be3bbf7..475b44bc49 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -405,6 +405,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski if (os_fileinfo((char *)fname, &file_info)) { buf_store_file_info(curbuf, &file_info); curbuf->b_mtime_read = curbuf->b_mtime; + curbuf->b_mtime_read_ns = curbuf->b_mtime_ns; #ifdef UNIX /* * Use the protection bits of the original file for the swap file. @@ -421,7 +422,9 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski #endif } else { curbuf->b_mtime = 0; + curbuf->b_mtime_ns = 0; curbuf->b_mtime_read = 0; + curbuf->b_mtime_read_ns = 0; curbuf->b_orig_size = 0; curbuf->b_orig_mode = 0; } @@ -3695,11 +3698,12 @@ nofail: msg_puts_attr(_("don't quit the editor until the file is successfully written!"), attr | MSG_HIST); - /* Update the timestamp to avoid an "overwrite changed file" - * prompt when writing again. */ + // Update the timestamp to avoid an "overwrite changed file" + // prompt when writing again. if (os_fileinfo((char *)fname, &file_info_old)) { buf_store_file_info(buf, &file_info_old); buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; } } } @@ -3893,8 +3897,7 @@ static void msg_add_eol(void) static int check_mtime(buf_T *buf, FileInfo *file_info) { if (buf->b_mtime_read != 0 - && time_differs(file_info->stat.st_mtim.tv_sec, - buf->b_mtime_read)) { + && time_differs(file_info, buf->b_mtime_read, buf->b_mtime_read_ns)) { msg_scroll = true; // Don't overwrite messages here. msg_silent = 0; // Must give this prompt. // Don't use emsg() here, don't want to flush the buffers. @@ -3908,19 +3911,17 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) return OK; } -/// Return true if the times differ -/// -/// @param t1 first time -/// @param t2 second time -static bool time_differs(long t1, long t2) FUNC_ATTR_CONST +static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST { #if defined(__linux__) || defined(MSWIN) // On a FAT filesystem, esp. under Linux, there are only 5 bits to store // the seconds. Since the roundoff is done when flushing the inode, the // time may change unexpectedly by one second!!! - return t1 - t2 > 1 || t2 - t1 > 1; + return (long)file_info->stat.st_mtim.tv_sec - mtime > 1 + || mtime - (long)file_info->stat.st_mtim.tv_sec > 1 + || (long)file_info->stat.st_mtim.tv_nsec != mtime_ns; #else - return t1 != t2; + return (long)file_info->stat.st_mtim.tv_sec != mtime; #endif } @@ -4943,7 +4944,7 @@ int buf_check_timestamp(buf_T *buf) if (!(buf->b_flags & BF_NOTEDITED) && buf->b_mtime != 0 && (!(file_info_ok = os_fileinfo((char *)buf->b_ffname, &file_info)) - || time_differs(file_info.stat.st_mtim.tv_sec, buf->b_mtime) + || time_differs(&file_info, buf->b_mtime, buf->b_mtime_ns) || (int)file_info.stat.st_mode != buf->b_orig_mode)) { const long prev_b_mtime = buf->b_mtime; @@ -5034,6 +5035,7 @@ int buf_check_timestamp(buf_T *buf) // Only timestamp changed, store it to avoid a warning // in check_mtime() later. buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; } } } @@ -5262,6 +5264,7 @@ void buf_store_file_info(buf_T *buf, FileInfo *file_info) FUNC_ATTR_NONNULL_ALL { buf->b_mtime = file_info->stat.st_mtim.tv_sec; + buf->b_mtime_ns = file_info->stat.st_mtim.tv_nsec; buf->b_orig_size = os_fileinfo_size(file_info); buf->b_orig_mode = (int)file_info->stat.st_mode; } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 9925971783..004ef36b36 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -704,11 +704,14 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) long_to_char((long)os_fileinfo_inode(&file_info), b0p->b0_ino); buf_store_file_info(buf, &file_info); buf->b_mtime_read = buf->b_mtime; + buf->b_mtime_read_ns = buf->b_mtime_ns; } else { long_to_char(0L, b0p->b0_mtime); long_to_char(0L, b0p->b0_ino); buf->b_mtime = 0; + buf->b_mtime_ns = 0; buf->b_mtime_read = 0; + buf->b_mtime_read_ns = 0; buf->b_orig_size = 0; buf->b_orig_mode = 0; } @@ -1720,6 +1723,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) FileInfo file_info; if (!os_fileinfo((char *)buf->b_ffname, &file_info) || file_info.stat.st_mtim.tv_sec != buf->b_mtime_read + || file_info.stat.st_mtim.tv_nsec != buf->b_mtime_read_ns || os_fileinfo_size(&file_info) != buf->b_orig_size) { ml_preserve(buf, false, do_fsync); did_check_timestamps = false; diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index 170358e023..a6fe31b85a 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -1,5 +1,7 @@ " Tests for stat functions and checktime +source check.vim + func CheckFileTime(doSleep) let fnames = ['Xtest1.tmp', 'Xtest2.tmp', 'Xtest3.tmp'] let times = [] @@ -74,6 +76,40 @@ func Test_checktime() call delete(fname) endfunc +func Test_checktime_fast() + CheckFeature nanotime + + let fname = 'Xtest.tmp' + + let fl = ['Hello World!'] + call writefile(fl, fname) + set autoread + exec 'e' fname + let fl = readfile(fname) + let fl[0] .= ' - checktime' + sleep 10m " make test less flaky in Nvim + call writefile(fl, fname) + checktime + call assert_equal(fl[0], getline(1)) + + call delete(fname) +endfunc + +func Test_autoread_fast() + CheckFeature nanotime + + new Xautoread + set autoread + call setline(1, 'foo') + + w! + silent !echo bar > Xautoread + checktime + + call assert_equal('bar', trim(getline(1))) + call delete('Xautoread') +endfunc + func Test_autoread_file_deleted() new Xautoread set autoread -- cgit From bad22bd65668bb0677be35759a793e60543df980 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 21:33:28 +0800 Subject: vim-patch:8.2.3512: timestamp test fails on some systems Problem: Timestamp test fails on some systems. Solution: Sleep for a short while. https://github.com/vim/vim/commit/accf4ed352c07ffe59022377c42d36e12dd6d461 --- src/nvim/testdir/test_stat.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index a6fe31b85a..841b5d2db7 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -104,6 +104,7 @@ func Test_autoread_fast() w! silent !echo bar > Xautoread + sleep 10m checktime call assert_equal('bar', trim(getline(1))) -- cgit From 21283aa445a0b2e13f1c6ac56512165c5bc17922 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 21:33:28 +0800 Subject: vim-patch:8.2.3514: autoread test with nano second time sometimes fails Problem: Autoread test with nano second time sometimes fails. Solution: Mark the test as being flaky. https://github.com/vim/vim/commit/eaa006dae3d5730e3b6dead27905444998b2cf8e --- src/nvim/testdir/test_stat.vim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index 841b5d2db7..893a740ee2 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -98,16 +98,18 @@ endfunc func Test_autoread_fast() CheckFeature nanotime + " this is timing sensitive + let g:test_is_flaky = 1 + new Xautoread - set autoread + setlocal autoread call setline(1, 'foo') - w! silent !echo bar > Xautoread sleep 10m checktime - call assert_equal('bar', trim(getline(1))) + call delete('Xautoread') endfunc -- cgit From 0f1c7059361f5db894fa0715ee6ee15d444a9c2b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 21:33:28 +0800 Subject: vim-patch:8.2.3515: nano time test fails on Mac and FreeBSD Problem: Nano time test fails on Mac and FreeBSD. Solution: Also check nano time when not on Linux. (Ozaki Kiichi, closes vim/vim#9000) https://github.com/vim/vim/commit/def69dffb3d09a69629b071c89b7893a1783ba53 --- src/nvim/fileio.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 475b44bc49..110981159f 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3913,15 +3913,15 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST { + return (long)file_info->stat.st_mtim.tv_nsec != mtime_ns #if defined(__linux__) || defined(MSWIN) - // On a FAT filesystem, esp. under Linux, there are only 5 bits to store - // the seconds. Since the roundoff is done when flushing the inode, the - // time may change unexpectedly by one second!!! - return (long)file_info->stat.st_mtim.tv_sec - mtime > 1 - || mtime - (long)file_info->stat.st_mtim.tv_sec > 1 - || (long)file_info->stat.st_mtim.tv_nsec != mtime_ns; + // On a FAT filesystem, esp. under Linux, there are only 5 bits to store + // the seconds. Since the roundoff is done when flushing the inode, the + // time may change unexpectedly by one second!!! + || (long)file_info->stat.st_mtim.tv_sec - mtime > 1 + || mtime - (long)file_info->stat.st_mtim.tv_sec > 1; #else - return (long)file_info->stat.st_mtim.tv_sec != mtime; + || (long)file_info->stat.st_mtim.tv_sec != mtime; #endif } -- cgit From b2606673cc367c018527203fbc7c3e85cc9a4e8e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Feb 2022 21:33:28 +0800 Subject: vim-patch:8.2.3534: autoread test is a bit flaky Problem: Autoread test is a bit flaky. Solution: Wait a brief moment before overwriting the file. https://github.com/vim/vim/commit/944eeb44fb6e9d6d28474a1348d27c07873892f9 --- src/nvim/testdir/test_stat.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index 893a740ee2..b44f3e9b94 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -105,7 +105,8 @@ func Test_autoread_fast() setlocal autoread call setline(1, 'foo') w! - silent !echo bar > Xautoread + sleep 10m + call writefile(['bar'], 'Xautoread') sleep 10m checktime call assert_equal('bar', trim(getline(1))) -- cgit From 2ed6b99a42c41ca4cc28f57dc79274263384d5bb Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Tue, 8 Feb 2022 14:13:35 +0100 Subject: refactor(PVS/V560): part of conditional expression is always true/false --- src/mpack/lmpack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mpack/lmpack.c b/src/mpack/lmpack.c index 126f2f3824..53d7092a0c 100644 --- a/src/mpack/lmpack.c +++ b/src/mpack/lmpack.c @@ -246,7 +246,7 @@ static mpack_uint32_t lmpack_objlen(lua_State *L, int *is_array) } end: - if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1) + if ((size_t)-1 > (mpack_uint32_t)-1 && len > (mpack_uint32_t)-1) // -V560 /* msgpack spec doesn't allow lengths > 32 bits */ len = (mpack_uint32_t)-1; assert(top == lua_gettop(L)); -- cgit From 5220891571a799fd630cbcbe836d1f9e3d2dc1fa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 11:35:25 +0800 Subject: vim-patch:8.2.4343: when reloading not all properties are detected Problem: When reloading not all properties are detected. Solution: Add the "edit" value to v:fcs_choice. (Rob Pilling, closes vim/vim#9579) https://github.com/vim/vim/commit/8196e94a8b72ed8618605cb66615571313097d78 Cherry-pick some test changes from patch 8.1.1826. --- src/nvim/fileio.c | 70 ++++++++++++--------- src/nvim/message.c | 1 + src/nvim/spellfile.c | 2 +- src/nvim/testdir/setup.vim | 1 + src/nvim/testdir/test_filechanged.vim | 111 +++++++++++++++++++++++++++++++--- 5 files changed, 147 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 110981159f..f5824d83be 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2013,10 +2013,8 @@ static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp) return lnum; } -/* - * Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary to be - * equal to the buffer "buf". Used for calling readfile(). - */ +/// Fill "*eap" to force the 'fileencoding', 'fileformat' and 'binary' to be +/// equal to the buffer "buf". Used for calling readfile(). void prep_exarg(exarg_T *eap, const buf_T *buf) FUNC_ATTR_NONNULL_ALL { @@ -4901,13 +4899,11 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) return retval; } -/* - * Check if buffer "buf" has been changed. - * Also check if the file for a new buffer unexpectedly appeared. - * return 1 if a changed buffer was found. - * return 2 if a message has been displayed. - * return 0 otherwise. - */ +/// Check if buffer "buf" has been changed. +/// Also check if the file for a new buffer unexpectedly appeared. +/// return 1 if a changed buffer was found. +/// return 2 if a message has been displayed. +/// return 0 otherwise. int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { @@ -4916,7 +4912,11 @@ int buf_check_timestamp(buf_T *buf) char *mesg = NULL; char *mesg2 = ""; bool helpmesg = false; - bool reload = false; + enum { + RELOAD_NONE, + RELOAD_NORMAL, + RELOAD_DETECT + } reload = RELOAD_NONE; bool can_reload = false; uint64_t orig_size = buf->b_orig_size; int orig_mode = buf->b_orig_mode; @@ -4969,7 +4969,7 @@ int buf_check_timestamp(buf_T *buf) // If 'autoread' is set, the buffer has no changes and the file still // exists, reload the buffer. Use the buffer-local option value if it // was set, the global option value otherwise. - reload = true; + reload = RELOAD_NORMAL; } else { if (!file_info_ok) { reason = "deleted"; @@ -5000,7 +5000,9 @@ int buf_check_timestamp(buf_T *buf) } s = get_vim_var_str(VV_FCS_CHOICE); if (STRCMP(s, "reload") == 0 && *reason != 'd') { - reload = true; + reload = RELOAD_NORMAL; + } else if (STRCMP(s, "edit") == 0) { + reload = RELOAD_DETECT; } else if (STRCMP(s, "ask") == 0) { n = false; } else { @@ -5064,9 +5066,15 @@ int buf_check_timestamp(buf_T *buf) xstrlcat(tbuf, "\n", tbuf_len - 1); xstrlcat(tbuf, mesg2, tbuf_len - 1); } - if (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf, - (char_u *)_("&OK\n&Load File"), 1, NULL, true) == 2) { - reload = true; + switch (do_dialog(VIM_WARNING, (char_u *)_("Warning"), (char_u *)tbuf, + (char_u *)_("&OK\n&Load File\nLoad File &and Options"), + 1, NULL, true)) { + case 2: + reload = RELOAD_NORMAL; + break; + case 3: + reload = RELOAD_DETECT; + break; } } else if (State > NORMAL_BUSY || (State & CMDLINE) || already_warned) { if (*mesg2 != NUL) { @@ -5100,9 +5108,9 @@ int buf_check_timestamp(buf_T *buf) xfree(tbuf); } - if (reload) { + if (reload != RELOAD_NONE) { // Reload the buffer. - buf_reload(buf, orig_mode); + buf_reload(buf, orig_mode, reload == RELOAD_DETECT); if (buf->b_p_udf && buf->b_ffname != NULL) { char_u hash[UNDO_HASH_SIZE]; @@ -5120,13 +5128,11 @@ int buf_check_timestamp(buf_T *buf) return retval; } -/* - * Reload a buffer that is already loaded. - * Used when the file was changed outside of Vim. - * "orig_mode" is buf->b_orig_mode before the need for reloading was detected. - * buf->b_orig_mode may have been reset already. - */ -void buf_reload(buf_T *buf, int orig_mode) +/// Reload a buffer that is already loaded. +/// Used when the file was changed outside of Vim. +/// "orig_mode" is buf->b_orig_mode before the need for reloading was detected. +/// buf->b_orig_mode may have been reset already. +void buf_reload(buf_T *buf, int orig_mode, bool reload_options) { exarg_T ea; pos_T old_cursor; @@ -5141,11 +5147,15 @@ void buf_reload(buf_T *buf, int orig_mode) // set curwin/curbuf for "buf" and save some things aucmd_prepbuf(&aco, buf); - // We only want to read the text from the file, not reset the syntax - // highlighting, clear marks, diff status, etc. Force the fileformat and - // encoding to be the same. + // Unless reload_options is set, we only want to read the text from the + // file, not reset the syntax highlighting, clear marks, diff status, etc. + // Force the fileformat and encoding to be the same. + if (reload_options) { + memset(&ea, 0, sizeof(ea)); + } else { + prep_exarg(&ea, buf); + } - prep_exarg(&ea, buf); old_cursor = curwin->w_cursor; old_topline = curwin->w_topline; diff --git a/src/nvim/message.c b/src/nvim/message.c index 93742ccbdb..b39450cdc6 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -3464,6 +3464,7 @@ void msg_advance(int col) /// /// @param textfiel IObuff for inputdialog(), NULL otherwise /// @param ex_cmd when TRUE pressing : accepts default and starts Ex command +/// @returns 0 if cancelled, otherwise the nth button (1-indexed). int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton, char_u *textfield, int ex_cmd) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index f6b95f37b1..d7b220b3f6 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5654,7 +5654,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo // If the .add file is edited somewhere, reload it. if (buf != NULL) { - buf_reload(buf, buf->b_orig_mode); + buf_reload(buf, buf->b_orig_mode, false); } redraw_all_later(SOME_VALID); diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index fdae0697c3..15e3b31498 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -10,6 +10,7 @@ let s:did_load = 1 set backspace= set directory^=. set fillchars=vert:\|,fold:- +set fsync set laststatus=1 set listchars=eol:$ set joinspaces diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index b4c32fcc16..06ccd6e85f 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -1,9 +1,10 @@ " Tests for when a file was changed outside of Vim. +source check.vim + func Test_FileChangedShell_reload() - if !has('unix') - return - endif + CheckUnix + augroup testreload au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload' augroup END @@ -90,11 +91,107 @@ func Test_FileChangedShell_reload() call delete('Xchanged_r') endfunc +func Test_FileChangedShell_edit() + CheckUnix + + new Xchanged_r + call setline(1, 'reload this') + set fileformat=unix + write + + " File format changed, reload (content only, no 'ff' etc) + augroup testreload + au! + au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'reload' + augroup END + call assert_equal(&fileformat, 'unix') + sleep 10m " make the test less flaky in Nvim + call writefile(["line1\r", "line2\r"], 'Xchanged_r') + let g:reason = '' + checktime + call assert_equal('changed', g:reason) + call assert_equal(&fileformat, 'unix') + call assert_equal("line1\r", getline(1)) + call assert_equal("line2\r", getline(2)) + %s/\r + write + + " File format changed, reload with 'ff', etc + augroup testreload + au! + au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'edit' + augroup END + call assert_equal(&fileformat, 'unix') + sleep 10m " make the test less flaky in Nvim + call writefile(["line1\r", "line2\r"], 'Xchanged_r') + let g:reason = '' + checktime + call assert_equal('changed', g:reason) + call assert_equal(&fileformat, 'dos') + call assert_equal('line1', getline(1)) + call assert_equal('line2', getline(2)) + set fileformat=unix + write + + au! testreload + bwipe! + call delete(undofile('Xchanged_r')) + call delete('Xchanged_r') +endfunc + +func Test_FileChangedShell_edit_dialog() + throw 'Skipped: requires a UI to be active' + CheckNotGui + + new Xchanged_r + call setline(1, 'reload this') + set fileformat=unix + write + + " File format changed, reload (content only) via prompt + augroup testreload + au! + au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' + augroup END + call assert_equal(&fileformat, 'unix') + call writefile(["line1\r", "line2\r"], 'Xchanged_r') + let g:reason = '' + call feedkeys('L', 'L') " load file content only + checktime + call assert_equal('changed', g:reason) + call assert_equal(&fileformat, 'unix') + call assert_equal("line1\r", getline(1)) + call assert_equal("line2\r", getline(2)) + %s/\r + write + + " File format changed, reload (file and options) via prompt + augroup testreload + au! + au FileChangedShell Xchanged_r let g:reason = v:fcs_reason | let v:fcs_choice = 'ask' + augroup END + call assert_equal(&fileformat, 'unix') + call writefile(["line1\r", "line2\r"], 'Xchanged_r') + let g:reason = '' + call feedkeys('a', 'L') " load file content and options + checktime + call assert_equal('changed', g:reason) + call assert_equal(&fileformat, 'dos') + call assert_equal("line1", getline(1)) + call assert_equal("line2", getline(2)) + set fileformat=unix + write + + au! testreload + bwipe! + call delete(undofile('Xchanged_r')) + call delete('Xchanged_r') +endfunc + func Test_file_changed_dialog() - throw 'Skipped: requires a UI to a active' - if !has('unix') || has('gui_running') - return - endif + throw 'Skipped: requires a UI to be active' + CheckUnix + CheckNotGui au! FileChangedShell new Xchanged_d -- cgit From 8a80ab27bd7b819f4c8b48976b641dab002f0247 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 18:56:30 +0800 Subject: vim-patch:8.1.1846: inconsistently using GetVimCommand() and v:progpath Problem: Inconsistently using GetVimCommand() and v:progpath. (Daniel Hahler) Solution: Use GetVimCommand(). (closes vim/vim#4806) https://github.com/vim/vim/commit/93344c2d707d9953f351c944e6a237c9916f69a3 Cherry-pick a change to test_profile.vim from patch 8.1.1544. Cherry-pick a change to test_vimscript.vim from patch 8.1.1826. Some of the args are no-op in Nvim, and `-i NONE` and `--headless` are already added by `GetVimCommand()`. I'll try to match the order of args in upstream, substituting `--not-a-term` with `--headless`. --- src/nvim/testdir/test_autocmd.vim | 6 +++--- src/nvim/testdir/test_normal.vim | 8 ++++---- src/nvim/testdir/test_profile.vim | 31 ++++++++++++++++--------------- src/nvim/testdir/test_suspend.vim | 4 ++-- src/nvim/testdir/test_system.vim | 26 +++++++++++++------------- src/nvim/testdir/test_vimscript.vim | 5 ++++- 6 files changed, 42 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index a8d51ef598..e4f4583b1a 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -502,7 +502,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost() [CODE] call writefile(content, 'Xvimrc') - call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq') + call system(GetVimCommand('Xvimrc') .. ' --headless --noplugins -S Session.vim -c cq') let errors = join(readfile('Xerrors')) call assert_match('E814', errors) @@ -562,7 +562,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost2() [CODE] call writefile(content, 'Xvimrc') - call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq') + call system(GetVimCommand('Xvimrc') .. ' --headless --noplugins -S Session.vim -c cq') let errors = join(readfile('Xerrors')) " This probably only ever matches on unix. call assert_notmatch('Caught deadly signal SEGV', errors) @@ -1506,7 +1506,7 @@ func Test_bufunload_all() call writefile(content, 'Xtest') call delete('Xout') - call system(v:progpath. ' -u NORC -i NONE -N -S Xtest') + call system(GetVimCommandClean() .. ' -N --headless -S Xtest') call assert_true(filereadable('Xout')) call delete('Xxx1') diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 5b7cf6fee5..a2de34de69 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1118,7 +1118,7 @@ func Test_normal20_exmode() endif call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript') call writefile(['1', '2'], 'Xfile') - call system(v:progpath .' -e -s < Xscript Xfile') + call system(GetVimCommand() .. ' -e -s < Xscript Xfile') let a=readfile('Xfile2') call assert_equal(['1', 'foo', 'bar', '2'], a) @@ -1171,13 +1171,13 @@ func Test_normal22_zet() endfor call writefile(['1', '2'], 'Xfile_Test_normal22_zet') - let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins' - call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet') + let args = ' -N -i NONE --noplugins -X --headless' + call system(GetVimCommand() .. args .. ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet') let a = readfile('Xfile_Test_normal22_zet') call assert_equal([], a) " Test for ZQ call writefile(['1', '2'], 'Xfile_Test_normal22_zet') - call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet') + call system(GetVimCommand() . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet') let a = readfile('Xfile_Test_normal22_zet') call assert_equal(['1', '2'], a) diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim index 4b0097617e..fdb6f13e2b 100644 --- a/src/nvim/testdir/test_profile.vim +++ b/src/nvim/testdir/test_profile.vim @@ -1,8 +1,9 @@ " Test Vim profiler -if !has('profile') - finish -endif +source check.vim +CheckFeature profile + +source shared.vim source screendump.vim func Test_profile_func() @@ -37,7 +38,7 @@ func Test_profile_func() [CODE] call writefile(lines, 'Xprofile_func.vim') - call system(v:progpath + call system(GetVimCommand() \ . ' -es --clean' \ . ' -c "so Xprofile_func.vim"' \ . ' -c "qall!"') @@ -124,8 +125,8 @@ func Test_profile_func_with_ifelse() [CODE] call writefile(lines, 'Xprofile_func.vim') - call system(v:progpath - \ . ' -es -u NONE -U NONE -i NONE --noplugin' + call system(GetVimCommand() + \ . ' -es -i NONE --noplugin' \ . ' -c "profile start Xprofile_func.log"' \ . ' -c "profile func Foo*"' \ . ' -c "so Xprofile_func.vim"' @@ -237,8 +238,8 @@ func Test_profile_func_with_trycatch() [CODE] call writefile(lines, 'Xprofile_func.vim') - call system(v:progpath - \ . ' -es -u NONE -U NONE -i NONE --noplugin' + call system(GetVimCommand() + \ . ' -es -i NONE --noplugin' \ . ' -c "profile start Xprofile_func.log"' \ . ' -c "profile func Foo*"' \ . ' -c "so Xprofile_func.vim"' @@ -324,8 +325,8 @@ func Test_profile_file() [CODE] call writefile(lines, 'Xprofile_file.vim') - call system(v:progpath - \ . ' -es --clean' + call system(GetVimCommandClean() + \ . ' -es' \ . ' -c "profile start Xprofile_file.log"' \ . ' -c "profile file Xprofile_file.vim"' \ . ' -c "so Xprofile_file.vim"' @@ -369,8 +370,8 @@ func Test_profile_file_with_cont() \ ] call writefile(lines, 'Xprofile_file.vim') - call system(v:progpath - \ . ' -es -u NONE -U NONE -i NONE --noplugin' + call system(GetVimCommandClean() + \ . ' -es' \ . ' -c "profile start Xprofile_file.log"' \ . ' -c "profile file Xprofile_file.vim"' \ . ' -c "so Xprofile_file.vim"' @@ -427,7 +428,7 @@ func Test_profile_truncate_mbyte() \ ] call writefile(lines, 'Xprofile_file.vim') - call system(v:progpath + call system(GetVimCommandClean() \ . ' -es --cmd "set enc=utf-8"' \ . ' -c "profile start Xprofile_file.log"' \ . ' -c "profile file Xprofile_file.vim"' @@ -474,7 +475,7 @@ func Test_profdel_func() call Foo3() [CODE] call writefile(lines, 'Xprofile_file.vim') - call system(v:progpath . ' -es --clean -c "so Xprofile_file.vim" -c q') + call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q') call assert_equal(0, v:shell_error) let lines = readfile('Xprofile_file.log') @@ -509,7 +510,7 @@ func Test_profdel_star() call Foo() [CODE] call writefile(lines, 'Xprofile_file.vim') - call system(v:progpath . ' -es --clean -c "so Xprofile_file.vim" -c q') + call system(GetVimCommandClean() . ' -es -c "so Xprofile_file.vim" -c q') call assert_equal(0, v:shell_error) let lines = readfile('Xprofile_file.log') diff --git a/src/nvim/testdir/test_suspend.vim b/src/nvim/testdir/test_suspend.vim index 4b3bd5eadf..bf88bd4453 100644 --- a/src/nvim/testdir/test_suspend.vim +++ b/src/nvim/testdir/test_suspend.vim @@ -26,8 +26,8 @@ func Test_suspend() " Wait for shell prompt. call WaitForAssert({-> assert_match('[$#] $', term_getline(buf, '.'))}) - call term_sendkeys(buf, v:progpath - \ . " --clean -X" + call term_sendkeys(buf, GetVimCommandClean() + \ . " -X" \ . " -c 'set nu'" \ . " -c 'call setline(1, \"foo\")'" \ . " Xfoo\") diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim index 5b8079d7b6..18692f42c9 100644 --- a/src/nvim/testdir/test_system.vim +++ b/src/nvim/testdir/test_system.vim @@ -50,11 +50,11 @@ endfunc func Test_system_exmode() if has('unix') " echo $? only works on Unix - let cmd = ' -es --headless -u NONE -c "source Xscript" +q; echo "result=$?"' + let cmd = ' -es -c "source Xscript" +q; echo "result=$?"' " Need to put this in a script, "catch" isn't found after an unknown " function. call writefile(['try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript') - let a = system(v:progpath . cmd) + let a = system(GetVimCommand() . cmd) call assert_match('result=0', a) call assert_equal(0, v:shell_error) endif @@ -62,33 +62,33 @@ func Test_system_exmode() " Error before try does set error flag. call writefile(['call nosuchfunction()', 'try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript') if has('unix') " echo $? only works on Unix - let a = system(v:progpath . cmd) + let a = system(GetVimCommand() . cmd) call assert_notequal('0', a[0]) endif - let cmd = ' -es --headless -u NONE -c "source Xscript" +q' - let a = system(v:progpath . cmd) + let cmd = ' -es -c "source Xscript" +q' + let a = system(GetVimCommand() . cmd) call assert_notequal(0, v:shell_error) call delete('Xscript') if has('unix') " echo $? only works on Unix - let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q; echo $?' - let a = system(v:progpath. cmd) + let cmd = ' -es -c "call doesnotexist()" +q; echo $?' + let a = system(GetVimCommand() . cmd) call assert_notequal(0, a[0]) endif - let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q' - let a = system(v:progpath. cmd) + let cmd = ' -es -c "call doesnotexist()" +q' + let a = system(GetVimCommand(). cmd) call assert_notequal(0, v:shell_error) if has('unix') " echo $? only works on Unix - let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q; echo $?' - let a = system(v:progpath. cmd) + let cmd = ' -es -c "call doesnotexist()|let a=1" +q; echo $?' + let a = system(GetVimCommand() . cmd) call assert_notequal(0, a[0]) endif - let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q' - let a = system(v:progpath. cmd) + let cmd = ' -es -c "call doesnotexist()|let a=1" +q' + let a = system(GetVimCommand() . cmd) call assert_notequal(0, v:shell_error) endfunc diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 75a965f16d..f93eb6e274 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1,6 +1,9 @@ " Test various aspects of the Vim script language. " Most of this was formerly in test49. +source check.vim +source shared.vim + "------------------------------------------------------------------------------- " Test environment {{{1 "------------------------------------------------------------------------------- @@ -1744,7 +1747,7 @@ func Test_function_defined_line() [CODE] call writefile(lines, 'Xtest.vim') - let res = system(v:progpath .. ' --clean -es -X -S Xtest.vim') + let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim') call assert_equal(0, v:shell_error) let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*') -- cgit From c9b46f154bfd1208310f8de931e899f562da9a95 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 18:56:30 +0800 Subject: vim-patch:8.1.1955: tests contain typos Problem: Tests contain typos. Solution: Correct the typos. (Dominique Pelle) https://github.com/vim/vim/commit/1bc353b6f1b063e189e0cef26f8dc586dcf9161f Other changes are either N/A or already applied. --- src/nvim/testdir/test_cindent.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 5dc54111e7..e5933ed80b 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -815,7 +815,7 @@ func Test_cindent_1() } } - public: // <-- this was incoreectly indented before!! + public: // <-- this was incorectly indented before!! void testfall(); protected: void testfall(); @@ -1792,7 +1792,7 @@ func Test_cindent_1() } } - public: // <-- this was incoreectly indented before!! + public: // <-- this was incorectly indented before!! void testfall(); protected: void testfall(); -- cgit From dcefd48c1b7023bb1dce25bf9f6cfce47edbe993 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 18:56:30 +0800 Subject: vim-patch:8.2.0156: various typos in source files and tests Problem: Various typos in source files and tests. Solution: Fix the typos. (Emir Sari, closes vim/vim#5532) https://github.com/vim/vim/commit/4b96df5a017a04141c4e901b1fc5704a3ca48099 --- src/nvim/buffer.c | 3 ++- src/nvim/charset.c | 4 ++-- src/nvim/ex_docmd.c | 2 +- src/nvim/getchar.c | 2 +- src/nvim/option.c | 2 +- src/nvim/testdir/test_breakindent.vim | 2 +- src/nvim/testdir/test_cindent.vim | 9 ++++++--- src/nvim/testdir/test_digraph.vim | 4 ++-- src/nvim/testdir/test_edit.vim | 2 +- src/nvim/testdir/test_quickfix.vim | 2 +- src/nvim/testdir/test_registers.vim | 2 +- src/nvim/testdir/test_stat.vim | 2 +- 12 files changed, 20 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 38b045b31c..19e5bb69f3 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1441,7 +1441,7 @@ void set_curbuf(buf_T *buf, int action) set_bufref(&prevbufref, prevbuf); set_bufref(&newbufref, buf); - // Autocommands may delete the curren buffer and/or the buffer we want to go + // Autocommands may delete the current buffer and/or the buffer we want to go // to. In those cases don't close the buffer. if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf) || (bufref_valid(&prevbufref) && bufref_valid(&newbufref) @@ -1454,6 +1454,7 @@ void set_curbuf(buf_T *buf, int action) } if (bufref_valid(&prevbufref) && !aborting()) { win_T *previouswin = curwin; + // Do not sync when in Insert mode and the buffer is open in // another window, might be a timer doing something in another // window. diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 583a040ed1..f4882e57e1 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1439,7 +1439,7 @@ bool vim_isblankline(char_u *lbuf) /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. /// @param strict If true, fail if the number has unexpected trailing -/// alpha-numeric chars: *len is set to 0 and nothing else is +/// alphanumeric chars: *len is set to 0 and nothing else is /// returned. void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen, @@ -1585,7 +1585,7 @@ vim_str2nr_hex: #undef PARSE_NUMBER vim_str2nr_proceed: - // Check for an alpha-numeric character immediately following, that is + // Check for an alphanumeric character immediately following, that is // most likely a typo. if (strict && ptr - (const char *)start != maxlen && ASCII_ISALNUM(*ptr)) { return; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4dba0b97ed..18fff47a9c 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7485,7 +7485,7 @@ static void ex_edit(exarg_T *eap) do_exedit(eap, NULL); } -/// ":edit " command and alikes. +/// ":edit " command and alike. /// /// @param old_curwin curwin before doing a split or NULL void do_exedit(exarg_T *eap, win_T *old_curwin) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5d8a8ddbfe..695656d072 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2466,7 +2466,7 @@ static int vgetorpeek(bool advance) /// 1. a scriptfile /// 2. the keyboard /// -/// As much characters as we can get (up to 'maxlen') are put in "buf" and +/// As many characters as we can get (up to 'maxlen') are put in "buf" and /// NUL terminated (buffer length must be 'maxlen' + 1). /// Minimum for "maxlen" is 3!!!! /// diff --git a/src/nvim/option.c b/src/nvim/option.c index c8e50d4494..d97a22c342 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4277,7 +4277,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, } // Save the global value before changing anything. This is needed as for - // a global-only option setting the "local value" infact sets the global + // a global-only option setting the "local value" in fact sets the global // value (since there is only one value). if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) { old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index b619f2adb6..438edb0257 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -1,7 +1,7 @@ " Test for breakindent " " Note: if you get strange failures when adding new tests, it might be that -" while the test is run, the breakindent cacheing gets in its way. +" while the test is run, the breakindent caching gets in its way. " It helps to change the tabstop setting and force a redraw (e.g. see " Test_breakindent08()) if !exists('+breakindent') diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index e5933ed80b..4b702bf2b8 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -815,7 +815,7 @@ func Test_cindent_1() } } - public: // <-- this was incorectly indented before!! + public: // <-- this was incorrectly indented before!! void testfall(); protected: void testfall(); @@ -1792,7 +1792,7 @@ func Test_cindent_1() } } - public: // <-- this was incorectly indented before!! + public: // <-- this was incorrectly indented before!! void testfall(); protected: void testfall(); @@ -5302,9 +5302,12 @@ endfunc " this was going beyond the end of the line. func Test_cindent_case() new - call setline(1, "case x: // x") + call setline(1, 'case x: // x') set cindent norm! f:a: + call assert_equal('case x:: // x', getline(1)) + + set cindent& bwipe! endfunc diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index d23748a3e3..5965ee48ef 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -81,7 +81,7 @@ func Test_digraphs() call Put_Dig(".e") call Put_Dig("a.") " not defined call assert_equal(['ḃ', 'Ä—', '.'], getline(line('.')-2,line('.'))) - " Diaresis + " Diaeresis call Put_Dig("a:") call Put_Dig(":u") call Put_Dig("b:") " not defined @@ -288,7 +288,7 @@ func Test_digraphs_option() call Put_Dig_BS(".","e") call Put_Dig_BS("a",".") " not defined call assert_equal(['ḃ', 'Ä—', '.'], getline(line('.')-2,line('.'))) - " Diaresis + " Diaeresis call Put_Dig_BS("a",":") call Put_Dig_BS(":","u") call Put_Dig_BS("b",":") " not defined diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim index a1f6a84a99..360b3aaaa0 100644 --- a/src/nvim/testdir/test_edit.vim +++ b/src/nvim/testdir/test_edit.vim @@ -590,7 +590,7 @@ func Test_edit_CTRL_K() call feedkeys("A\\\\\\\\", 'tnix') call assert_equal(['AA'], getline(1, '$')) - " press an unexecpted key after dictionary completion + " press an unexpected key after dictionary completion %d call setline(1, 'A') call cursor(1, 1) diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index c4d70fb1de..6852f53ea8 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -2739,7 +2739,7 @@ func Test_cwindow_jump() call assert_true(winnr('$') == 2) call assert_true(winnr() == 1) - " Jumping to a file from the location list window should find a usuable + " Jumping to a file from the location list window should find a usable " window by wrapping around the window list. enew | only call setloclist(0, [], 'f') diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 2e92d9aa2f..23e39eba35 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -219,7 +219,7 @@ func Test_set_register() call setreg('=', 'b', 'a') call assert_equal('regwrite', getreg('=')) - " Test for settting a list of lines to special registers + " Test for setting a list of lines to special registers call setreg('/', []) call assert_equal('', @/) call setreg('=', []) diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index b44f3e9b94..d3059664e9 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -7,7 +7,7 @@ func CheckFileTime(doSleep) let times = [] let result = 0 - " Use three files istead of localtim(), with a network filesystem the file + " Use three files instead of localtim(), with a network filesystem the file " times may differ at bit let fl = ['Hello World!'] for fname in fnames -- cgit From abf42b7ec6b408d4b4ce8c66b6a8a77c638d3c75 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Feb 2022 13:08:00 +0000 Subject: test(old): cherry-pick test_visual changes from v8.2.0369 https://github.com/vim/vim/commit/1671f4488105ee12a6a8558ae351436c26ab55fc Omit Test_AAA_start_visual_mode_with_count comment change as it hasn't been ported yet. --- src/nvim/testdir/test_visual.vim | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 099a90643f..2fc7fd0e9c 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -132,11 +132,16 @@ func Test_blockwise_visual_o_O() exe "norm! gvO\rb" exe "norm! gvo\rc" exe "norm! gvO\rd" + set selection=exclusive + exe "norm! gvOo\re" + call assert_equal('...a be.', getline(4)) + exe "norm! gvOO\rf" + set selection& call assert_equal(['..........', \ '...c d..', \ '... ..', - \ '...a b..', + \ '...a bf.', \ '..........'], getline(1, '$')) enew! @@ -658,6 +663,16 @@ func Test_characterwise_select_mode() exe "normal Gkgh\\\" call assert_equal(['', 'a', ''], getline(1, '$')) + " CTRL-H in select mode behaves like 'x' + call setline(1, 'abcdef') + exe "normal! gggh\\\\" + call assert_equal('ef', getline(1)) + + " CTRL-O in select mode switches to visual mode for one command + call setline(1, 'abcdef') + exe "normal! gggh\3lm" + call assert_equal('mef', getline(1)) + sunmap End> sunmap Down> sunmap Del> @@ -757,8 +772,7 @@ endfunc func Test_visual_block_mode() new call append(0, '') - call setline(1, ['abcdefghijklm', 'abcdefghijklm', 'abcdefghijklm', - \ 'abcdefghijklm', 'abcdefghijklm']) + call setline(1, repeat(['abcdefghijklm'], 5)) call cursor(1, 1) " Test shift-right of a block @@ -777,6 +791,16 @@ func Test_visual_block_mode() \ 'axyzqqqqefgmnoklm', \ 'abcdqqqqijklm'], getline(1, 5)) + " Test 'C' to change till the end of the line + call cursor(3, 4) + exe "normal! \j3lCooo" + call assert_equal(['axyooo', 'axyooo'], getline(3, 4)) + + " Test 'D' to delete till the end of the line + call cursor(3, 3) + exe "normal! \j2lD" + call assert_equal(['ax', 'ax'], getline(3, 4)) + " Test from ':help v_b_I_example' %d _ setlocal tabstop=8 shiftwidth=4 -- cgit From b7e6c1b525613bd0ece6fde8c1bcbc52fdc9372f Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Feb 2022 13:20:26 +0000 Subject: test(old): cherry-pick test_visual changes from v8.2.2901 https://github.com/vim/vim/commit/3e72dcad8b752a42b6eaf71213e3f5d534175256 --- src/nvim/testdir/test_visual.vim | 101 ++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 2fc7fd0e9c..2c79ed25f5 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -59,7 +59,7 @@ endfunc " Test for visual block shift and tab characters. func Test_block_shift_tab() - enew! + new call append(0, repeat(['one two three'], 5)) call cursor(1,1) exe "normal i\u" @@ -68,7 +68,7 @@ func Test_block_shift_tab() call assert_equal('on1 two three', getline(2)) call assert_equal('on1 two three', getline(5)) - enew! + %d _ call append(0, repeat(['abcdefghijklmnopqrstuvwxyz'], 5)) call cursor(1,1) exe "normal \4jI \j<<11|D" @@ -93,12 +93,26 @@ func Test_block_shift_tab() call assert_equal(" abc\\defghijklmnopqrstuvwxyz", getline(4)) call assert_equal(" abc\ defghijklmnopqrstuvwxyz", getline(5)) - enew! + " Test for block shift with space characters at the beginning and with + " 'noexpandtab' and 'expandtab' + %d _ + call setline(1, [" 1", " 2", " 3"]) + setlocal shiftwidth=2 noexpandtab + exe "normal gg\3j>" + call assert_equal(["\t1", "\t2", "\t3"], getline(1, '$')) + %d _ + call setline(1, [" 1", " 2", " 3"]) + setlocal shiftwidth=2 expandtab + exe "normal gg\3j>" + call assert_equal([" 1", " 2", " 3"], getline(1, '$')) + setlocal shiftwidth& + + bw! endfunc " Tests Blockwise Visual when there are TABs before the text. func Test_blockwise_visual() - enew! + new call append(0, ['123456', \ '234567', \ '345678', @@ -120,12 +134,12 @@ func Test_blockwise_visual() \ "\t\tsomext", \ "\t\ttesext"], getline(1, 7)) - enew! + bw! endfunc " Test swapping corners in blockwise visual mode with o and O func Test_blockwise_visual_o_O() - enew! + new exe "norm! 10i.\Y4P3lj\4l2jr " exe "norm! gvO\ra" @@ -144,7 +158,7 @@ func Test_blockwise_visual_o_O() \ '...a bf.', \ '..........'], getline(1, '$')) - enew! + bw! endfun " Test Virtual replace mode. @@ -273,7 +287,6 @@ func Test_visual_mode_reset() " thus preventing the problem: exe "normal! GV:call TriggerTheProblem()\" call assert_equal("Everything's fine.", g:msg) - endfunc func Test_Visual_word_textobject() @@ -440,15 +453,13 @@ endfunc " Test for 'p'ut in visual block mode func Test_visual_block_put() - enew - + new call append(0, ['One', 'Two', 'Three']) normal gg yank call feedkeys("jl\ljp", 'xt') call assert_equal(['One', 'T', 'Tee', 'One', ''], getline(1, '$')) - - enew! + bw! endfunc func Test_visual_put_in_block() @@ -628,6 +639,12 @@ func Test_characterwise_visual_mode() normal Gkvj$d call assert_equal(['', 'a', ''], getline(1, '$')) + " characterwise visual mode: replace a single character line and the eol + %d _ + call setline(1, "a") + normal v$rx + call assert_equal(['x'], getline(1, '$')) + bwipe! endfunc @@ -801,6 +818,66 @@ func Test_visual_block_mode() exe "normal! \j2lD" call assert_equal(['ax', 'ax'], getline(3, 4)) + " Test block insert with a short line that ends before the block + %d _ + call setline(1, [" one", "a", " two"]) + exe "normal gg\2jIx" + call assert_equal([" xone", "a", " xtwo"], getline(1, '$')) + + " Test block append at EOL with '$' and without '$' + %d _ + call setline(1, ["one", "a", "two"]) + exe "normal gg$\2jAx" + call assert_equal(["onex", "ax", "twox"], getline(1, '$')) + %d _ + call setline(1, ["one", "a", "two"]) + exe "normal gg3l\2jAx" + call assert_equal(["onex", "a x", "twox"], getline(1, '$')) + + " Test block replace with an empty line in the middle and use $ to jump to + " the end of the line. + %d _ + call setline(1, ['one', '', 'two']) + exe "normal gg$\2jrx" + call assert_equal(["onx", "", "twx"], getline(1, '$')) + + " Test block replace with an empty line in the middle and move cursor to the + " end of the line + %d _ + call setline(1, ['one', '', 'two']) + exe "normal gg2l\2jrx" + call assert_equal(["onx", "", "twx"], getline(1, '$')) + + " Replace odd number of characters with a multibyte character + %d _ + call setline(1, ['abcd', 'efgh']) + exe "normal ggl\2ljr\u1100" + call assert_equal(["a\u1100 ", "e\u1100 "], getline(1, '$')) + + " During visual block append, if the cursor moved outside of the selected + " range, then the edit should not be applied to the block. + %d _ + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal 2G\jAx\" + call assert_equal(['aaa', 'bxbb', 'ccc'], getline(1, '$')) + + " During visual block append, if the cursor is moved before the start of the + " block, then the new text should be appended there. + %d _ + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal $\2jA\x" + " BUG: Instead of adding x as the third character in all the three lines, + " 'a' is added in the second and third lines at the end. This bug is not + " reproducible if this operation is performed manually. + "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) + call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + + " Change a characterwise motion to a blockwise motion using CTRL-V + %d _ + call setline(1, ['123', '456', '789']) + exe "normal ld\j" + call assert_equal(['13', '46', '789'], getline(1, '$')) + " Test from ':help v_b_I_example' %d _ setlocal tabstop=8 shiftwidth=4 -- cgit From d5d51308c0e531f6c2dec6b5570b3ffe04ab6b45 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Feb 2022 13:26:42 +0000 Subject: test(old): cherry-pick test_visual changes from v8.2.2945 https://github.com/vim/vim/commit/59b262362f26b3aaea1eeb0078adc33eed59863e --- src/nvim/testdir/test_visual.vim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 2c79ed25f5..d05035b4e9 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -871,6 +871,10 @@ func Test_visual_block_mode() " reproducible if this operation is performed manually. "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + " Repeat the previous test but use 'l' to move the cursor instead of '$' + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal! gg2l\2jA\x" + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) " Change a characterwise motion to a blockwise motion using CTRL-V %d _ -- cgit From f8b75e582215be5ed07aebb02cdbe69de8cad393 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Feb 2022 01:58:47 +0000 Subject: vim-patch:8.2.3073: when cursor is move for block append wrong text is inserted Problem: When cursor is move for block append wrong text is inserted. Solution: Calculate an offset. (Christian Brabandt, closes vim/vim#8433, closes vim/vim#8288) https://github.com/vim/vim/commit/4067bd3604215b48e4b4201e28f9e401b08418e4 --- src/nvim/ops.c | 56 +++++++++++++++++++++++++------------ src/nvim/testdir/test_blockedit.vim | 48 +++++++++++++++++++++++++++++++ src/nvim/testdir/test_visual.vim | 6 +--- 3 files changed, 87 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index f999b68236..68ce9824c7 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -580,6 +580,9 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def spaces -= off; count -= off; } + if (spaces < 0) { // can happen when the cursor was moved + spaces = 0; + } assert(count >= 0); newp = (char_u *)xmalloc(STRLEN(oldp) + s_len + (size_t)count + 1); @@ -2278,6 +2281,7 @@ void op_insert(oparg_T *oap, long count1) } t1 = oap->start; + const pos_T start_insert = curwin->w_cursor; (void)edit(NUL, false, (linenr_T)count1); // When a tab was inserted, and the characters in front of the tab @@ -2315,26 +2319,30 @@ void op_insert(oparg_T *oap, long count1) if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) { - if (oap->op_type == OP_INSERT - && oap->start.col + oap->start.coladd - != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { - int t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); - oap->start.col = curbuf->b_op_start_orig.col; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - } else if (oap->op_type == OP_APPEND - && oap->end.col + oap->end.coladd - >= curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) { - int t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); - oap->start.col = curbuf->b_op_start_orig.col; + const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd); + + if (!bd.is_MAX) { + if (oap->op_type == OP_INSERT + && oap->start.col + oap->start.coladd + != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + } else if (oap->op_type == OP_APPEND + && oap->end.col + oap->end.coladd + >= curbuf->b_op_start_orig.col + + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; + // reset pre_textlen to the value of OP_INSERT + pre_textlen += bd.textlen; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + oap->op_type = OP_INSERT; + } + } else if (bd.is_MAX && oap->op_type == OP_APPEND) { // reset pre_textlen to the value of OP_INSERT pre_textlen += bd.textlen; pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - oap->op_type = OP_INSERT; } } @@ -2376,15 +2384,27 @@ void op_insert(oparg_T *oap, long count1) firstline = ml_get(oap->start.lnum); const size_t len = STRLEN(firstline); colnr_T add = bd.textcol; + colnr_T offset = 0; // offset when cursor was moved in insert mode if (oap->op_type == OP_APPEND) { add += bd.textlen; + // account for pressing cursor in insert mode when '$' was used + if (bd.is_MAX && start_insert.lnum == Insstart.lnum && start_insert.col > Insstart.col) { + offset = start_insert.col - Insstart.col; + add -= offset; + if (oap->end_vcol > offset) { + oap->end_vcol -= offset + 1; + } else { + // moved outside of the visual block, what to do? + return; + } + } } if ((size_t)add > len) { firstline += len; // short line, point to the NUL } else { firstline += add; } - ins_len = (long)STRLEN(firstline) - pre_textlen; + ins_len = (long)STRLEN(firstline) - pre_textlen - offset; if (pre_textlen >= 0 && ins_len > 0) { ins_text = vim_strnsave(firstline, (size_t)ins_len); // block handled here diff --git a/src/nvim/testdir/test_blockedit.vim b/src/nvim/testdir/test_blockedit.vim index 38978ef689..7b56b1554f 100644 --- a/src/nvim/testdir/test_blockedit.vim +++ b/src/nvim/testdir/test_blockedit.vim @@ -81,4 +81,52 @@ func Test_blockinsert_delete() bwipe! endfunc +func Test_blockappend_eol_cursor() + new + " Test 1 Move 1 char left + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! gg$\2jA\x\" + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) + " Test 2 Move 2 chars left + sil %d + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! gg$\2jA\\x\" + call assert_equal(['axaa', 'bxbb', 'cxcc'], getline(1, '$')) + " Test 3 Move 3 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! ggl$\2jA\\\x\" + call assert_equal(['xaaa', 'bbb', 'ccc'], getline(1, '$')) + bw! +endfunc + +func Test_blockappend_eol_cursor2() + new + " Test 1 Move 1 char left + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\x\" + call assert_equal(['aaaaxa', 'bbbx', 'ccccxc'], getline(1, '$')) + " Test 2 Move 2 chars left + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\\x\" + call assert_equal(['aaaxaa', 'bbbx', 'cccxcc'], getline(1, '$')) + " Test 3 Move 3 chars left (to the beginning of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\\\x\" + call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$')) + " Test 4 Move 3 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! ggl\$2jA\\\x\" + call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$')) + " Test 5 Move 4 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! ggl\$2jA\\\\x\" + call assert_equal(['axaaaa', 'bxbb', 'cxcccc'], getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index d05035b4e9..f95801cab5 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -866,11 +866,7 @@ func Test_visual_block_mode() %d _ call setline(1, ['aaa', 'bbb', 'ccc']) exe "normal $\2jA\x" - " BUG: Instead of adding x as the third character in all the three lines, - " 'a' is added in the second and third lines at the end. This bug is not - " reproducible if this operation is performed manually. - "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) - call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) " Repeat the previous test but use 'l' to move the cursor instead of '$' call setline(1, ['aaa', 'bbb', 'ccc']) exe "normal! gg2l\2jA\x" -- cgit From e662d86e8d3198ea77e1a6d0b9be7c09255de520 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 3 Feb 2022 01:10:33 +0000 Subject: vim-patch:8.2.4120: block insert goes over the end of the line Problem: Block insert goes over the end of the line. Solution: Handle invalid byte better. Fix inserting the wrong text. https://github.com/vim/vim/commit/9f8c304c8a390ade133bac29963dc8e56ab14cbc --- src/nvim/ops.c | 38 ++++++++++++++++++++++++-------------- src/nvim/testdir/test_visual.vim | 9 +++++++++ 2 files changed, 33 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 68ce9824c7..e712aa18b3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -573,19 +573,24 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def // Avoid starting halfway through a multi-byte character. if (b_insert) { off = utf_head_off(oldp, oldp + offset + spaces); + spaces -= off; + count -= off; } else { - off = mb_off_next(oldp, oldp + offset); - offset += off; + // spaces fill the gap, the character that's at the edge moves + // right + off = utf_head_off(oldp, oldp + offset); + offset -= off; } - spaces -= off; - count -= off; } if (spaces < 0) { // can happen when the cursor was moved spaces = 0; } assert(count >= 0); - newp = (char_u *)xmalloc(STRLEN(oldp) + s_len + (size_t)count + 1); + // Make sure the allocated size matches what is actually copied below. + newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len + + (spaces > 0 && !bdp->is_short ? (size_t)p_ts - (size_t)spaces : 0) + + (size_t)count + 1); // copy up to shifted part memmove(newp, oldp, (size_t)offset); @@ -600,14 +605,19 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def offset += (int)s_len; int skipped = 0; - if (spaces && !bdp->is_short) { - // insert post-padding - memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces)); - // We're splitting a TAB, don't copy it. - oldp++; - // We allowed for that TAB, remember this now - count++; - skipped = 1; + if (spaces > 0 && !bdp->is_short) { + if (*oldp == TAB) { + // insert post-padding + memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces)); + // We're splitting a TAB, don't copy it. + oldp++; + // We allowed for that TAB, remember this now + count++; + skipped = 1; + } else { + // Not a TAB, no extra spaces + count = spaces; + } } if (spaces > 0) { @@ -2329,7 +2339,7 @@ void op_insert(oparg_T *oap, long count1) pre_textlen -= t - oap->start_vcol; oap->start_vcol = t; } else if (oap->op_type == OP_APPEND - && oap->end.col + oap->end.coladd + && oap->start.col + oap->start.coladd >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { oap->start.col = curbuf->b_op_start_orig.col; diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index f95801cab5..a2c93bd573 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1242,6 +1242,15 @@ func Test_visual_block_ctrl_w_f() au! BufNew endfunc +func Test_visual_block_append_invalid_char() + " this was going over the end of the line + new + call setline(1, [' let xxx', 'xxxxxˆ', 'xxxxxxxxxxx']) + exe "normal 0\jjA-\" + call assert_equal([' - let xxx', 'xxxxx -ˆ', 'xxxxxxxx-xxx'], getline(1, 3)) + bwipe! +endfunc + func Test_visual_reselect_with_count() " this was causing an illegal memory access let lines =<< trim END -- cgit From afbed8cb75af714a636128c4ad7fb4be6edf7727 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 3 Feb 2022 01:22:24 +0000 Subject: vim-patch:8.2.4151: reading beyond the end of a line Problem: Reading beyond the end of a line. Solution: For block insert only use the offset for correcting the length. https://github.com/vim/vim/commit/57df9e8a9f9ae1aafdde9b86b10ad907627a87dc --- src/nvim/ops.c | 15 ++------------- src/nvim/testdir/test_visual.vim | 9 +++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e712aa18b3..c9a99fef84 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -568,19 +568,8 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def } if (spaces > 0) { - int off; - - // Avoid starting halfway through a multi-byte character. - if (b_insert) { - off = utf_head_off(oldp, oldp + offset + spaces); - spaces -= off; - count -= off; - } else { - // spaces fill the gap, the character that's at the edge moves - // right - off = utf_head_off(oldp, oldp + offset); - offset -= off; - } + // avoid copying part of a multi-byte character + offset -= utf_head_off(oldp, oldp + offset); } if (spaces < 0) { // can happen when the cursor was moved spaces = 0; diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index a2c93bd573..edb72c82e3 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1271,6 +1271,15 @@ func Test_visual_reselect_with_count() call delete('XvisualReselect') endfunc +func Test_visual_block_insert_round_off() + new + " The number of characters are tuned to fill a 4096 byte allocated block, + " so that valgrind reports going over the end. + call setline(1, ['xxxxx', repeat('0', 1350), "\t", repeat('x', 60)]) + exe "normal gg0\GI" .. repeat('0', 1320) .. "\" + bwipe! +endfunc + " this was leaving the end of the Visual area beyond the end of a line func Test_visual_ex_copy_line() new -- cgit From 2b75ac7aa9cfc6f8b49b4399c02bada45af450d4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 13 Feb 2022 19:02:06 +0000 Subject: vim-patch:8.2.4152: block insert with double wide character fails Problem: Block insert with double wide character fails. Solution: Adjust the expected output. https://github.com/vim/vim/commit/fc6ccebea668c49e9e617e0657421b6a8ed9df1e --- src/nvim/testdir/test_utf8.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim index 36776d5a64..9b010a5dbc 100644 --- a/src/nvim/testdir/test_utf8.vim +++ b/src/nvim/testdir/test_utf8.vim @@ -8,7 +8,7 @@ func Test_visual_block_insert() new call setline(1, ["aaa", "ã‚ã‚ã‚", "bbb"]) exe ":norm! gg0l\jjIx\" - call assert_equal(['axaa', 'xã‚ã‚ã‚', 'bxbb'], getline(1, '$')) + call assert_equal(['axaa', ' xã‚ã‚ã‚', 'bxbb'], getline(1, '$')) bwipeout! endfunc -- cgit From 9f9ef3e21ddbf6611348dc9df0d304ee234f477b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Feb 2022 08:59:46 +0800 Subject: test(old): reorder test_functions.vim and test_visual.vim to match Vim --- src/nvim/testdir/test_functions.vim | 322 ++++++++++++++++++------------------ src/nvim/testdir/test_visual.vim | 112 ++++++------- 2 files changed, 217 insertions(+), 217 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 438bed51c6..6e36f4e3d2 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -332,37 +332,6 @@ func Test_simplify() call assert_fails('call simplify(1.2)', 'E806:') endfunc -func Test_setbufvar_options() - " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the - " window layout. - call assert_equal(1, winnr('$')) - split dummy_preview - resize 2 - set winfixheight winfixwidth - let prev_id = win_getid() - - wincmd j - let wh = winheight(0) - let dummy_buf = bufnr('dummy_buf1', v:true) - call setbufvar(dummy_buf, '&buftype', 'nofile') - execute 'belowright vertical split #' . dummy_buf - call assert_equal(wh, winheight(0)) - let dum1_id = win_getid() - - wincmd h - let wh = winheight(0) - let dummy_buf = bufnr('dummy_buf2', v:true) - eval 'nofile'->setbufvar(dummy_buf, '&buftype') - execute 'belowright vertical split #' . dummy_buf - call assert_equal(wh, winheight(0)) - - bwipe! - call win_gotoid(prev_id) - bwipe! - call win_gotoid(dum1_id) - bwipe! -endfunc - func Test_pathshorten() call assert_equal('', pathshorten('')) call assert_equal('foo', pathshorten('foo')) @@ -1293,6 +1262,37 @@ func Test_shellescape() let &shell = save_shell endfunc +func Test_setbufvar_options() + " This tests that aucmd_prepbuf() and aucmd_restbuf() properly restore the + " window layout. + call assert_equal(1, winnr('$')) + split dummy_preview + resize 2 + set winfixheight winfixwidth + let prev_id = win_getid() + + wincmd j + let wh = winheight(0) + let dummy_buf = bufnr('dummy_buf1', v:true) + call setbufvar(dummy_buf, '&buftype', 'nofile') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight(0)) + let dum1_id = win_getid() + + wincmd h + let wh = winheight(0) + let dummy_buf = bufnr('dummy_buf2', v:true) + eval 'nofile'->setbufvar(dummy_buf, '&buftype') + execute 'belowright vertical split #' . dummy_buf + call assert_equal(wh, winheight(0)) + + bwipe! + call win_gotoid(prev_id) + bwipe! + call win_gotoid(dum1_id) + bwipe! +endfunc + func Test_redo_in_nested_functions() nnoremap g. :set opfunc=Operatorg@ function Operator( type, ... ) @@ -1350,98 +1350,6 @@ func Test_trim() call assert_equal("x", trim(chars . "x" . chars)) endfunc -func EditAnotherFile() - let word = expand('') - edit Xfuncrange2 -endfunc - -func Test_func_range_with_edit() - " Define a function that edits another buffer, then call it with a range that - " is invalid in that buffer. - call writefile(['just one line'], 'Xfuncrange2') - new - eval 10->range()->setline(1) - write Xfuncrange1 - call assert_fails('5,8call EditAnotherFile()', 'E16:') - - call delete('Xfuncrange1') - call delete('Xfuncrange2') - bwipe! -endfunc - -func Test_func_exists_on_reload() - call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists') - call assert_equal(0, exists('*ExistingFunction')) - source Xfuncexists - call assert_equal(1, '*ExistingFunction'->exists()) - " Redefining a function when reloading a script is OK. - source Xfuncexists - call assert_equal(1, exists('*ExistingFunction')) - - " But redefining in another script is not OK. - call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2') - call assert_fails('source Xfuncexists2', 'E122:') - - delfunc ExistingFunction - call assert_equal(0, exists('*ExistingFunction')) - call writefile([ - \ 'func ExistingFunction()', 'echo "yes"', 'endfunc', - \ 'func ExistingFunction()', 'echo "no"', 'endfunc', - \ ], 'Xfuncexists') - call assert_fails('source Xfuncexists', 'E122:') - call assert_equal(1, exists('*ExistingFunction')) - - call delete('Xfuncexists2') - call delete('Xfuncexists') - delfunc ExistingFunction -endfunc - -func Test_platform_name() - " The system matches at most only one name. - let names = ['amiga', 'beos', 'bsd', 'hpux', 'linux', 'mac', 'qnx', 'sun', 'vms', 'win32', 'win32unix'] - call assert_inrange(0, 1, len(filter(copy(names), 'has(v:val)'))) - - " Is Unix? - call assert_equal(has('beos'), has('beos') && has('unix')) - call assert_equal(has('bsd'), has('bsd') && has('unix')) - call assert_equal(has('hpux'), has('hpux') && has('unix')) - call assert_equal(has('linux'), has('linux') && has('unix')) - call assert_equal(has('mac'), has('mac') && has('unix')) - call assert_equal(has('qnx'), has('qnx') && has('unix')) - call assert_equal(has('sun'), has('sun') && has('unix')) - call assert_equal(has('win32'), has('win32') && !has('unix')) - call assert_equal(has('win32unix'), has('win32unix') && has('unix')) - - if has('unix') && executable('uname') - let uname = system('uname') - call assert_equal(uname =~? 'BeOS', has('beos')) - " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined - call assert_equal(uname =~? '\%(GNU/k\w\+\)\@ 'hello'} - call assert_equal('hello', F()) - - sandbox let F = {-> "normal ix\"->execute()} - call assert_fails('call F()', 'E48:') - unlet F - - call assert_fails('call Fsandbox()', 'E48:') - delfunc Fsandbox -endfunc - " Test for reg_recording() and reg_executing() func Test_reg_executing_and_recording() let s:reg_stat = '' @@ -1615,47 +1523,96 @@ func Test_libcall_libcallnr() call assert_fails("call libcallnr('Xdoesnotexist_', 'strlen', 'abcd')", 'E364:') endfunc -func Test_bufadd_bufload() - call assert_equal(0, bufexists('someName')) - let buf = bufadd('someName') - call assert_notequal(0, buf) - call assert_equal(1, bufexists('someName')) - call assert_equal(0, getbufvar(buf, '&buflisted')) - call assert_equal(0, bufloaded(buf)) - call bufload(buf) - call assert_equal(1, bufloaded(buf)) - call assert_equal([''], getbufline(buf, 1, '$')) +sandbox function Fsandbox() + normal ix +endfunc - let curbuf = bufnr('') - eval ['some', 'text']->writefile('XotherName') - let buf = 'XotherName'->bufadd() - call assert_notequal(0, buf) - eval 'XotherName'->bufexists()->assert_equal(1) - call assert_equal(0, getbufvar(buf, '&buflisted')) - call assert_equal(0, bufloaded(buf)) - eval buf->bufload() - call assert_equal(1, bufloaded(buf)) - call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) - call assert_equal(curbuf, bufnr('')) +func Test_func_sandbox() + sandbox let F = {-> 'hello'} + call assert_equal('hello', F()) - let buf1 = bufadd('') - let buf2 = bufadd('') - call assert_notequal(0, buf1) - call assert_notequal(0, buf2) - call assert_notequal(buf1, buf2) - call assert_equal(1, bufexists(buf1)) - call assert_equal(1, bufexists(buf2)) - call assert_equal(0, bufloaded(buf1)) - exe 'bwipe ' .. buf1 - call assert_equal(0, bufexists(buf1)) - call assert_equal(1, bufexists(buf2)) - exe 'bwipe ' .. buf2 - call assert_equal(0, bufexists(buf2)) + sandbox let F = {-> "normal ix\"->execute()} + call assert_fails('call F()', 'E48:') + unlet F - bwipe someName - bwipe XotherName - call assert_equal(0, bufexists('someName')) - call delete('XotherName') + call assert_fails('call Fsandbox()', 'E48:') + delfunc Fsandbox +endfunc + +func EditAnotherFile() + let word = expand('') + edit Xfuncrange2 +endfunc + +func Test_func_range_with_edit() + " Define a function that edits another buffer, then call it with a range that + " is invalid in that buffer. + call writefile(['just one line'], 'Xfuncrange2') + new + eval 10->range()->setline(1) + write Xfuncrange1 + call assert_fails('5,8call EditAnotherFile()', 'E16:') + + call delete('Xfuncrange1') + call delete('Xfuncrange2') + bwipe! +endfunc + +func Test_func_exists_on_reload() + call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists') + call assert_equal(0, exists('*ExistingFunction')) + source Xfuncexists + call assert_equal(1, '*ExistingFunction'->exists()) + " Redefining a function when reloading a script is OK. + source Xfuncexists + call assert_equal(1, exists('*ExistingFunction')) + + " But redefining in another script is not OK. + call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2') + call assert_fails('source Xfuncexists2', 'E122:') + + delfunc ExistingFunction + call assert_equal(0, exists('*ExistingFunction')) + call writefile([ + \ 'func ExistingFunction()', 'echo "yes"', 'endfunc', + \ 'func ExistingFunction()', 'echo "no"', 'endfunc', + \ ], 'Xfuncexists') + call assert_fails('source Xfuncexists', 'E122:') + call assert_equal(1, exists('*ExistingFunction')) + + call delete('Xfuncexists2') + call delete('Xfuncexists') + delfunc ExistingFunction +endfunc + +func Test_platform_name() + " The system matches at most only one name. + let names = ['amiga', 'beos', 'bsd', 'hpux', 'linux', 'mac', 'qnx', 'sun', 'vms', 'win32', 'win32unix'] + call assert_inrange(0, 1, len(filter(copy(names), 'has(v:val)'))) + + " Is Unix? + call assert_equal(has('beos'), has('beos') && has('unix')) + call assert_equal(has('bsd'), has('bsd') && has('unix')) + call assert_equal(has('hpux'), has('hpux') && has('unix')) + call assert_equal(has('linux'), has('linux') && has('unix')) + call assert_equal(has('mac'), has('mac') && has('unix')) + call assert_equal(has('qnx'), has('qnx') && has('unix')) + call assert_equal(has('sun'), has('sun') && has('unix')) + call assert_equal(has('win32'), has('win32') && !has('unix')) + call assert_equal(has('win32unix'), has('win32unix') && has('unix')) + + if has('unix') && executable('uname') + let uname = system('uname') + call assert_equal(uname =~? 'BeOS', has('beos')) + " GNU userland on BSD kernels (e.g., GNU/kFreeBSD) don't have BSD defined + call assert_equal(uname =~? '\%(GNU/k\w\+\)\@writefile('XotherName') + let buf = 'XotherName'->bufadd() + call assert_notequal(0, buf) + eval 'XotherName'->bufexists()->assert_equal(1) + call assert_equal(0, getbufvar(buf, '&buflisted')) + call assert_equal(0, bufloaded(buf)) + eval buf->bufload() + call assert_equal(1, bufloaded(buf)) + call assert_equal(['some', 'text'], getbufline(buf, 1, '$')) + call assert_equal(curbuf, bufnr('')) + + let buf1 = bufadd('') + let buf2 = bufadd('') + call assert_notequal(0, buf1) + call assert_notequal(0, buf2) + call assert_notequal(buf1, buf2) + call assert_equal(1, bufexists(buf1)) + call assert_equal(1, bufexists(buf2)) + call assert_equal(0, bufloaded(buf1)) + exe 'bwipe ' .. buf1 + call assert_equal(0, bufexists(buf1)) + call assert_equal(1, bufexists(buf2)) + exe 'bwipe ' .. buf2 + call assert_equal(0, bufexists(buf2)) + + bwipe someName + bwipe XotherName + call assert_equal(0, bufexists('someName')) + call delete('XotherName') +endfunc + " Test for the eval() function func Test_eval() call assert_fails("call eval('5 a')", 'E488:') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index edb72c82e3..04c7db38ef 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -22,6 +22,14 @@ func Test_block_shift_overflow() q! endfunc +func Test_dotregister_paste() + new + exe "norm! ihello world\" + norm! 0ve".p + call assert_equal('hello world world', getline(1)) + q! +endfunc + func Test_Visual_ctrl_o() new call setline(1, ['one', 'two', 'three']) @@ -42,14 +50,6 @@ func Test_Visual_vapo() bwipe! endfunc -func Test_dotregister_paste() - new - exe "norm! ihello world\" - norm! 0ve".p - call assert_equal('hello world world', getline(1)) - q! -endfunc - func Test_Visual_inner_quote() new normal oxX @@ -57,6 +57,34 @@ func Test_Visual_inner_quote() bwipe! endfunc +" Test for Visual mode not being reset causing E315 error. +func TriggerTheProblem() + " At this point there is no visual selection because :call reset it. + " Let's restore the selection: + normal gv + '<,'>del _ + try + exe "normal \" + catch /^Vim\%((\a\+)\)\=:E315/ + echom 'Snap! E315 error!' + let g:msg = 'Snap! E315 error!' + endtry +endfunc + +func Test_visual_mode_reset() + enew + let g:msg = "Everything's fine." + enew + setl buftype=nofile + call append(line('$'), 'Delete this line.') + + " NOTE: this has to be done by a call to a function because executing :del + " the ex-way will require the colon operator which resets the visual mode + " thus preventing the problem: + exe "normal! GV:call TriggerTheProblem()\" + call assert_equal("Everything's fine.", g:msg) +endfunc + " Test for visual block shift and tab characters. func Test_block_shift_tab() new @@ -261,34 +289,6 @@ func Test_virtual_replace2() set bs&vim endfunc -" Test for Visual mode not being reset causing E315 error. -func TriggerTheProblem() - " At this point there is no visual selection because :call reset it. - " Let's restore the selection: - normal gv - '<,'>del _ - try - exe "normal \" - catch /^Vim\%((\a\+)\)\=:E315/ - echom 'Snap! E315 error!' - let g:msg = 'Snap! E315 error!' - endtry -endfunc - -func Test_visual_mode_reset() - enew - let g:msg = "Everything's fine." - enew - setl buftype=nofile - call append(line('$'), 'Delete this line.') - - " NOTE: this has to be done by a call to a function because executing :del - " the ex-way will require the colon operator which resets the visual mode - " thus preventing the problem: - exe "normal! GV:call TriggerTheProblem()\" - call assert_equal("Everything's fine.", g:msg) -endfunc - func Test_Visual_word_textobject() new call setline(1, ['First sentence. Second sentence.']) @@ -367,17 +367,6 @@ func Test_Visual_sentence_textobject() bwipe! endfunc -func Test_curswant_not_changed() - new - call setline(1, ['one', 'two']) - au InsertLeave * call getcurpos() - call feedkeys("gg0\jI123 \j", 'xt') - call assert_equal([0, 2, 1, 0, 1], getcurpos()) - - bwipe! - au! InsertLeave -endfunc - func Test_Visual_paragraph_textobject() new call setline(1, ['First line.', @@ -427,6 +416,17 @@ func Test_Visual_paragraph_textobject() bwipe! endfunc +func Test_curswant_not_changed() + new + call setline(1, ['one', 'two']) + au InsertLeave * call getcurpos() + call feedkeys("gg0\jI123 \j", 'xt') + call assert_equal([0, 2, 1, 0, 1], getcurpos()) + + bwipe! + au! InsertLeave +endfunc + " Tests for "vaBiB", end could be wrong. func Test_Visual_Block() new @@ -462,15 +462,6 @@ func Test_visual_block_put() bw! endfunc -func Test_visual_put_in_block() - new - call setline(1, ['xxxx', 'y∞yy', 'zzzz']) - normal 1G2yl - exe "normal 1G2l\jjlp" - call assert_equal(['xxxx', 'y∞xx', 'zzxx'], getline(1, 3)) - bwipe! -endfunc - " Visual modes (v V CTRL-V) followed by an operator; count; repeating func Test_visual_mode_op() new @@ -1109,6 +1100,15 @@ func Test_block_insert_replace_tabs() bwipe! endfunc +func Test_visual_put_in_block() + new + call setline(1, ['xxxx', 'y∞yy', 'zzzz']) + normal 1G2yl + exe "normal 1G2l\jjlp" + call assert_equal(['xxxx', 'y∞xx', 'zzxx'], getline(1, 3)) + bwipe! +endfunc + func Test_visual_put_in_block_using_zp() new " paste using zP -- cgit From 8f3e56ed3a3b05e19a9c5b9c812596cc87281bf9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Feb 2022 09:35:43 +0800 Subject: vim-patch:8.2.3820: "vrc" does not replace composing characters Problem: "vrc" does not replace composing characters, while "rc" does. Solution: Check the byte length including composing characters. (closes vim/vim#9351) https://github.com/vim/vim/commit/8ee6028de3daa9af9e5f90fa9e583ff407bee04f vim-patch:8.2.3823: test for visual replace is in wrong function Problem: Test for visual replace is in wrong function. Solution: Move it to another function. https://github.com/vim/vim/commit/6ecf58b0d7d9b8fbba780d19d2e6c0f227df715b --- src/nvim/ops.c | 7 +++++-- src/nvim/testdir/test_visual.vim | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c9a99fef84..e012ab0c57 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1961,11 +1961,14 @@ static int op_replace(oparg_T *oap, int c) while (ltoreq(curwin->w_cursor, oap->end)) { n = gchar_cursor(); if (n != NUL) { - if (utf_char2len(c) > 1 || utf_char2len(n) > 1) { + int new_byte_len = utf_char2len(c); + int old_byte_len = utfc_ptr2len(get_cursor_pos_ptr()); + + if (new_byte_len > 1 || old_byte_len > 1) { // This is slow, but it handles replacing a single-byte // with a multi-byte and the other way around. if (curwin->w_cursor.lnum == oap->end.lnum) { - oap->end.col += utf_char2len(c) - utf_char2len(n); + oap->end.col += new_byte_len - old_byte_len; } replace_character(c); } else { diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 04c7db38ef..da88293c8e 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -636,6 +636,11 @@ func Test_characterwise_visual_mode() normal v$rx call assert_equal(['x'], getline(1, '$')) + " replace a character with composing characters + call setline(1, "xã̳x") + normal gg0lvrb + call assert_equal("xbx", getline(1)) + bwipe! endfunc -- cgit From 6eec30ccfc09f0ea45c9ef86a77ca92a5fc4a1b9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Feb 2022 10:23:27 +0800 Subject: vim-patch:8.2.4154: ml_get error when exchanging windows in Visual mode Problem: ml_get error when exchanging windows in Visual mode. Solution: Correct end of Visual area when entering another buffer. https://github.com/vim/vim/commit/05b27615481e72e3b338bb12990fb3e0c2ecc2a9 --- src/nvim/testdir/test_visual.vim | 10 ++++++++++ src/nvim/window.c | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index 04c7db38ef..4650d4e3df 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1280,6 +1280,16 @@ func Test_visual_block_insert_round_off() bwipe! endfunc +" this was causing an ml_get error +func Test_visual_exchange_windows() + enew! + new + call setline(1, ['foo', 'bar']) + exe "normal G\gg\\OO\" + bwipe! + bwipe! +endfunc + " this was leaving the end of the Visual area beyond the end of a line func Test_visual_ex_copy_line() new diff --git a/src/nvim/window.c b/src/nvim/window.c index e09af7a7bb..1e737d2083 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1727,6 +1727,12 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); // recompute window positions + if (wp->w_buffer != curbuf) { + reset_VIsual_and_resel(); + } else if (VIsual_active) { + wp->w_cursor = curwin->w_cursor; + } + win_enter(wp, true); redraw_later(curwin, NOT_VALID); redraw_later(wp, NOT_VALID); -- cgit From f92e74900fda4583001cb85ba7f974394948ce1a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Feb 2022 11:12:09 +0800 Subject: fix(api): nvim_win_set_cursor() redraw for cursorline and statusline --- src/nvim/api/window.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index fc7823a070..9c473ff724 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -71,6 +71,7 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) } /// Sets the (1,0)-indexed cursor position in the window. |api-indexing| +/// Unlike |win_execute()| this scrolls the window. /// /// @param window Window handle, or 0 for current window /// @param pos (row, col) tuple representing the new position @@ -118,6 +119,8 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) update_topline_win(win); redraw_later(win, VALID); + redraw_for_cursorline(win); + win->w_redr_status = true; } /// Gets the window height -- cgit From a2c3d431d6c4e49d4ead8c8b4eda9c24a6333ffb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Feb 2022 20:53:24 +0800 Subject: vim-patch:8.2.4388: dead code in op_insert() Problem: Dead code in op_insert(). Solution: Remove condition and else block. (closes vim/vim#9782) https://github.com/vim/vim/commit/7745f14ef324a7134b2f26a47451cf5032f44b89 --- src/nvim/ops.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e012ab0c57..18facef13c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2318,33 +2318,24 @@ void op_insert(oparg_T *oap, long count1) // The user may have moved the cursor before inserting something, try // to adjust the block for that. But only do it, if the difference // does not come from indent kicking in. - if (oap->start.lnum == curbuf->b_op_start_orig.lnum - && !bd.is_MAX - && !did_indent) { + if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) { const int t = getviscol2(curbuf->b_op_start_orig.col, curbuf->b_op_start_orig.coladd); - if (!bd.is_MAX) { - if (oap->op_type == OP_INSERT - && oap->start.col + oap->start.coladd - != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { - oap->start.col = curbuf->b_op_start_orig.col; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - } else if (oap->op_type == OP_APPEND - && oap->start.col + oap->start.coladd - >= curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) { - oap->start.col = curbuf->b_op_start_orig.col; - // reset pre_textlen to the value of OP_INSERT - pre_textlen += bd.textlen; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - oap->op_type = OP_INSERT; - } - } else if (bd.is_MAX && oap->op_type == OP_APPEND) { + if (oap->op_type == OP_INSERT + && oap->start.col + oap->start.coladd + != curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + } else if (oap->op_type == OP_APPEND + && oap->start.col + oap->start.coladd + >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { + oap->start.col = curbuf->b_op_start_orig.col; // reset pre_textlen to the value of OP_INSERT pre_textlen += bd.textlen; pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + oap->op_type = OP_INSERT; } } -- cgit From 238b944e58d12a28245be996e69bf36a2a452a90 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 15 Feb 2022 13:08:40 -0700 Subject: fix(api): validate command names in nvim_add_user_command (#17406) This uses the same validation used when defining commands with `:command`. --- src/nvim/api/private/helpers.c | 5 +++++ src/nvim/ex_docmd.c | 33 +++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ddcfff0097..2b107a3f27 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1384,6 +1384,11 @@ void add_user_command(String name, Object command, Dict(user_command) *opts, int LuaRef luaref = LUA_NOREF; LuaRef compl_luaref = LUA_NOREF; + if (!uc_validate_name(name.data)) { + api_set_error(err, kErrorTypeValidation, "Invalid command name"); + goto err; + } + if (mb_islower(name.data[0])) { api_set_error(err, kErrorTypeValidation, "'name' must begin with an uppercase letter"); goto err; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4dba0b97ed..87f8865133 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5164,6 +5164,24 @@ char_u *get_command_name(expand_T *xp, int idx) return cmdnames[idx].cmd_name; } +/// Check for a valid user command name +/// +/// If the given {name} is valid, then a pointer to the end of the valid name is returned. +/// Otherwise, returns NULL. +char *uc_validate_name(char *name) +{ + if (ASCII_ISALPHA(*name)) { + while (ASCII_ISALNUM(*name)) { + name++; + } + } + if (!ends_excmd(*name) && !ascii_iswhite(*name)) { + return NULL; + } + + return name; +} + int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, LuaRef luaref, bool force) @@ -5679,23 +5697,18 @@ static void ex_command(exarg_T *eap) // Get the name (if any) and skip to the following argument. name = p; - if (ASCII_ISALPHA(*p)) { - while (ASCII_ISALNUM(*p)) { - p++; - } - } - if (!ends_excmd(*p) && !ascii_iswhite(*p)) { + end = (char_u *)uc_validate_name((char *)name); + if (!end) { emsg(_("E182: Invalid command name")); return; } - end = p; - name_len = (int)(end - name); + name_len = (size_t)(end - name); // If there is nothing after the name, and no attributes were specified, // we are listing commands p = skipwhite(end); if (!has_attr && ends_excmd(*p)) { - uc_list(name, end - name); + uc_list(name, name_len); } else if (!ASCII_ISUPPER(*name)) { emsg(_("E183: User defined commands must start with an uppercase letter")); } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { @@ -5703,7 +5716,7 @@ static void ex_command(exarg_T *eap) } else if (compl > 0 && (argt & EX_EXTRA) == 0) { emsg(_(e_complete_used_without_nargs)); } else { - uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF, + uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, addr_type_arg, LUA_NOREF, eap->forceit); } } -- cgit From d512be55a2ea54dd83914ff25f57c02d703f93b4 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 16 Dec 2021 11:40:23 +0000 Subject: fix(api): re-route nvim_get_runtime_file errors This allows nvim_get_runtime_file to be properly used via pcall --- src/nvim/api/vim.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f4909b0801..0c11ea7e6e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -497,8 +497,12 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Error *err) int flags = DIP_DIRFILE | (all ? DIP_ALL : 0); - do_in_runtimepath((char_u *)(name.size ? name.data : ""), - flags, find_runtime_cb, &rv); + TRY_WRAP({ + try_start(); + do_in_runtimepath((char_u *)(name.size ? name.data : ""), + flags, find_runtime_cb, &rv); + try_end(err); + }); return rv; } -- cgit From f5518b78c843e4b187bd246d31bb7fde2925139e Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Fri, 7 Jan 2022 18:31:17 +0600 Subject: chore: improve lua keymaps internal representation scheme --- src/nvim/getchar.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 5d8a8ddbfe..217477246e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2679,8 +2679,7 @@ void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, mapargs->orig_rhs_len = 0; // stores ref_no in map_str mapargs->rhs_len = (size_t)vim_snprintf(S_LEN(tmp_buf), "%c%c%c%d\r", K_SPECIAL, - (char_u)KEY2TERMCAP0(K_LUA), KEY2TERMCAP1(K_LUA), - rhs_lua); + (char_u)KS_EXTRA, KE_LUA, rhs_lua); mapargs->rhs = vim_strsave((char_u *)tmp_buf); } -- cgit From 07a98b1a75b8c4995ecf8b0e947a1909d91a3d51 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Wed, 16 Feb 2022 12:22:15 +0600 Subject: fix: not shown in :map commands --- src/nvim/getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 217477246e..cc653bb4e9 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3483,7 +3483,7 @@ static void showmap(mapblock_T *mp, bool local) char msg[100]; snprintf(msg, sizeof(msg), "", mp->m_luaref); msg_puts_attr(msg, HL_ATTR(HLF_8)); - } else if (mp->m_str == NULL) { + } else if (mp->m_str[0] == NUL) { msg_puts_attr("", HL_ATTR(HLF_8)); } else { // Remove escaping of K_SPECIAL, because "m_str" is in a format to be used -- cgit From 9a74c2b04ac8f54a17925a437b5a2f03b18f6281 Mon Sep 17 00:00:00 2001 From: Shadman <13149513+shadmansaleh@users.noreply.github.com> Date: Wed, 16 Feb 2022 14:39:50 +0600 Subject: feat(mappings): considering map description when filtering (#17423) --- src/nvim/getchar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index cc653bb4e9..470bf56a6e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3433,8 +3433,8 @@ static void showmap(mapblock_T *mp, bool local) { size_t len = 1; - if (message_filtered(mp->m_keys) - && mp->m_str != NULL && message_filtered(mp->m_str)) { + if (message_filtered(mp->m_keys) && message_filtered(mp->m_str) + && (mp->m_desc == NULL || message_filtered((char_u *)mp->m_desc))) { return; } -- cgit From cc81a8253be032aa12f05730e8a2f1b5d94fd08c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 16 Feb 2022 16:58:32 +0800 Subject: docs: minor changes related to mapping description --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f4909b0801..565015cada 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1584,7 +1584,7 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// @param rhs Right-hand-side |{rhs}| of the mapping. /// @param opts Optional parameters map. Accepts all |:map-arguments| /// as keys excluding || but including |noremap| and "desc". -/// |desc| can be used to give a description to keymap. +/// "desc" can be used to give a description to keymap. /// When called from Lua, also accepts a "callback" key that takes /// a Lua function to call when the mapping is executed. /// Values are Booleans. Unknown key is an error. -- cgit From 758f1e59990ab515c17667f8a4ee2d3fe44af29f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 16 Feb 2022 17:01:35 +0800 Subject: refactor: remove NULL check that is always true --- src/nvim/getchar.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 470bf56a6e..6978823f2b 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3583,8 +3583,7 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) mp = maphash[hash]; } for (; mp; mp = mp->m_next) { - if ((mp->m_mode & mode) - && mp->m_str != NULL && strstr((char *)mp->m_str, rhs) != NULL) { + if ((mp->m_mode & mode) && strstr((char *)mp->m_str, rhs) != NULL) { return true; } } -- cgit From 876aaf2003d1a6eb8f0701cf11e1834751b28980 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 15 Feb 2022 23:40:06 +0000 Subject: fix(highlight): allow globals to be cleared - and reduce heap allocations Fixes #17420 --- src/nvim/highlight.c | 10 ++++------ src/nvim/syntax.c | 26 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index fcd91cdf16..4b80267087 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -904,15 +904,13 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e hlattrs.rgb_bg_color = bg; hlattrs.rgb_fg_color = fg; hlattrs.rgb_sp_color = sp; - hlattrs.cterm_bg_color = - ctermbg == -1 ? cterm_normal_bg_color : ctermbg + 1; - hlattrs.cterm_fg_color = - ctermfg == -1 ? cterm_normal_fg_color : ctermfg + 1; + hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1; + hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1; hlattrs.cterm_ae_attr = cterm_mask; } else { hlattrs.cterm_ae_attr = cterm_mask; - hlattrs.cterm_bg_color = bg == -1 ? cterm_normal_bg_color : bg + 1; - hlattrs.cterm_fg_color = fg == -1 ? cterm_normal_fg_color : fg + 1; + hlattrs.cterm_bg_color = bg == -1 ? 0 : bg + 1; + hlattrs.cterm_fg_color = fg == -1 ? 0 : fg + 1; } return hlattrs; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 119f6e811f..962a48822f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6758,16 +6758,26 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) { NULL, -1, NIL }, }; + char hex_name[8]; + char *name; + for (int j = 0; cattrs[j].dest; j++) { - if (cattrs[j].val != -1) { + if (cattrs[j].val < 0) { + XFREE_CLEAR(*cattrs[j].dest); + continue; + } + + if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + name = cattrs[j].name.data.string.data; + } else { + snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); + name = hex_name; + } + + if (!*cattrs[j].dest + || STRCMP(*cattrs[j].dest, name) != 0) { xfree(*cattrs[j].dest); - if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { - *cattrs[j].dest = xstrdup(cattrs[j].name.data.string.data); - } else { - char hex_name[8]; - snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); - *cattrs[j].dest = xstrdup(hex_name); - } + *cattrs[j].dest = xstrdup(name); } } -- cgit From 9c5228f3e76a3b096c29d95fd2f5b4f45e4cfb7e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 05:58:18 +0800 Subject: vim-patch:8.2.4400: MS-Windows: cannot use the mouse in the console with VIMDLL Problem: MS-Windows: cannot use the mouse in the console with VIMDLL. Solution: use add_char2buf() instead of fix_input_buffer(). (closes vim/vim#9784, closes vim/vim#9769) https://github.com/vim/vim/commit/646bb7247ad6051aca223a2b04b008f682cdb57f N/A patches for version.c: vim-patch:8.2.4392: MS-Windows with VIMDLL: Escaping CSI is wrong Problem: MS-Windows with VIMDLL: Escaping CSI is wrong. Solution: Put back #ifdef. (Ken Takata, closes vim/vim#9769) https://github.com/vim/vim/commit/64d95cfc56406858a05032c6a134f1e08fe2ca78 vim-patch:8.2.4394: UTF8 select mode test fails on MS-Windows Problem: UTF8 select mode test fails on MS-Windows. Solution: Revert the #ifdef change. https://github.com/vim/vim/commit/9fdde7992ab4c21517f447ca3d651b9ff4a770e8 --- src/nvim/getchar.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 6978823f2b..741fc6d803 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1004,20 +1004,9 @@ int ins_char_typebuf(int c, int modifier) buf[len + 2] = (char_u)K_THIRD(c); buf[len + 3] = NUL; } else { - char_u *p = buf + len; - int char_len = utf_char2bytes(c, p); - len += char_len; - // If the character contains K_SPECIAL bytes they need escaping. - for (int i = char_len; --i >= 0; p++) { - if ((uint8_t)(*p) == K_SPECIAL) { - memmove(p + 3, p + 1, (size_t)i); - *p++ = K_SPECIAL; - *p++ = KS_SPECIAL; - *p = KE_FILLER; - len += 2; - } - } - *p = NUL; + char_u *end = add_char2buf(c, buf + len); + *end = NUL; + len = (int)(end - buf); } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); return len; -- cgit From 7b048df4dce802bd0ff5425b09074ae7abbf126d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 06:08:13 +0800 Subject: vim-patch:8.2.4345: is expanded like a file name for DirChangedPre Problem: is expanded like a file name for DirChangedPre. Solution: Do not expand . (closes vim/vim#9742) Also for the User event. https://github.com/vim/vim/commit/f6246f51e3d85a982a899b4a8fd9045a5e23016f --- src/nvim/autocmd.c | 3 ++- src/nvim/testdir/test_autocmd.vim | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 6dc614813b..9117dde089 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1523,7 +1523,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, || event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX || event == EVENT_SIGNAL - || event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) { + || event == EVENT_TABCLOSED || event == EVENT_USER + || event == EVENT_WINCLOSED) { fname = vim_strsave(fname); } else { fname = (char_u *)FullName_save((char *)fname, false); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 82e8f9ba77..0146c06109 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1827,6 +1827,14 @@ func Test_autocommand_all_events() call assert_fails('au * x bwipe', 'E1155:') endfunc +func Test_autocmd_user() + au User MyEvent let s:res = [expand(""), expand("")] + doautocmd User MyEvent + call assert_equal(['MyEvent', 'MyEvent'], s:res) + au! User + unlet s:res +endfunc + function s:Before_test_dirchanged() augroup test_dirchanged autocmd! @@ -1850,11 +1858,11 @@ endfunc function Test_dirchanged_global() call s:Before_test_dirchanged() - autocmd test_dirchanged DirChangedPre global call add(s:li, "pre cd " .. v:event.directory) + autocmd test_dirchanged DirChangedPre global call add(s:li, expand("") .. " pre cd " .. v:event.directory) autocmd test_dirchanged DirChanged global call add(s:li, "cd:") autocmd test_dirchanged DirChanged global call add(s:li, expand("")) call chdir(s:dir_foo) - let expected = ["pre cd " .. s:dir_foo, "cd:", s:dir_foo] + let expected = ["global pre cd " .. s:dir_foo, "cd:", s:dir_foo] call assert_equal(expected, s:li) call chdir(s:dir_foo) call assert_equal(expected, s:li) -- cgit From dc24eeb9febaa331e660e14c3c325fd0977b6b93 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 16 Feb 2022 20:38:40 +0000 Subject: feat(highlight): support color names for cterm --- src/nvim/highlight.c | 31 +++++++++++++++++++++---------- src/nvim/syntax.c | 16 ++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 4b80267087..e8a22ce6a8 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -821,27 +821,27 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, global, , HL_GLOBAL); if (HAS_KEY(dict->fg)) { - fg = object_to_color(dict->fg, "fg", err); + fg = object_to_color(dict->fg, "fg", true, err); } else if (HAS_KEY(dict->foreground)) { - fg = object_to_color(dict->foreground, "foreground", err); + fg = object_to_color(dict->foreground, "foreground", true, err); } if (ERROR_SET(err)) { return hlattrs; } if (HAS_KEY(dict->bg)) { - bg = object_to_color(dict->bg, "bg", err); + bg = object_to_color(dict->bg, "bg", true, err); } else if (HAS_KEY(dict->background)) { - bg = object_to_color(dict->background, "background", err); + bg = object_to_color(dict->background, "background", true, err); } if (ERROR_SET(err)) { return hlattrs; } if (HAS_KEY(dict->sp)) { - sp = object_to_color(dict->sp, "sp", err); + sp = object_to_color(dict->sp, "sp", true, err); } else if (HAS_KEY(dict->special)) { - sp = object_to_color(dict->special, "special", err); + sp = object_to_color(dict->special, "special", true, err); } if (ERROR_SET(err)) { return hlattrs; @@ -882,14 +882,14 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e #undef CHECK_FLAG if (HAS_KEY(dict->ctermfg)) { - ctermfg = object_to_color(dict->ctermfg, "ctermfg", err); + ctermfg = object_to_color(dict->ctermfg, "ctermfg", false, err); if (ERROR_SET(err)) { return hlattrs; } } if (HAS_KEY(dict->ctermbg)) { - ctermbg = object_to_color(dict->ctermbg, "ctermbg", err); + ctermbg = object_to_color(dict->ctermbg, "ctermbg", false, err); if (ERROR_SET(err)) { return hlattrs; } @@ -916,14 +916,25 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e return hlattrs; } -int object_to_color(Object val, char *key, Error *err) +int object_to_color(Object val, char *key, bool rgb, Error *err) { if (val.type == kObjectTypeInteger) { return (int)val.data.integer; } else if (val.type == kObjectTypeString) { String str = val.data.string; // TODO(bfredl): be more fancy with "bg", "fg" etc - return str.size ? name_to_color(str.data) : 0; + int color; + if (!str.size) { + color = 0; + } else if (rgb) { + color = name_to_color(str.data); + } else { + color = name_to_ctermcolor(str.data); + } + if (color < 0) { + api_set_error(err, kErrorTypeValidation, "'%s' is not a valid color", str.data); + } + return color; } else { api_set_error(err, kErrorTypeValidation, "'%s' must be string or integer", key); return 0; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 962a48822f..2b74c45478 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -8859,6 +8859,22 @@ RgbValue name_to_color(const char *name) return -1; } +int name_to_ctermcolor(const char *name) +{ + int i; + int off = TOUPPER_ASC(*name); + for (i = ARRAY_SIZE(color_names); --i >= 0;) { + if (off == color_names[i][0] + && STRICMP(name+1, color_names[i]+1) == 0) { + break; + } + } + if (i < 0) { + return -1; + } + TriState bold = kNone; + return lookup_color(i, false, &bold); +} /************************************** * End of Highlighting stuff * -- cgit From c90cf8c77b96cda0fd40dbf4aa2c1762cd09dab3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 07:18:10 +0800 Subject: vim-patch:8.1.2336: when an expr mapping moves the cursor it is not restored Problem: When an expr mapping moves the cursor it is not restored. Solution: Position the cursor after an expr mapping. (closes vim/vim#5256) https://github.com/vim/vim/commit/4ebe0e62d097d68c5312f9c32714fb41a4c947a3 --- src/nvim/getchar.c | 10 +++++++++- src/nvim/testdir/test_mapping.vim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 741fc6d803..ac5d587c1e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1949,8 +1949,10 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // expression. Also save and restore the command line // for "normal :". if (mp->m_expr) { - int save_vgetc_busy = vgetc_busy; + const int save_vgetc_busy = vgetc_busy; const bool save_may_garbage_collect = may_garbage_collect; + const int save_cursor_row = ui_current_row(); + const int save_cursor_col = ui_current_col(); vgetc_busy = 0; may_garbage_collect = false; @@ -1960,6 +1962,12 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) save_m_str = vim_strsave(mp->m_str); } map_str = eval_map_expr(mp, NUL); + + // The mapping may do anything, but we expect it to take care of + // redrawing. Do put the cursor back where it was. + ui_cursor_goto(save_cursor_row, save_cursor_col); + ui_flush(); + vgetc_busy = save_vgetc_busy; may_garbage_collect = save_may_garbage_collect; } else { diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index f88e8cf843..749c75106e 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -1,6 +1,8 @@ " Tests for mappings and abbreviations source shared.vim +source check.vim +source screendump.vim func Test_abbreviation() " abbreviation with 0x80 should work @@ -451,6 +453,36 @@ func Test_expr_map_gets_cursor() nunmap ! endfunc +func Test_expr_map_restore_cursor() + CheckScreendump + + let lines =<< trim END + call setline(1, ['one', 'two', 'three']) + 2 + set ls=2 + hi! link StatusLine ErrorMsg + noremap Func() + func Func() + let g:on = !get(g:, 'on', 0) + redraws + return '' + endfunc + func Status() + return get(g:, 'on', 0) ? '[on]' : '' + endfunc + set stl=%{Status()} + END + call writefile(lines, 'XtestExprMap') + let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10}) + call term_wait(buf) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_map_expr_1', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestExprMap') +endfunc + " Test for mapping errors func Test_map_error() call assert_fails('unmap', 'E474:') -- cgit From 2ffe66a5a4e1297ee24fd3ab8cc4dda45b1381bd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 07:18:10 +0800 Subject: vim-patch:8.2.4338: an error from an expression mapping messes up the display Problem: An error from an expression mapping messes up the display. Solution: When the expression results in an empty string return K_IGNORE. In cmdline mode redraw the command line. (closes vim/vim#9726) https://github.com/vim/vim/commit/74a0a5b26d0180f3ea89e9495dff6a26f0df23cb --- src/nvim/getchar.c | 21 +++++++++++++++++++++ src/nvim/testdir/test_mapping.vim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index ac5d587c1e..43a4f31478 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1953,6 +1953,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) const bool save_may_garbage_collect = may_garbage_collect; const int save_cursor_row = ui_current_row(); const int save_cursor_col = ui_current_col(); + const int prev_did_emsg = did_emsg; vgetc_busy = 0; may_garbage_collect = false; @@ -1968,6 +1969,26 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) ui_cursor_goto(save_cursor_row, save_cursor_col); ui_flush(); + // If an error was displayed and the expression returns an empty + // string, generate a to allow for a redraw. + if (prev_did_emsg != did_emsg && (map_str == NULL || *map_str == NUL)) { + char_u buf[4]; + xfree(map_str); + buf[0] = K_SPECIAL; + buf[1] = KS_EXTRA; + buf[2] = KE_IGNORE; + buf[3] = NUL; + map_str = vim_strsave(buf); + if (State & CMDLINE) { + // redraw the command below the error + msg_didout = true; + if (msg_row < cmdline_row) { + msg_row = cmdline_row; + } + redrawcmd(); + } + } + vgetc_busy = save_vgetc_busy; may_garbage_collect = save_may_garbage_collect; } else { diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 749c75106e..105f70e6bb 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -483,6 +483,38 @@ func Test_expr_map_restore_cursor() call delete('XtestExprMap') endfunc +func Test_expr_map_error() + CheckScreendump + + let lines =<< trim END + func Func() + throw 'test' + return '' + endfunc + + nnoremap Func() + cnoremap Func() + + call test_override('ui_delay', 10) + END + call writefile(lines, 'XtestExprMap') + let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10}) + call TermWait(buf) + call term_sendkeys(buf, "\") + call TermWait(buf) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_map_expr_2', {}) + + call term_sendkeys(buf, ":abc\") + call VerifyScreenDump(buf, 'Test_map_expr_3', {}) + call term_sendkeys(buf, "\0") + call VerifyScreenDump(buf, 'Test_map_expr_4', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestExprMap') +endfunc + " Test for mapping errors func Test_map_error() call assert_fails('unmap', 'E474:') -- cgit From a92046e43ff22a04d9ca0d861937463f6e262d38 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 07:18:10 +0800 Subject: vim-patch:8.2.4401: map listing does not clear the rest of the command line Problem: Map listing does not clear the rest of the command line. Solution: Call msg_clear_eos(). (closes vim/vim#5623, closes vim/vim#5962) https://github.com/vim/vim/commit/d288eaad846f0e07e0141226f97d858dcf96cb78 --- src/nvim/getchar.c | 1 + src/nvim/testdir/test_mapping.vim | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 43a4f31478..77e42d7471 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3519,6 +3519,7 @@ static void showmap(mapblock_T *mp, bool local) if (p_verbose > 0) { last_set_msg(mp->m_script_ctx); } + msg_clr_eos(); ui_flush(); // show one line at a time } diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 105f70e6bb..1080a3c85b 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -474,7 +474,6 @@ func Test_expr_map_restore_cursor() END call writefile(lines, 'XtestExprMap') let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10}) - call term_wait(buf) call term_sendkeys(buf, "\") call VerifyScreenDump(buf, 'Test_map_expr_1', {}) @@ -483,6 +482,22 @@ func Test_expr_map_restore_cursor() call delete('XtestExprMap') endfunc +func Test_map_listing() + CheckScreendump + + let lines =<< trim END + nmap a b + END + call writefile(lines, 'XtestMapList') + let buf = RunVimInTerminal('-S XtestMapList', #{rows: 6}) + call term_sendkeys(buf, ": nmap a\") + call VerifyScreenDump(buf, 'Test_map_list_1', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XtestMapList') +endfunc + func Test_expr_map_error() CheckScreendump @@ -499,7 +514,6 @@ func Test_expr_map_error() END call writefile(lines, 'XtestExprMap') let buf = RunVimInTerminal('-S XtestExprMap', #{rows: 10}) - call TermWait(buf) call term_sendkeys(buf, "\") call TermWait(buf) call term_sendkeys(buf, "\") -- cgit From ac5856b3f515a39f5047f47d9c675a6206ce19bf Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 15 Feb 2022 18:04:22 +0000 Subject: vim-patch:8.2.3492: crash when pasting too many times Problem: Crash when pasting too many times. Solution: Limit the size to what fits in an int. (closes vim/vim#8962) https://github.com/vim/vim/commit/eeed1c7ae090c17f4df51cf97b2a9e4d8b4f4dc7 Note that this overflow check pretty bad. It also doesn't work well on Windows (where sizeof(int) == sizeof(long)). This is all temporary; everything here is rewritten in future patches anyway. e_resulting_text_too_long was already cherry-picked. totlen is size_t in Nvim, but is int in Vim. This means we'll need some casts. We could technically adjust the logic in do_put to use the entire range of size_t in stuff like totlen, but there's not much gain, and it's much easier to just port the patch like Vim as was done before (also allows us to use the same tests). --- src/nvim/ops.c | 9 +++++++-- src/nvim/testdir/test_put.vim | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 18facef13c..e6e617a419 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3431,8 +3431,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } do { - totlen = (size_t)(count * yanklen); - if (totlen > 0) { + const long multlen = count * yanklen; + + totlen = (size_t)(int)multlen; + if (totlen != (size_t)multlen) { + emsg(_(e_resulting_text_too_long)); + break; + } else if (totlen > 0) { oldp = ml_get(lnum); if (lnum > start_lnum) { pos_T pos = { diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index ed76709a56..cef2cf0dd7 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -138,6 +138,13 @@ func Test_p_with_count_leaves_mark_at_end() bwipe! endfunc +func Test_very_larg_count() + new + let @" = 'x' + call assert_fails('norm 44444444444444p', 'E1240:') + bwipe! +endfunc + func Test_put_above_first_line() new let @" = 'text' -- cgit From 3fba994de890625eeebf7693a13d4e38cfbea0c7 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:21:05 +0000 Subject: vim-patch:8.2.3493: large count test fails on MS-Windows Problem: Large count test fails on MS-Windows. Solution: Skip the test on MS-Windows. https://github.com/vim/vim/commit/cddd5ac911707034ca27f10037c4b1b523188c47 --- src/nvim/testdir/test_put.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index cef2cf0dd7..13c00b3b71 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -138,7 +138,10 @@ func Test_p_with_count_leaves_mark_at_end() bwipe! endfunc -func Test_very_larg_count() +func Test_very_large_count() + " FIXME: should actually check if sizeof(int) == sizeof(long) + CheckNotMSWindows + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From a256b710a26db5c08447eee3e602b86c13c78b06 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:38:27 +0000 Subject: vim-patch:8.2.3570: Test_very_large_count fails on 32bit systems Problem: Test_very_large_count fails on 32bit systems. Solution: Bail out when using 32 bit numbers. (closes vim/vim#9072) https://github.com/vim/vim/commit/ec6e63079dde24a1d74b4103775e74d00f9215ec --- src/nvim/testdir/test_put.vim | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 13c00b3b71..a159687a76 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -142,6 +142,10 @@ func Test_very_large_count() " FIXME: should actually check if sizeof(int) == sizeof(long) CheckNotMSWindows + if v:numbersize != 64 + throw 'Skipped: only works with 64 bit numbers' + endif + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From b149665689f84ee7297ab5ce8a8eb59b12611af1 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 21:23:08 +0000 Subject: vim-patch:8.2.3573: cannot decide whether to skip test that fails with 64 bit Problem: Cannot decide whether to skip test that fails with 64 bit ints. (closes vim/vim#9072) Solution: Add v:sizeofint, v:sizeoflong and v:sizeofpointer. Improve the check for multiply overflow. https://github.com/vim/vim/commit/69b3072d984480935ec412b32b97fea974d2b689 Omit v:sizeof{int,long,pointer} as they're only really used for tests. --- src/nvim/ops.c | 3 ++- src/nvim/testdir/test_put.vim | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e6e617a419..2218b079b0 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3434,7 +3434,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) const long multlen = count * yanklen; totlen = (size_t)(int)multlen; - if (totlen != (size_t)multlen) { + if (totlen != (size_t)multlen || (long)totlen / count != yanklen + || (long)totlen / yanklen != count) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index a159687a76..bf222477a2 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,13 +139,6 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() - " FIXME: should actually check if sizeof(int) == sizeof(long) - CheckNotMSWindows - - if v:numbersize != 64 - throw 'Skipped: only works with 64 bit numbers' - endif - new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From de8e2c61c1b273f57a5a2d85c474b4f12b1b8994 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:19:06 +0000 Subject: vim-patch:8.2.3574: divide by zero Problem: Divide by zero. Solution: Don't check for overflow if multiplicand is zero. https://github.com/vim/vim/commit/8a1962d1355096af55e84b1ea2f0baf5f1c5a5bc --- src/nvim/ops.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 2218b079b0..8dc367d572 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3434,8 +3434,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) const long multlen = count * yanklen; totlen = (size_t)(int)multlen; - if (totlen != (size_t)multlen || (long)totlen / count != yanklen - || (long)totlen / yanklen != count) { + if (count != 0 && yanklen != 0 + && (totlen != (size_t)multlen || (long)totlen / count != yanklen + || (long)totlen / yanklen != count)) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { -- cgit From 6890f8774bf6ec56b645c9b6389cdd1e4ed823ed Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:23:31 +0000 Subject: vim-patch:8.2.3575: overflow check still fails when sizeof(int) == sizeof(long) Problem: Overflow check still fails when sizeof(int) == sizeof(long). Solution: Use a float to check the result. https://github.com/vim/vim/commit/e551ccfb9311eea5252d1c3106ff7a53c762d994 This approach is... interesting... Tests fail. --- src/nvim/ops.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 8dc367d572..ea480c0b31 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3431,12 +3431,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } do { - const long multlen = count * yanklen; + const double multlen = (double)count * (double)yanklen; - totlen = (size_t)(int)multlen; - if (count != 0 && yanklen != 0 - && (totlen != (size_t)multlen || (long)totlen / count != yanklen - || (long)totlen / yanklen != count)) { + totlen = (size_t)(int)(count * yanklen); + if ((double)totlen != multlen) { emsg(_(e_resulting_text_too_long)); break; } else if (totlen > 0) { -- cgit From 308c1952aa55d63a1643dea798f5143eb41e8ed4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:31:40 +0000 Subject: vim-patch:8.2.3577: overflow check fails with 32 ints Problem: Overflow check fails with 32 ints. Solution: Only test with 64 bit ints. https://github.com/vim/vim/commit/0f0044125c2a5dcde2c4605efc39d2e237eed024 --- src/nvim/testdir/test_put.vim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index bf222477a2..a71224785a 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,6 +139,12 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() + throw 'Skipped: v:sizeofint is N/A' + + if v:sizeofint != 8 + throw 'Skipped: only works with 64 bit ints' + endif + new let @" = 'x' call assert_fails('norm 44444444444444p', 'E1240:') -- cgit From 8170260bb35f3761d2008405289832b2620abc53 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 17 Feb 2022 01:43:18 +0000 Subject: fix(ops): str_to_reg passing NULL to memcpy Required for the tests introduced in v8.2.3601 to pass ASAN when running test_alot.vim. Co-authored-by: erw7 --- src/nvim/ops.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index ea480c0b31..95573799e5 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -5696,7 +5696,9 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str // When appending, copy the previous line and free it after. size_t extra = append ? STRLEN(pp[--lnum]) : 0; char_u *s = xmallocz(line_len + extra); - memcpy(s, pp[lnum], extra); + if (extra > 0) { + memcpy(s, pp[lnum], extra); + } memcpy(s + extra, start, line_len); size_t s_len = extra + line_len; -- cgit From 41d0e7af2097e0374ab16fb1567cf22d21aad180 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 16 Feb 2022 22:37:10 +0000 Subject: vim-patch:8.2.3601: check for overflow in put count does not work well Problem: Check for overflow in put count does not work well. Solution: Improve the overflow check. (Ozaki Kiichi, closes vim/vim#9102) https://github.com/vim/vim/commit/fa53722367c3793fda95dac665af74b8651065e9 Add some casts as Nvim uses size_t variables in some places. We could technically adjust the logic to check for overflow outside of size_t's range, but it's much easier to just port the patch exactly (also means we can use the same tests). v:sizeoflong is N/A, so convert the 64-bit tests to Lua and use the FFI to check long's size. --- src/nvim/ops.c | 51 ++++++++++++++++++++++++++++--------------- src/nvim/testdir/test_put.vim | 37 ++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 95573799e5..b5c7020dee 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3314,18 +3314,28 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } - // insert the new text + // Insert the new text. + // First check for multiplication overflow. + if (yanklen + spaces != 0 + && count > ((INT_MAX - (bd.startspaces + bd.endspaces)) / (yanklen + spaces))) { + emsg(_(e_resulting_text_too_long)); + break; + } + totlen = (size_t)(count * (yanklen + spaces) + bd.startspaces + bd.endspaces); int addcount = (int)totlen + lines_appended; newp = (char_u *)xmalloc(totlen + oldlen + 1); + // copy part up to cursor to new line ptr = newp; memmove(ptr, oldp, (size_t)bd.textcol); ptr += bd.textcol; + // may insert some spaces before the new text memset(ptr, ' ', (size_t)bd.startspaces); ptr += bd.startspaces; + // insert the new text for (long j = 0; j < count; j++) { memmove(ptr, y_array[i], (size_t)yanklen); @@ -3339,9 +3349,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) addcount -= spaces; } } + // may insert some spaces after the new text memset(ptr, ' ', (size_t)bd.endspaces); ptr += bd.endspaces; + // move the text after the cursor to the end of the line. int columns = (int)oldlen - bd.textcol - delcount + 1; assert(columns >= 0); @@ -3430,15 +3442,18 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } - do { - const double multlen = (double)count * (double)yanklen; - - totlen = (size_t)(int)(count * yanklen); - if ((double)totlen != multlen) { - emsg(_(e_resulting_text_too_long)); - break; - } else if (totlen > 0) { + if (count == 0 || yanklen == 0) { + if (VIsual_active) { + lnum = end_lnum; + } + } else if (count > INT_MAX / yanklen) { + // multiplication overflow + emsg(_(e_resulting_text_too_long)); + } else { + totlen = (size_t)(count * yanklen); + do { oldp = ml_get(lnum); + oldlen = STRLEN(oldp); if (lnum > start_lnum) { pos_T pos = { .lnum = lnum, @@ -3449,11 +3464,11 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) col = MAXCOL; } } - if (VIsual_active && col > (int)STRLEN(oldp)) { + if (VIsual_active && col > (colnr_T)oldlen) { lnum++; continue; } - newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1)); + newp = (char_u *)xmalloc(totlen + oldlen + 1); memmove(newp, oldp, (size_t)col); ptr = newp + col; for (i = 0; i < (size_t)count; i++) { @@ -3475,14 +3490,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) changed_bytes(lnum, col); extmark_splice_cols(curbuf, (int)lnum-1, col, 0, (int)totlen, kExtmarkUndo); - } - if (VIsual_active) { - lnum++; - } - } while (VIsual_active && lnum <= end_lnum); + if (VIsual_active) { + lnum++; + } + } while (VIsual_active && lnum <= end_lnum); - if (VIsual_active) { // reset lnum to the last visual line - lnum--; + if (VIsual_active) { // reset lnum to the last visual line + lnum--; + } } // put '] at the first byte of the last character diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index a71224785a..9f2fc999a7 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -139,10 +139,18 @@ func Test_p_with_count_leaves_mark_at_end() endfunc func Test_very_large_count() - throw 'Skipped: v:sizeofint is N/A' + new + " total put-length (21474837 * 100) brings 32 bit int overflow + let @" = repeat('x', 100) + call assert_fails('norm 21474837p', 'E1240:') + bwipe! +endfunc + +func Test_very_large_count_64bit() + throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead - if v:sizeofint != 8 - throw 'Skipped: only works with 64 bit ints' + if v:sizeoflong < 8 + throw 'Skipped: only works with 64 bit long ints' endif new @@ -151,6 +159,29 @@ func Test_very_large_count() bwipe! endfunc +func Test_very_large_count_block() + new + " total put-length (21474837 * 100) brings 32 bit int overflow + call setline(1, repeat('x', 100)) + exe "norm \99ly" + call assert_fails('norm 21474837p', 'E1240:') + bwipe! +endfunc + +func Test_very_large_count_block_64bit() + throw 'Skipped: v:sizeoflong is N/A' " use legacy/put_spec.lua instead + + if v:sizeoflong < 8 + throw 'Skipped: only works with 64 bit long ints' + endif + + new + call setline(1, 'x') + exe "norm \y" + call assert_fails('norm 44444444444444p', 'E1240:') + bwipe! +endfunc + func Test_put_above_first_line() new let @" = 'text' -- cgit From e35a2d86fc2a8ed19aef00af9f35991385e833f1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 17 Feb 2022 14:56:03 +0100 Subject: fix(api): allow empty list for cterm in nvim_set_hl Problem: when accessing `nvim_set_hl` from Lua, empty tables are converted to empty lists, not dictionaries, resulting in an error for :lua vim.api.nvim_set_hl(0, "Comment", { cterm = {} }) Workaround: add an empty array as a special case when checking `dict->cterm.type` and just set `cterm_mask_provided`. (Proper solution: handle this in `gen_api_dispatch.lua`.) --- src/nvim/highlight.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index e8a22ce6a8..8b998ff62e 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -876,6 +876,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH); CHECK_FLAG(cterm, cterm_mask, nocombine, , HL_NOCOMBINE); + } else if (dict->cterm.type == kObjectTypeArray && dict->cterm.data.array.size == 0) { + // empty list from Lua API should clear all cterm attributes + // TODO(clason): handle via gen_api_dispatch + cterm_mask_provided = true; } else if (HAS_KEY(dict->cterm)) { api_set_error(err, kErrorTypeValidation, "'cterm' must be a Dictionary."); } -- cgit From 1fd106ca88a606241e1e1fb8c73645dcea5ea5c8 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 17 Feb 2022 23:05:48 +0100 Subject: vim-patch:8.2.4411: bicep files are not recognized (#17447) Problem: Bicep files are not recognized. Solution: Match *.bicep files. (Dundar Goc, closes vim/vim#9791) https://github.com/vim/vim/commit/8e5ba693ad9377fbf4b047093624248b81eac854 --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index b663032c24..dd14aa3e6f 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -81,6 +81,7 @@ let s:filename_checks = { \ 'bc': ['file.bc'], \ 'bdf': ['file.bdf'], \ 'bib': ['file.bib'], + \ 'bicep': ['file.bicep'], \ 'beancount': ['file.beancount'], \ 'bindzone': ['named.root', '/bind/db.file', '/named/db.file', 'any/bind/db.file', 'any/named/db.file'], \ 'blank': ['file.bl'], -- cgit From 3ed800e998e22a975e17ee54e675410148850c75 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Feb 2022 08:29:55 +0800 Subject: vim-patch:8.2.3659: integer overflow with large line number Problem: Integer overflow with large line number. Solution: Check for overflow. (closes vim/vim#9202) https://github.com/vim/vim/commit/03725c5795ae5b8c14da4a39cd0ce723c6dd4304 Put E1247 in globals.h as E1240 is also there. Do not make getdigits() abort. --- src/nvim/ex_docmd.c | 10 +++++++++- src/nvim/globals.h | 2 ++ src/nvim/normal.c | 17 +++++++---------- src/nvim/testdir/test_excmd.vim | 14 ++++++++++++++ src/nvim/testdir/test_normal.vim | 21 +++++++++++++++++++++ 5 files changed, 53 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9991584862..e8b8dc799c 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -4141,7 +4141,11 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1' n = 1; } else { - n = getdigits(&cmd, true, 0); + n = getdigits(&cmd, false, MAXLNUM); + if (n == MAXLNUM) { + emsg(_(e_line_number_out_of_range)); + goto error; + } } if (addr_type == ADDR_TABS_RELATIVE) { @@ -4160,6 +4164,10 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in if (i == '-') { lnum -= n; } else { + if (n >= LONG_MAX - lnum) { + emsg(_(e_line_number_out_of_range)); + goto error; + } lnum += n; } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 98a38c5fe2..5aa564623f 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1002,6 +1002,8 @@ EXTERN char e_cannot_define_autocommands_for_all_events[] INIT(= N_("E1155: Cann EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too long")); +EXTERN char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range")); + EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 21c465434a..a50f85dc7a 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -824,15 +824,12 @@ static bool normal_get_command_count(NormalState *s) if (s->c == K_DEL || s->c == K_KDEL) { s->ca.count0 /= 10; del_from_showcmd(4); // delete the digit and ~@% + } else if (s->ca.count0 >= 999999999L) { + s->ca.count0 = 999999999L; } else { s->ca.count0 = s->ca.count0 * 10 + (s->c - '0'); } - if (s->ca.count0 < 0) { - // overflow - s->ca.count0 = 999999999L; - } - // Set v:count here, when called from main() and not a stuffed // command, so that v:count can be used in an expression mapping // right after the count. Do set it for redo. @@ -1046,14 +1043,14 @@ static int normal_execute(VimState *state, int key) // If you give a count before AND after the operator, they are // multiplied. if (s->ca.count0) { - s->ca.count0 = (long)((uint64_t)s->ca.count0 * (uint64_t)s->ca.opcount); + if (s->ca.opcount >= 999999999L / s->ca.count0) { + s->ca.count0 = 999999999L; + } else { + s->ca.count0 *= s->ca.opcount; + } } else { s->ca.count0 = s->ca.opcount; } - if (s->ca.count0 < 0) { - // overflow - s->ca.count0 = 999999999L; - } } // Always remember the count. It will be set to zero (on the next call, diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index bbf8b4dfc8..8055a51a11 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -409,4 +409,18 @@ func Test_not_break_expression_register() call assert_equal('1+1', getreg('=', 1)) endfunc +func Test_address_line_overflow() + throw 'Skipped: v:sizeoflong is N/A' " use legacy/excmd_spec.lua instead + + if v:sizeoflong < 8 + throw 'Skipped: only works with 64 bit long ints' + endif + new + call setline(1, 'text') + call assert_fails('|.44444444444444444444444', 'E1247:') + call assert_fails('|.9223372036854775806', 'E1247:') + bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 5b7cf6fee5..e8eebb3fdd 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2779,4 +2779,25 @@ func Test_normal_gj_on_extra_wide_char() bw! endfunc +func Test_normal_count_out_of_range() + new + call setline(1, 'text') + normal 44444444444| + call assert_equal(999999999, v:count) + normal 444444444444| + call assert_equal(999999999, v:count) + normal 4444444444444| + call assert_equal(999999999, v:count) + normal 4444444444444444444| + call assert_equal(999999999, v:count) + + normal 9y99999999| + call assert_equal(899999991, v:count) + normal 10y99999999| + call assert_equal(999999999, v:count) + normal 44444444444y44444444444| + call assert_equal(999999999, v:count) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 330b3da51e60fbf26e25dc57b8c463ace6f2a933 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Feb 2022 08:48:29 +0800 Subject: vim-patch:8.2.3660: overflow check uses wrong number Problem: Overflow check uses wrong number. Solution: Divide by ten. https://github.com/vim/vim/commit/9b0e82f35ed4e98414333e71b71ca56219683d16 --- src/nvim/normal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a50f85dc7a..7fe6469527 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -824,7 +824,7 @@ static bool normal_get_command_count(NormalState *s) if (s->c == K_DEL || s->c == K_KDEL) { s->ca.count0 /= 10; del_from_showcmd(4); // delete the digit and ~@% - } else if (s->ca.count0 >= 999999999L) { + } else if (s->ca.count0 > 99999999L) { s->ca.count0 = 999999999L; } else { s->ca.count0 = s->ca.count0 * 10 + (s->c - '0'); -- cgit From 62a1290758a3cd6af95dc47a3bbdc7dcf290d531 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Feb 2022 08:55:00 +0800 Subject: vim-patch:8.2.3661: test for put with large count fails Problem: Test for put with large count fails. Solution: Adjust the counts in the test. https://github.com/vim/vim/commit/8bc07e800c2af36686aadd4178cc2671f5c454d4 --- src/nvim/testdir/test_put.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim index 9f2fc999a7..65232175c6 100644 --- a/src/nvim/testdir/test_put.vim +++ b/src/nvim/testdir/test_put.vim @@ -154,8 +154,8 @@ func Test_very_large_count_64bit() endif new - let @" = 'x' - call assert_fails('norm 44444444444444p', 'E1240:') + let @" = repeat('x', 100) + call assert_fails('norm 999999999p', 'E1240:') bwipe! endfunc @@ -176,9 +176,9 @@ func Test_very_large_count_block_64bit() endif new - call setline(1, 'x') - exe "norm \y" - call assert_fails('norm 44444444444444p', 'E1240:') + call setline(1, repeat('x', 100)) + exe "norm \$y" + call assert_fails('norm 999999999p', 'E1240:') bwipe! endfunc -- cgit From adad10284d8e0e7d15189181a061b6b665663467 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 26 Jan 2022 10:23:59 +0000 Subject: refactor(lua): call loadfile internally .. instead of luaL_loadfile allows files to be cached --- src/nvim/lua/executor.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index cbfb8364f6..029e7eb660 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1241,6 +1241,9 @@ void ex_luafile(exarg_T *const eap) /// execute lua code from a file. /// +/// Note: we call the lua global loadfile as opposed to calling luaL_loadfile +/// in case loadfile has been overridden in the users environment. +/// /// @param path path of the file /// /// @return true if everything ok, false if there was an error (echoed) @@ -1249,11 +1252,30 @@ bool nlua_exec_file(const char *path) { lua_State *const lstate = global_lstate; - if (luaL_loadfile(lstate, path)) { + lua_getglobal(lstate, "loadfile"); + lua_pushstring(lstate, path); + + if (nlua_pcall(lstate, 1, 2)) { + nlua_error(lstate, _("E5111: Error calling lua: %.*s")); + return false; + } + + // loadstring() returns either: + // 1. nil, error + // 2. chunk, nil + + if (lua_isnil(lstate, -2)) { + // 1 nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); + assert(lua_isnil(lstate, -1)); + lua_pop(lstate, 1); return false; } + // 2 + assert(lua_isnil(lstate, -1)); + lua_pop(lstate, 1); + if (nlua_pcall(lstate, 0, 0)) { nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); return false; -- cgit From 36362ef0aed92e726d967d30e3c6cd87c65642b3 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 18 Feb 2022 17:08:43 +0100 Subject: vim-patch:8.2.4414: solidity files are not recognized (#17451) Problem: Solidity files are not recognized. Solution: Add the *.sol pattern. (Dundar Goc, closes vim/vim#9792) https://github.com/vim/vim/commit/97b231541d4e82fbc85e51121448d95bd43c50ad --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index dd14aa3e6f..46d35a102a 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -478,6 +478,7 @@ let s:filename_checks = { \ 'skill': ['file.il', 'file.ils', 'file.cdf'], \ 'slang': ['file.sl'], \ 'slice': ['file.ice'], + \ 'solidity': ['file.sol'], \ 'solution': ['file.sln'], \ 'slpconf': ['/etc/slp.conf', 'any/etc/slp.conf'], \ 'slpreg': ['/etc/slp.reg', 'any/etc/slp.reg'], -- cgit From 30bf40ec4b44172d2720a6f2365cf6acb5ad8863 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 19 Feb 2022 15:13:37 +0000 Subject: vim-patch:8.2.4403: ml_get error with nested folds and deleting lines Problem: ml_get error with nested folds and deleting lines. Solution: Correct the last line number before calling hasFoldingWin(). https://github.com/vim/vim/commit/943773783384a5ff63f57769d37ddabf8156fe1e --- src/nvim/change.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/change.c b/src/nvim/change.c index 6ac759d5e0..736867b6d3 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -223,19 +223,20 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra // values for the cursor. // Update the folds for this window. Can't postpone this, because // a following operator might work on the whole fold: ">>dd". - foldUpdate(wp, lnum, lnume + xtra - 1); + linenr_T last = lnume + xtra - 1; // last line after the change + foldUpdate(wp, lnum, last); // The change may cause lines above or below the change to become // included in a fold. Set lnum/lnume to the first/last line that // might be displayed differently. // Set w_cline_folded here as an efficient way to update it when - // inserting lines just above a closed fold. */ + // inserting lines just above a closed fold. bool folded = hasFoldingWin(wp, lnum, &lnum, NULL, false, NULL); if (wp->w_cursor.lnum == lnum) { wp->w_cline_folded = folded; } - folded = hasFoldingWin(wp, lnume, NULL, &lnume, false, NULL); - if (wp->w_cursor.lnum == lnume) { + folded = hasFoldingWin(wp, last, NULL, &last, false, NULL); + if (wp->w_cursor.lnum == last) { wp->w_cline_folded = folded; } -- cgit From 9f4401897a860d10df9ce501eddbde725c943e44 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 19 Feb 2022 14:58:41 +0000 Subject: vim-patch:8.2.4418: crash when using special multi-byte character Problem: Crash when using special multi-byte character. Solution: Don't use isalpha() for an arbitrary character. https://github.com/vim/vim/commit/5921aeb5741fc6e84c870d68c7c35b93ad0c9f87 Rename vim_isalpha to mb_isalpha. --- src/nvim/mbyte.c | 6 ++++++ src/nvim/path.c | 3 +-- src/nvim/testdir/test_autochdir.vim | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index e5fa80a242..f634c7dda8 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1317,6 +1317,12 @@ bool mb_isupper(int a) return mb_tolower(a) != a; } +bool mb_isalpha(int a) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + return mb_islower(a) || mb_isupper(a); +} + static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2) { int c1, c2, cdiff; diff --git a/src/nvim/path.c b/src/nvim/path.c index 7f7f941e26..d3aa5e5bf2 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -642,8 +642,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff, } else if (path_end >= path + wildoff && (vim_strchr((char_u *)"*?[{~$", *path_end) != NULL #ifndef WIN32 - || (!p_fic && (flags & EW_ICASE) - && isalpha(utf_ptr2char(path_end))) + || (!p_fic && (flags & EW_ICASE) && mb_isalpha(utf_ptr2char(path_end))) #endif )) { e = p; diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 9ad727241e..a012541c0d 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -64,4 +64,14 @@ func Test_verbose_pwd() call delete('Xautodir', 'rf') endfunc +func Test_multibyte() + " using an invalid character should not cause a crash + set wic + " E344 is thrown first, but v8.1.1183 hasn't been ported yet + " call assert_fails('tc û¦*', 'E344:') + call assert_fails('tc û¦*', 'E472:') + set nowic +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 73cc729dbc156c5882e1db96b35913d4df48c7ba Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 19 Feb 2022 14:22:32 +0000 Subject: vim-patch:8.2.4419: illegal memory access when using 20 highlights Problem: Illegal memory access when using exactly 20 highlights. Solution: Add one more item in the array. (Brandon Richardson, closes vim/vim#9800) https://github.com/vim/vim/commit/a493b6506b67887a1cc2d1c00a896598c3b2d445 --- src/nvim/buffer.c | 12 ++++++++---- src/nvim/testdir/test_tabline.vim | 11 +++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 38b045b31c..aada11bc9e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3438,8 +3438,12 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use if (stl_items == NULL) { stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); stl_groupitems = xmalloc(sizeof(int) * stl_items_len); - stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); - stl_tabtab = xmalloc(sizeof(StlClickRecord) * stl_items_len); + + // Allocate one more, because the last element is used to indicate the + // end of the list. + stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1)); + stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1)); + stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); } @@ -3514,8 +3518,8 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len); stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len); - stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * new_len); - stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * new_len); + stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1)); + stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1)); stl_separator_locations = xrealloc(stl_separator_locations, sizeof(int) * new_len); diff --git a/src/nvim/testdir/test_tabline.vim b/src/nvim/testdir/test_tabline.vim index 117d962d08..3a18206078 100644 --- a/src/nvim/testdir/test_tabline.vim +++ b/src/nvim/testdir/test_tabline.vim @@ -86,6 +86,17 @@ func Test_tabline_empty_group() set tabline= endfunc +" When there are exactly 20 tabline format items (the exact size of the +" initial tabline items array), test that we don't write beyond the size +" of the array. +func Test_tabline_20_format_items_no_overrun() + set showtabline=2 + + let tabline = repeat('%#StatColorHi2#', 20) + let &tabline = tabline + redrawtabline + set showtabline& tabline& +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 9c04285057b70f90ca19cb08a49f96369085d882 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 19 Feb 2022 15:38:11 +0000 Subject: vim-patch:8.2.4422: autochdir test fails on MS-Windows Problem: Autochdir test fails on MS-Windows. Solution: Expecta nother error on MS-Windows. https://github.com/vim/vim/commit/adbb383e0f2bb59286ea8133f02c448fd334958f --- src/nvim/testdir/test_autochdir.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index a012541c0d..53ed4617f7 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -67,9 +67,9 @@ endfunc func Test_multibyte() " using an invalid character should not cause a crash set wic - " E344 is thrown first, but v8.1.1183 hasn't been ported yet - " call assert_fails('tc û¦*', 'E344:') - call assert_fails('tc û¦*', 'E472:') + " Except on Windows, E472 is also thrown last, but v8.1.1183 isn't ported yet + " call assert_fails('tc û¦*', has('win32') ? 'E480:' : 'E344:') + call assert_fails('tc û¦*', has('win32') ? 'E480:' : 'E472:') set nowic endfunc -- cgit From 439a843b80339d80e788e8382ae91414c3db6dd5 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 19 Feb 2022 23:41:11 +0100 Subject: vim-patch:8.2.4424: ".gts" and ".gjs" files are not recognized (#17464) Problem: ".gts" and ".gjs" files are not recognized. Solution: Recognize Glimmer flavored typescript and javascript. (closes vim/vim#9799) https://github.com/vim/vim/commit/cdf717283ca70b18f20b8a2cefe7957083280c6f --- src/nvim/testdir/test_filetype.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 46d35a102a..c2cfac6fe3 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -272,6 +272,7 @@ let s:filename_checks = { \ 'java': ['file.java', 'file.jav'], \ 'javacc': ['file.jj', 'file.jjt'], \ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'], + \ 'javascript.glimmer': ['file.gjs'], \ 'javascriptreact': ['file.jsx'], \ 'jess': ['file.clp'], \ 'jgraph': ['file.jgr'], @@ -546,6 +547,7 @@ let s:filename_checks = { \ 'tssgm': ['file.tssgm'], \ 'tssop': ['file.tssop'], \ 'twig': ['file.twig'], + \ 'typescript.glimmer': ['file.gts'], \ 'typescriptreact': ['file.tsx'], \ 'uc': ['file.uc'], \ 'udevconf': ['/etc/udev/udev.conf', 'any/etc/udev/udev.conf'], -- cgit From 82c5a02050dcc91673d4ef66b4ad20ecf9cae8c8 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sat, 19 Feb 2022 11:11:05 +0100 Subject: ci: skip tests that fail on windows --- src/nvim/testdir/test_alot.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index c0ac4393c4..5a3d1d56bb 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -39,7 +39,8 @@ source test_put.vim source test_rename.vim source test_scroll_opt.vim source test_shift.vim -source test_sort.vim +" Test fails on windows CI when using the MSVC compiler. +" source test_sort.vim source test_sha256.vim source test_suspend.vim source test_syn_attr.vim -- cgit From 3828fb7ea431c9ceec815c41aed89c50ab9712f5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Feb 2022 06:04:56 +0800 Subject: vim-patch:8.2.4427: getchar() may return modifiers if no character is available Problem: getchar() may return modifiers if no character is available. Solution: Do not process modifiers when there is no character. (closes vim/vim#9806) https://github.com/vim/vim/commit/ad6c45f62558e03d3e3a927b3fe4dbaf30a36bef --- src/nvim/eval/funcs.c | 2 +- src/nvim/testdir/test_functions.vim | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c6baa105b0..3763390c22 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3225,7 +3225,7 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) set_vim_var_nr(VV_MOUSE_COL, 0); rettv->vval.v_number = n; - if (IS_SPECIAL(n) || mod_mask != 0) { + if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) { char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1 int i = 0; diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 6e36f4e3d2..994d74601a 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1451,6 +1451,10 @@ func Test_getchar() call assert_equal('', getcharstr(0)) call assert_equal('', getcharstr(1)) + call feedkeys("\", '') + call assert_equal("\", getchar(0)) + call assert_equal(0, getchar(0)) + call setline(1, 'xxxx') " call test_setmouse(1, 3) " let v:mouse_win = 9 -- cgit From 1e7cb2dcd975aadeb91b913f117b21c7775c3374 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 21 Feb 2022 20:17:36 +0000 Subject: fix(highlight): accept NONE as a color name (#17487) ... for when `ns=0`. Also update the documentation of nvim_set_hl to clarify the set behaviour. Fixes #17478 --- src/nvim/api/vim.c | 4 ++++ src/nvim/highlight.c | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 11bb1750e4..4dc599564f 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -124,6 +124,10 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// Set a highlight group. /// +/// Note: unlike the `:highlight` command which can update a highlight group, +/// this function completely replaces the definition. For example: +/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group 'Visual'. +/// /// @param ns_id number of namespace for this highlight. Use value 0 /// to set a highlight group in the global (`:highlight`) /// namespace. diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index 8b998ff62e..bbad27014d 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -927,10 +927,11 @@ int object_to_color(Object val, char *key, bool rgb, Error *err) } else if (val.type == kObjectTypeString) { String str = val.data.string; // TODO(bfredl): be more fancy with "bg", "fg" etc + if (!str.size || STRICMP(str.data, "NONE") == 0) { + return -1; + } int color; - if (!str.size) { - color = 0; - } else if (rgb) { + if (rgb) { color = name_to_color(str.data); } else { color = name_to_ctermcolor(str.data); -- cgit From 11f7aeed7aa83d342d19897d9a69ba9f32ece7f7 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 22 Feb 2022 13:19:21 -0700 Subject: feat(api): implement nvim_buf_get_text (#15181) nvim_buf_get_text is the mirror of nvim_buf_set_text. It differs from nvim_buf_get_lines in that it allows retrieving only portions of lines. While this can typically be done easily enough by API clients, implementing this function provides symmetry between the get/set text/lines APIs, and also provides a nice convenience that saves API clients the work of having to slice the result of nvim_buf_get_lines themselves. --- src/nvim/api/buffer.c | 119 +++++++++++++++++++++++++++++++++++++---- src/nvim/api/private/helpers.c | 47 +++++++++++++++- 2 files changed, 156 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 2d5403d4b8..02bd294c74 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -287,8 +287,8 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, } bool oob = false; - start = normalize_index(buf, start, &oob); - end = normalize_index(buf, end, &oob); + start = normalize_index(buf, start, true, &oob); + end = normalize_index(buf, end, true, &oob); if (strict_indexing && oob) { api_set_error(err, kErrorTypeValidation, "Index out of bounds"); @@ -374,15 +374,14 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ } bool oob = false; - start = normalize_index(buf, start, &oob); - end = normalize_index(buf, end, &oob); + start = normalize_index(buf, start, true, &oob); + end = normalize_index(buf, end, true, &oob); if (strict_indexing && oob) { api_set_error(err, kErrorTypeValidation, "Index out of bounds"); return; } - if (start > end) { api_set_error(err, kErrorTypeValidation, @@ -554,13 +553,13 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // check range is ordered and everything! // start_row, end_row within buffer len (except add text past the end?) - start_row = normalize_index(buf, start_row, &oob); + start_row = normalize_index(buf, start_row, true, &oob); if (oob || start_row == buf->b_ml.ml_line_count + 1) { api_set_error(err, kErrorTypeValidation, "start_row out of bounds"); return; } - end_row = normalize_index(buf, end_row, &oob); + end_row = normalize_index(buf, end_row, true, &oob); if (oob || end_row == buf->b_ml.ml_line_count + 1) { api_set_error(err, kErrorTypeValidation, "end_row out of bounds"); return; @@ -757,6 +756,108 @@ end: try_end(err); } +/// Gets a range from the buffer. +/// +/// This differs from |nvim_buf_get_lines()| in that it allows retrieving only +/// portions of a line. +/// +/// Indexing is zero-based. Column indices are end-exclusive. +/// +/// Prefer |nvim_buf_get_lines()| when retrieving entire lines. +/// +/// @param channel_id +/// @param buffer Buffer handle, or 0 for current buffer +/// @param start_row First line index +/// @param start_col Starting byte offset of first line +/// @param end_row Last line index +/// @param end_col Ending byte offset of last line (exclusive) +/// @param opts Optional parameters. Currently unused. +/// @param[out] err Error details, if any +/// @return Array of lines, or empty array for unloaded buffer. +ArrayOf(String) nvim_buf_get_text(uint64_t channel_id, Buffer buffer, + Integer start_row, Integer start_col, + Integer end_row, Integer end_col, + Dictionary opts, Error *err) + FUNC_API_SINCE(9) +{ + Array rv = ARRAY_DICT_INIT; + + if (opts.size > 0) { + api_set_error(err, kErrorTypeValidation, "opts dict isn't empty"); + return rv; + } + + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return rv; + } + + // return sentinel value if the buffer isn't loaded + if (buf->b_ml.ml_mfp == NULL) { + return rv; + } + + bool oob = false; + start_row = normalize_index(buf, start_row, false, &oob); + end_row = normalize_index(buf, end_row, false, &oob); + + if (oob) { + api_set_error(err, kErrorTypeValidation, "Index out of bounds"); + return rv; + } + + // nvim_buf_get_lines doesn't care if the start row is greater than the end + // row (it will just return an empty array), but nvim_buf_get_text does in + // order to maintain symmetry with nvim_buf_set_text. + if (start_row > end_row) { + api_set_error(err, kErrorTypeValidation, "start is higher than end"); + return rv; + } + + bool replace_nl = (channel_id != VIML_INTERNAL_CALL); + + if (start_row == end_row) { + String line = buf_get_text(buf, start_row, start_col, end_col, replace_nl, err); + if (ERROR_SET(err)) { + return rv; + } + + ADD(rv, STRING_OBJ(line)); + return rv; + } + + rv.size = (size_t)(end_row - start_row) + 1; + rv.items = xcalloc(rv.size, sizeof(Object)); + + rv.items[0] = STRING_OBJ(buf_get_text(buf, start_row, start_col, MAXCOL-1, replace_nl, err)); + if (ERROR_SET(err)) { + goto end; + } + + if (rv.size > 2) { + Array tmp = ARRAY_DICT_INIT; + tmp.items = &rv.items[1]; + if (!buf_collect_lines(buf, rv.size - 2, start_row + 1, replace_nl, &tmp, err)) { + goto end; + } + } + + rv.items[rv.size-1] = STRING_OBJ(buf_get_text(buf, end_row, 0, end_col, replace_nl, err)); + if (ERROR_SET(err)) { + goto end; + } + +end: + if (ERROR_SET(err)) { + api_free_array(rv); + rv.size = 0; + rv.items = NULL; + } + + return rv; +} + /// Returns the byte offset of a line (0-indexed). |api-indexing| /// /// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte. @@ -1386,11 +1487,11 @@ static void fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) } // Normalizes 0-based indexes to buffer line numbers -static int64_t normalize_index(buf_T *buf, int64_t index, bool *oob) +static int64_t normalize_index(buf_T *buf, int64_t index, bool end_exclusive, bool *oob) { int64_t line_count = buf->b_ml.ml_line_count; // Fix if < 0 - index = index < 0 ? line_count + index +1 : index; + index = index < 0 ? line_count + index + (int)end_exclusive : index; // Check for oob if (index > line_count) { diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2b107a3f27..971fa1cb0f 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -411,7 +411,6 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object current_sctx = save_current_sctx; } - buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -758,6 +757,52 @@ bool buf_collect_lines(buf_T *buf, size_t n, int64_t start, bool replace_nl, Arr return true; } +/// Returns a substring of a buffer line +/// +/// @param buf Buffer handle +/// @param lnum Line number (1-based) +/// @param start_col Starting byte offset into line (0-based) +/// @param end_col Ending byte offset into line (0-based, exclusive) +/// @param replace_nl Replace newlines ('\n') with null ('\0') +/// @param err Error object +/// @return The text between start_col and end_col on line lnum of buffer buf +String buf_get_text(buf_T *buf, int64_t lnum, int64_t start_col, int64_t end_col, bool replace_nl, + Error *err) +{ + String rv = STRING_INIT; + + if (lnum >= MAXLNUM) { + api_set_error(err, kErrorTypeValidation, "Line index is too high"); + return rv; + } + + const char *bufstr = (char *)ml_get_buf(buf, (linenr_T)lnum, false); + size_t line_length = strlen(bufstr); + + start_col = start_col < 0 ? (int64_t)line_length + start_col + 1 : start_col; + end_col = end_col < 0 ? (int64_t)line_length + end_col + 1 : end_col; + + if (start_col >= MAXCOL || end_col >= MAXCOL) { + api_set_error(err, kErrorTypeValidation, "Column index is too high"); + return rv; + } + + if (start_col > end_col) { + api_set_error(err, kErrorTypeValidation, "start_col must be less than end_col"); + return rv; + } + + if ((size_t)start_col >= line_length) { + return rv; + } + + rv = cstrn_to_string(&bufstr[start_col], (size_t)(end_col - start_col)); + if (replace_nl) { + strchrsub(rv.data, '\n', '\0'); + } + + return rv; +} void api_free_string(String value) { -- cgit From 2deffb5ea8e2fc8dec2cc805dbed849ad6afa4b4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 31 Dec 2021 09:32:04 +0000 Subject: fix(aucmd_win): ensure aucmd_win stays floating Nvim uses a floating window for the autocmd window, but in certain situations, it can be made non-floating (`:wincmd J`), which can cause issues due to the previous setup and cleanup logic for a non-floating aucmd_win being removed from aucmd_prepbuf and aucmd_restbuf. This can cause glitchiness and crashes due to the aucmd_win's frame being invalid after closing its tabpage, for example. Ensure aucmd_win cannot be made non-floating. The only place this happens is in win_split_ins if new_wp != NULL. --- src/nvim/autocmd.c | 3 +++ src/nvim/window.c | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 9117dde089..cdaa120644 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1147,6 +1147,7 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) globaldir = NULL; block_autocmds(); // We don't want BufEnter/WinEnter autocommands. + make_snapshot(SNAP_AUCMD_IDX); if (need_append) { win_append(lastwin, aucmd_win); pmap_put(handle_T)(&window_handles, aucmd_win->handle, aucmd_win); @@ -1212,6 +1213,8 @@ win_found: close_tabpage(curtab); } + restore_snapshot(SNAP_AUCMD_IDX, false); + win_comp_pos(); // recompute window positions unblock_autocmds(); win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); diff --git a/src/nvim/window.c b/src/nvim/window.c index 43667377c5..549dd18e4f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -958,6 +958,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int wmh1; bool did_set_fraction = false; + // aucmd_win should always remain floating + if (new_wp != NULL && new_wp == aucmd_win) { + return FAIL; + } + if (flags & WSP_TOP) { oldwin = firstwin; } else if (flags & WSP_BOT || curwin->w_floating) { -- cgit From 430371da5ba40a791873b30a900ff34da95a9de4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 22 Feb 2022 21:06:53 +0000 Subject: refactor(aucmd_win): remove need to restore window layout There are some places that mess with the window layout in preparation for moving a window to a different split (win_split_ins called with new_wp != NULL). This means the window layout can change slightly even if win_split_ins fails. This is why it was still needed to restore the window layout in aucmd_{prep,rest}buf even if we disallow win_split_ins from making aucmd_win non-floating by moving it into a split. We can just skip messing with the layout in such places if we're dealing with the aucmd_win. --- src/nvim/autocmd.c | 3 --- src/nvim/eval/funcs.c | 2 +- src/nvim/window.c | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index cdaa120644..9117dde089 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1147,7 +1147,6 @@ void aucmd_prepbuf(aco_save_T *aco, buf_T *buf) globaldir = NULL; block_autocmds(); // We don't want BufEnter/WinEnter autocommands. - make_snapshot(SNAP_AUCMD_IDX); if (need_append) { win_append(lastwin, aucmd_win); pmap_put(handle_T)(&window_handles, aucmd_win->handle, aucmd_win); @@ -1213,8 +1212,6 @@ win_found: close_tabpage(curtab); } - restore_snapshot(SNAP_AUCMD_IDX, false); - win_comp_pos(); // recompute window positions unblock_autocmds(); win_T *const save_curwin = win_find_by_handle(aco->save_curwin_handle); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3763390c22..c5b01701de 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -4242,7 +4242,7 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags int height = wp->w_height; win_T *oldwin = curwin; - if (wp == targetwin) { + if (wp == targetwin || wp == aucmd_win) { return; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 549dd18e4f..83048d911f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1838,6 +1838,9 @@ static void win_totop(int size, int flags) beep_flush(); return; } + if (curwin == aucmd_win) { + return; + } if (curwin->w_floating) { ui_comp_remove_grid(&curwin->w_grid_alloc); -- cgit From 15004473b531e37a6ff3aeca9e3bfeaaa1487d9e Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Wed, 23 Feb 2022 15:19:47 -0700 Subject: fix(api)!: correctly handle negative line numbers for nvim_buf_set_text (#17498) nvim_buf_set_text does not handle negative row numbers correctly: for example, nvim_buf_set_text(0, -2, 0, -1, 20, {"Hello", "world"}) should replace the 2nd to last line in the buffer with "Hello" and the first 20 characters of the last line with "world". Instead, it reports "start_row out of bounds". This happens because when negative line numbers are used, they are incremented by one additional number to make the non-negative line numbers end-exclusive. However, the line numbers for nvim_buf_set_text should be end-inclusive. In #15181 we handled this for nvim_buf_get_text by adding a new parameter to `normalize_index`. We can solve the problem with nvim_buf_set_text by simply availing ourselves of this new argument. This is a breaking change, but makes the semantics of negative line numbers much clearer and more obvious (as well as matching nvim_buf_get_text). BREAKING CHANGE: Existing usages of nvim_buf_set_text that use negative line numbers will be off-by-one. --- src/nvim/api/buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 02bd294c74..922d288da1 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -553,13 +553,13 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In // check range is ordered and everything! // start_row, end_row within buffer len (except add text past the end?) - start_row = normalize_index(buf, start_row, true, &oob); + start_row = normalize_index(buf, start_row, false, &oob); if (oob || start_row == buf->b_ml.ml_line_count + 1) { api_set_error(err, kErrorTypeValidation, "start_row out of bounds"); return; } - end_row = normalize_index(buf, end_row, true, &oob); + end_row = normalize_index(buf, end_row, false, &oob); if (oob || end_row == buf->b_ml.ml_line_count + 1) { api_set_error(err, kErrorTypeValidation, "end_row out of bounds"); return; -- cgit From b5bf4877c0239767c1095e4567e67c222bea38a0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Feb 2022 16:50:05 +0000 Subject: feat(highlight): support for blend in nvim_set_hl (#17516) --- src/nvim/api/keysets.lua | 1 + src/nvim/highlight.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) (limited to 'src') diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index f6dce1905e..45a57b9257 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -97,6 +97,7 @@ return { "special"; "sp"; "link"; "fallback"; + "blend"; "temp"; }; highlight_cterm = { diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index bbad27014d..e43a56086f 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -800,6 +800,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e { HlAttrs hlattrs = HLATTRS_INIT; int32_t fg = -1, bg = -1, ctermfg = -1, ctermbg = -1, sp = -1; + int blend = -1; int16_t mask = 0; int16_t cterm_mask = 0; bool cterm_mask_provided = false; @@ -847,6 +848,20 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e return hlattrs; } + if (dict->blend.type == kObjectTypeInteger) { + Integer blend0 = dict->blend.data.integer; + if (blend0 < 0 || blend0 > 100) { + api_set_error(err, kErrorTypeValidation, "'blend' is not between 0 to 100"); + } else { + blend = (int)blend0; + } + } else if (HAS_KEY(dict->blend)) { + api_set_error(err, kErrorTypeValidation, "'blend' must be an integer"); + } + if (ERROR_SET(err)) { + return hlattrs; + } + if (HAS_KEY(dict->link)) { if (link_id) { *link_id = object_to_hl_id(dict->link, "link", err); @@ -908,6 +923,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e hlattrs.rgb_bg_color = bg; hlattrs.rgb_fg_color = fg; hlattrs.rgb_sp_color = sp; + hlattrs.hl_blend = blend; hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1; hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1; hlattrs.cterm_ae_attr = cterm_mask; -- cgit From e67cd22c38493d4dff90f6afa17bfeacd0ba953d Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 20 Feb 2022 17:26:39 +0000 Subject: fix(signcol): handle edge case with maximum value 50250542 failed to consider that the maximum passed to buf_signcols is window scoped whereas the signcols value is buffer scoped. This can lead to a bug where the signcolumn becomes incorrect if: - global signcolumn is set to auto:N - signcolumn in a window is changed locally to auto:M where M > N - the buffer has a line with M or greater signs. --- src/nvim/buffer.c | 8 ++++++++ src/nvim/buffer_defs.h | 1 + src/nvim/option.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index dd40623af2..9e82b4e80b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5495,11 +5495,19 @@ static int buf_signcols_inner(buf_T *buf, int maximum) int buf_signcols(buf_T *buf, int maximum) { + // The maximum can be determined from 'signcolumn' which is window scoped so + // need to invalidate signcols if the maximum is greater than the previous + // maximum. + if (maximum > buf->b_signcols_max) { + buf->b_signcols_valid = false; + } + if (!buf->b_signcols_valid) { int signcols = buf_signcols_inner(buf, maximum); // Check if we need to redraw if (signcols != buf->b_signcols) { buf->b_signcols = signcols; + buf->b_signcols_max = maximum; redraw_buf_later(buf, NOT_VALID); } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7ae5df164f..5d4f8d112c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -864,6 +864,7 @@ struct file_buffer { sign_entry_T *b_signlist; // list of placed signs int b_signcols; // last calculated number of sign columns bool b_signcols_valid; // calculated sign columns is valid + int b_signcols_max; // Maximum value b_signcols is valid for. Terminal *terminal; // Terminal instance associated with the buffer diff --git a/src/nvim/option.c b/src/nvim/option.c index d97a22c342..4ec6ee3148 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -8165,7 +8165,7 @@ int win_signcol_configured(win_T *wp, int *is_fixed) } int needed_signcols = buf_signcols(wp->w_buffer, maximum); - int ret = MAX(minimum, needed_signcols); + int ret = MAX(minimum, MIN(maximum, needed_signcols)); assert(ret <= SIGN_SHOW_MAX); return ret; } -- cgit From eb6e5d09910dc80932f91135f83957bd96efc3a9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 20 Feb 2022 17:34:10 +0000 Subject: refactor(signcol): store signcol in win_T --- src/nvim/buffer_defs.h | 1 + src/nvim/screen.c | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5d4f8d112c..1e0c837056 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1354,6 +1354,7 @@ struct window_S { // recomputed int w_nrwidth; // width of 'number' and 'relativenumber' // column being used + int w_scwidth; // width of 'signcolumn' /* * === end of cached values === diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 15fb6901cc..dcc4722b0d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -794,7 +794,7 @@ static void win_update(win_T *wp, Providers *providers) // If we can compute a change in the automatic sizing of the sign column // under 'signcolumn=auto:X' and signs currently placed in the buffer, better // figuring it out here so we can redraw the entire screen for it. - win_signcol_count(wp); + wp->w_scwidth = win_signcol_count(wp); type = wp->w_redr_type; @@ -1846,7 +1846,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i win_hl_attr(wp, HLF_FC)); } // draw the sign column - int count = win_signcol_count(wp); + int count = wp->w_scwidth; if (count > 0) { n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row, endrow, win_hl_attr(wp, HLF_SC)); @@ -2792,10 +2792,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc draw_state = WL_SIGN; /* Show the sign column when there are any signs in this * buffer or when using Netbeans. */ - int count = win_signcol_count(wp); - if (count > 0) { + if (wp->w_scwidth > 0) { get_sign_display_info(false, wp, lnum, sattrs, row, - startrow, filler_lines, filler_todo, count, + startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), &p_extra, &n_extra, &char_attr, &draw_state, &sign_idx); @@ -2814,9 +2813,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // number. if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) { - int count = win_signcol_count(wp); get_sign_display_info(true, wp, lnum, sattrs, row, - startrow, filler_lines, filler_todo, count, + startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), &p_extra, &n_extra, &char_attr, &draw_state, &sign_idx); @@ -4674,10 +4672,11 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) // @param[in, out] sign_idxp Index of the displayed sign static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[], int row, int startrow, int filler_lines, int filler_todo, - int count, int *c_extrap, int *c_finalp, char_u *extra, + int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, char_u **pp_extra, int *n_extrap, int *char_attrp, int *draw_statep, int *sign_idxp) { + int count = wp->w_scwidth; // Draw cells with the sign value or blank. *c_extrap = ' '; *c_finalp = NUL; -- cgit From 9d53791cf8aa170f4a3b569ddbae43d6f1132af0 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 20 Feb 2022 17:39:40 +0000 Subject: fix(signcol): update cursor when signcol changes Fixes #14195 --- src/nvim/screen.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index dcc4722b0d..7fafe3dd6e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -326,6 +326,18 @@ void redraw_buf_status_later(buf_T *buf) } } +void redraw_win_signcol(win_T *wp) +{ + // If we can compute a change in the automatic sizing of the sign column + // under 'signcolumn=auto:X' and signs currently placed in the buffer, better + // figuring it out here so we can redraw the entire screen for it. + int scwidth = wp->w_scwidth; + wp->w_scwidth = win_signcol_count(wp); + if (wp->w_scwidth != scwidth) { + changed_line_abv_curs_win(wp); + } +} + /// Redraw the parts of the screen that is marked for redraw. /// /// Most code shouldn't call this directly, rather use redraw_later() and @@ -790,12 +802,6 @@ static void win_update(win_T *wp, Providers *providers) linenr_T mod_bot = 0; int save_got_int; - - // If we can compute a change in the automatic sizing of the sign column - // under 'signcolumn=auto:X' and signs currently placed in the buffer, better - // figuring it out here so we can redraw the entire screen for it. - wp->w_scwidth = win_signcol_count(wp); - type = wp->w_redr_type; if (type >= NOT_VALID) { @@ -817,6 +823,8 @@ static void win_update(win_T *wp, Providers *providers) return; } + redraw_win_signcol(wp); + init_search_hl(wp); /* Force redraw when width of 'number' or 'relativenumber' column -- cgit From 81bffbd147cd24580ac92fa9d9d85121151ca01f Mon Sep 17 00:00:00 2001 From: Oliver Marriott Date: Sat, 19 Feb 2022 22:56:50 +1100 Subject: feat: call __tostring on lua errors if possible before reporting to user --- src/nvim/lua/executor.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 029e7eb660..a61273084c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -78,7 +78,22 @@ static void nlua_error(lua_State *const lstate, const char *const msg) FUNC_ATTR_NONNULL_ALL { size_t len; - const char *const str = lua_tolstring(lstate, -1, &len); + const char *str = NULL; + + if (luaL_getmetafield(lstate, -1, "__tostring")) { + if (lua_isfunction(lstate, -1) && luaL_callmeta(lstate, -2, "__tostring")) { + // call __tostring, convert the result and pop result. + str = lua_tolstring(lstate, -1, &len); + lua_pop(lstate, 1); + } + // pop __tostring. + lua_pop(lstate, 1); + } + + if (!str) { + // defer to lua default conversion, this will render tables as [NULL]. + str = lua_tolstring(lstate, -1, &len); + } msg_ext_set_kind("lua_error"); semsg_multiline(msg, (int)len, str); -- cgit From e5b5cbd19c6374540ee6ffa6d8b27ceb8a293f65 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 25 Feb 2022 18:45:48 -0800 Subject: feat(hardcopy): check gui colours for highlights first Previously, :hardcopy would only use terminal highlight colours, with a fixed mapping table, despite internally supporting true colour. This patch looks at the guifg colour first while coming up with the printing highlight colours, then falls back to the terminal ones. I have passed through the modec argument in this change because it was there before, but it could be deleted and hardcoded to 'c' since nobody sets it to anything else anywhere. --- src/nvim/hardcopy.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 6fc70144ac..eb10c65be9 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -386,30 +386,43 @@ static uint32_t prt_get_term_color(int colorindex) return cterm_color_8[colorindex % 8]; } -static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) +static uint32_t prt_get_color(int hl_id, int modec) { int colorindex; uint32_t fg_color; + const char *color = highlight_color(hl_id, "fg#", 'g'); + if (color != NULL) { + RgbValue rgb = name_to_color(color); + if (rgb != -1) { + return (uint32_t)rgb; + } + } + + color = highlight_color(hl_id, "fg", modec); + if (color == NULL) { + colorindex = 0; + } else { + colorindex = atoi(color); + } + + if (colorindex >= 0 && colorindex < t_colors) { + fg_color = prt_get_term_color(colorindex); + } else { + fg_color = PRCOLOR_BLACK; + } + + return fg_color; +} + +static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) +{ pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL); pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); - { - const char *color = highlight_color(hl_id, "fg", modec); - if (color == NULL) { - colorindex = 0; - } else { - colorindex = atoi(color); - } - - if (colorindex >= 0 && colorindex < t_colors) { - fg_color = prt_get_term_color(colorindex); - } else { - fg_color = PRCOLOR_BLACK; - } - } + uint32_t fg_color = prt_get_color(hl_id, modec); if (fg_color == PRCOLOR_WHITE) { fg_color = PRCOLOR_BLACK; -- cgit From d0f8f76224f501d919ba6c8a5cd717de76903b34 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 26 Feb 2022 14:01:37 +0100 Subject: vim-patch:8.2.4464: Dtrace files are recognized as filetype D (#17518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Dtrace files are recognized as filetype D. Solution: Add a pattern for Dtrace files. (Teubel György, closes vim/vim#9841) Add some more testing. https://github.com/vim/vim/commit/4d56b971cbae01cc454eb09713326224993e38ed --- src/nvim/testdir/test_filetype.vim | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index c2cfac6fe3..5f4a7dac6e 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -132,6 +132,7 @@ let s:filename_checks = { \ 'cvs': ['cvs123'], \ 'cvsrc': ['.cvsrc'], \ 'cynpp': ['file.cyn'], + \ 'd': ['file.d'], \ 'dart': ['file.dart', 'file.drt'], \ 'datascript': ['file.ds'], \ 'dcd': ['file.dcd'], @@ -154,6 +155,7 @@ let s:filename_checks = { \ 'dot': ['file.dot', 'file.gv'], \ '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'], \ 'dune': ['jbuild', 'dune', 'dune-project', 'dune-workspace'], \ 'dylan': ['file.dylan'], @@ -802,6 +804,42 @@ func Test_bas_file() filetype off endfunc +func Test_d_file() + filetype on + + call writefile(['looks like D'], 'Xfile.d') + split Xfile.d + call assert_equal('d', &filetype) + bwipe! + + call writefile(['#!/some/bin/dtrace'], 'Xfile.d') + split Xfile.d + call assert_equal('dtrace', &filetype) + bwipe! + + call writefile(['#pragma D option'], 'Xfile.d') + split Xfile.d + call assert_equal('dtrace', &filetype) + bwipe! + + call writefile([':some:thing:'], 'Xfile.d') + split Xfile.d + call assert_equal('dtrace', &filetype) + bwipe! + + call writefile(['module this', '#pragma D option'], 'Xfile.d') + split Xfile.d + call assert_equal('d', &filetype) + bwipe! + + call writefile(['import that', '#pragma D option'], 'Xfile.d') + split Xfile.d + call assert_equal('d', &filetype) + bwipe! + + filetype off +endfunc + func Test_dep3patch_file() filetype on -- cgit From b87867e69e94d9784468a126f21c721446f080de Mon Sep 17 00:00:00 2001 From: erw7 Date: Sat, 11 Sep 2021 11:48:58 +0900 Subject: feat(lua): add proper support of luv threads --- src/cjson/lua_cjson.c | 20 ++- src/nvim/CMakeLists.txt | 3 + src/nvim/api/vim.c | 2 +- src/nvim/lua/converter.c | 14 +- src/nvim/lua/executor.c | 460 ++++++++++++++++++++++++++++++++++++++--------- src/nvim/lua/executor.h | 6 +- src/nvim/lua/stdlib.c | 83 ++++----- src/nvim/lua/vim.lua | 47 ----- src/nvim/main.c | 6 +- src/nvim/os/fs.c | 31 +++- src/nvim/runtime.c | 65 ++++++- 11 files changed, 541 insertions(+), 196 deletions(-) (limited to 'src') diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index cf9e82c38e..b5f97bc485 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -776,7 +776,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, if (has_metatable) { - nlua_pushref(l, nlua_empty_dict_ref); + nlua_pushref(l, nlua_get_empty_dict_ref(l)); if (lua_rawequal(l, -2, -1)) { as_empty_dict = true; } else { @@ -822,7 +822,7 @@ static void json_append_data(lua_State *l, json_config_t *cfg, } break; case LUA_TUSERDATA: - nlua_pushref(l, nlua_nil_ref); + nlua_pushref(l, nlua_get_nil_ref(l)); bool is_nil = lua_rawequal(l, -2, -1); lua_pop(l, 1); if (is_nil) { @@ -1285,7 +1285,7 @@ static void json_parse_object_context(lua_State *l, json_parse_t *json) /* Handle empty objects */ if (token.type == T_OBJ_END) { - nlua_pushref(l, nlua_empty_dict_ref); \ + nlua_pushref(l, nlua_get_empty_dict_ref(l)); \ lua_setmetatable(l, -2); \ json_decode_ascend(json); return; @@ -1392,7 +1392,7 @@ static void json_process_value(lua_State *l, json_parse_t *json, if (use_luanil) { lua_pushnil(l); } else { - nlua_pushref(l, nlua_nil_ref); + nlua_pushref(l, nlua_get_nil_ref(l)); } break;; default: @@ -1549,7 +1549,15 @@ int lua_cjson_new(lua_State *l) }; /* Initialise number conversions */ - fpconv_init(); + lua_getfield(l, LUA_REGISTRYINDEX, "nvim.thread"); + bool is_thread = lua_toboolean(l, -1); + lua_pop(l, 1); + + // Since fpconv_init does not need to be called multiple times and is not + // thread safe, it should only be called in the main thread. + if (!is_thread) { + fpconv_init(); + } /* Test if array metatables are in registry */ lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array)); @@ -1582,7 +1590,7 @@ int lua_cjson_new(lua_State *l) compat_luaL_setfuncs(l, reg, 1); /* Set cjson.null */ - nlua_pushref(l, nlua_nil_ref); + nlua_pushref(l, nlua_get_nil_ref(l)); lua_setfield(l, -2, "null"); /* Set cjson.empty_array_mt */ diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index dcc20194f0..3ba3923a82 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -63,6 +63,7 @@ set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua) set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua) +set(LUA_LOAD_PACKAGE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_load_package.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") @@ -336,6 +337,7 @@ add_custom_command( ${LUA_F_MODULE_SOURCE} lua_F_module ${LUA_META_MODULE_SOURCE} lua_meta_module ${LUA_FILETYPE_MODULE_SOURCE} lua_filetype_module + ${LUA_LOAD_PACKAGE_MODULE_SOURCE} lua_load_package_module DEPENDS ${CHAR_BLOB_GENERATOR} ${LUA_VIM_MODULE_SOURCE} @@ -344,6 +346,7 @@ add_custom_command( ${LUA_F_MODULE_SOURCE} ${LUA_META_MODULE_SOURCE} ${LUA_FILETYPE_MODULE_SOURCE} + ${LUA_LOAD_PACKAGE_MODULE_SOURCE} VERBATIM ) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4dc599564f..cd9d61ed24 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1955,7 +1955,7 @@ Dictionary nvim__stats(void) Dictionary rv = ARRAY_DICT_INIT; PUT(rv, "fsync", INTEGER_OBJ(g_stats.fsync)); PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw)); - PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_refcount)); + PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count())); return rv; } diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 0fbd56ed53..47bc9ab91c 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -156,7 +156,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) && ret.string_keys_num == 0)) { ret.type = kObjectTypeArray; if (tsize == 0 && lua_getmetatable(lstate, -1)) { - nlua_pushref(lstate, nlua_empty_dict_ref); + nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); if (lua_rawequal(lstate, -2, -1)) { ret.type = kObjectTypeDictionary; } @@ -401,7 +401,7 @@ nlua_pop_typval_table_processing_end: } case LUA_TUSERDATA: { // TODO(bfredl): check mt.__call and convert to function? - nlua_pushref(lstate, nlua_nil_ref); + nlua_pushref(lstate, nlua_get_nil_ref(lstate)); bool is_nil = lua_rawequal(lstate, -2, -1); lua_pop(lstate, 1); if (is_nil) { @@ -445,7 +445,7 @@ static bool typval_conv_special = false; if (typval_conv_special) { \ lua_pushnil(lstate); \ } else { \ - nlua_pushref(lstate, nlua_nil_ref); \ + nlua_pushref(lstate, nlua_get_nil_ref(lstate)); \ } \ } while (0) @@ -495,7 +495,7 @@ static bool typval_conv_special = false; nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \ } else { \ lua_createtable(lstate, 0, 0); \ - nlua_pushref(lstate, nlua_empty_dict_ref); \ + nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); \ lua_setmetatable(lstate, -2); \ } \ } while (0) @@ -734,7 +734,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special } else { lua_createtable(lstate, 0, (int)dict.size); if (dict.size == 0 && !special) { - nlua_pushref(lstate, nlua_empty_dict_ref); + nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); lua_setmetatable(lstate, -2); } } @@ -782,7 +782,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) if (special) { lua_pushnil(lstate); } else { - nlua_pushref(lstate, nlua_nil_ref); + nlua_pushref(lstate, nlua_get_nil_ref(lstate)); } break; case kObjectTypeLuaRef: { @@ -1206,7 +1206,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; case LUA_TUSERDATA: { - nlua_pushref(lstate, nlua_nil_ref); + nlua_pushref(lstate, nlua_get_nil_ref(lstate)); bool is_nil = lua_rawequal(lstate, -2, -1); lua_pop(lstate, 1); if (is_nil) { diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 029e7eb660..38f21a933e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -45,11 +45,22 @@ static int in_fast_callback = 0; // Initialized in nlua_init(). static lua_State *global_lstate = NULL; +static uv_thread_t main_thread; + typedef struct { Error err; String lua_err_str; } LuaError; +typedef struct { + LuaRef nil_ref; + LuaRef empty_dict_ref; + int ref_count; +#if __has_feature(address_sanitizer) + PMap(handle_T) ref_markers; +#endif +} nlua_ref_state_t; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.c.generated.h" # include "lua/vim_module.generated.h" @@ -65,11 +76,16 @@ typedef struct { } #if __has_feature(address_sanitizer) -static PMap(handle_T) nlua_ref_markers = MAP_INIT; static bool nlua_track_refs = false; # define NLUA_TRACK_REFS #endif +typedef enum luv_err_type { + kCallback, + kThread, + kThreadCallback, +} luv_err_t; + /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. @@ -122,8 +138,21 @@ static int nlua_nvim_version(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL static void nlua_luv_error_event(void **argv) { char *error = (char *)argv[0]; + luv_err_t type = (luv_err_t)(intptr_t)argv[1]; msg_ext_set_kind("lua_error"); - semsg_multiline("Error executing luv callback:\n%s", error); + switch (type) { + case kCallback: + semsg_multiline("Error executing luv callback:\n%s", error); + break; + case kThread: + semsg_multiline("Error in luv thread:\n%s", error); + break; + case kThreadCallback: + semsg_multiline("Error in luv callback, thread:\n%s", error); + break; + default: + break; + } xfree(error); } @@ -148,7 +177,7 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags const char *error = lua_tostring(lstate, -1); multiqueue_put(main_loop.events, nlua_luv_error_event, - 1, xstrdup(error)); + 2, xstrdup(error), (intptr_t)kCallback); lua_pop(lstate, 1); // error message retval = -status; } else { // LUA_OK @@ -162,6 +191,106 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags return retval; } +static int nlua_luv_thread_cb_cfpcall(lua_State *lstate, int nargs, int nresult, + int flags) +{ + return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, true); +} + +static int nlua_luv_thread_cfpcall(lua_State *lstate, int nargs, int nresult, + int flags) + FUNC_ATTR_NONNULL_ALL +{ + return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, false); +} + +static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func, + void *ud, int flags) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + lua_pushcfunction(lstate, func); + lua_pushlightuserdata(lstate, ud); + int retval = nlua_luv_thread_cfpcall(lstate, 1, 0, flags); + return retval; +} + +static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nresult, + int flags, bool is_callback) + FUNC_ATTR_NONNULL_ALL +{ + int retval; + + int top = lua_gettop(lstate); + int status = lua_pcall(lstate, nargs, nresult, 0); + if (status) { + if (status == LUA_ERRMEM && !(flags & LUVF_CALLBACK_NOEXIT)) { + // Terminate this thread, as the main thread may be able to continue + // execution. + mch_errmsg(e_outofmem); + mch_errmsg("\n"); + lua_close(lstate); +#ifdef WIN32 + ExitThread(0); +#else + pthread_exit(0); +#endif + } + const char *error = lua_tostring(lstate, -1); + + loop_schedule_deferred(&main_loop, + event_create(nlua_luv_error_event, 2, + xstrdup(error), + is_callback + ? (intptr_t)kThreadCallback + : (intptr_t)kThread)); + lua_pop(lstate, 1); // error message + retval = -status; + } else { // LUA_OK + if (nresult == LUA_MULTRET) { + nresult = lua_gettop(lstate) - top + nargs + 1; + } + retval = nresult; + } + + return retval; +} + +static int nlua_thr_api_nvim__get_runtime(lua_State *lstate) +{ + if (lua_gettop(lstate) != 3) { + return luaL_error(lstate, "Expected 3 arguments"); + } + + luaL_checktype(lstate, -1, LUA_TTABLE); + lua_getfield(lstate, -1, "is_lua"); + if (!lua_isboolean(lstate, -1)) { + return luaL_error(lstate, "is_lua is not a boolean"); + } + bool is_lua = lua_toboolean(lstate, -1); + lua_pop(lstate, 2); + + luaL_checktype(lstate, -1, LUA_TBOOLEAN); + bool all = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + + Error err = ERROR_INIT; + const Array pat = nlua_pop_Array(lstate, &err); + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } + + ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all); + nlua_push_Array(lstate, ret, true); + api_free_array(ret); + api_free_array(pat); + + return 1; +} + static void nlua_schedule_event(void **argv) { LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; @@ -302,6 +431,157 @@ static int nlua_wait(lua_State *lstate) return 2; } +static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread) + FUNC_ATTR_NONNULL_ALL +{ + nlua_ref_state_t *ref_state = lua_newuserdata(lstate, sizeof(*ref_state)); + memset(ref_state, 0, sizeof(*ref_state)); + ref_state->nil_ref = LUA_NOREF; + ref_state->empty_dict_ref = LUA_NOREF; + return ref_state; +} + +static nlua_ref_state_t *nlua_get_ref_state(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_getfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state"); + nlua_ref_state_t *ref_state = lua_touserdata(lstate, -1); + lua_pop(lstate, 1); + + return ref_state; +} + +LuaRef nlua_get_nil_ref(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + return ref_state->nil_ref; +} + +LuaRef nlua_get_empty_dict_ref(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + return ref_state->empty_dict_ref; +} + +static int nlua_get_ref_count(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + return ref_state->ref_count; +} + +int nlua_get_global_ref_count(void) +{ + lua_State *const lstate = global_lstate; + return nlua_get_ref_count(lstate); +} + +static void nlua_common_vim_init(lua_State *lstate, bool is_thread) + FUNC_ATTR_NONNULL_ARG(1) +{ + nlua_ref_state_t *ref_state = nlua_new_ref_state(lstate, is_thread); + lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.ref_state"); + + // vim.is_thread + lua_pushboolean(lstate, is_thread); + lua_setfield(lstate, LUA_REGISTRYINDEX, "nvim.thread"); + lua_pushcfunction(lstate, &nlua_is_thread); + lua_setfield(lstate, -2, "is_thread"); + + // vim.NIL + lua_newuserdata(lstate, 0); + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, &nlua_nil_tostring); + lua_setfield(lstate, -2, "__tostring"); + lua_setmetatable(lstate, -2); + ref_state->nil_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); + lua_setfield(lstate, -2, "NIL"); + + // vim._empty_dict_mt + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, &nlua_empty_dict_tostring); + lua_setfield(lstate, -2, "__tostring"); + ref_state->empty_dict_ref = nlua_ref(lstate, -1); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); + lua_setfield(lstate, -2, "_empty_dict_mt"); + + // vim.loop + if (is_thread) { + luv_set_callback(lstate, nlua_luv_thread_cb_cfpcall); + luv_set_thread(lstate, nlua_luv_thread_cfpcall); + luv_set_cthread(lstate, nlua_luv_thread_cfcpcall); + } else { + luv_set_loop(lstate, &main_loop.uv); + luv_set_callback(lstate, nlua_luv_cfpcall); + } + luaopen_luv(lstate); + lua_pushvalue(lstate, -1); + lua_setfield(lstate, -3, "loop"); + + // package.loaded.luv = vim.loop + // otherwise luv will be reinitialized when require'luv' + lua_getglobal(lstate, "package"); + lua_getfield(lstate, -1, "loaded"); + lua_pushvalue(lstate, -3); + lua_setfield(lstate, -2, "luv"); + lua_pop(lstate, 3); +} + +static void nlua_common_package_init(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + { + const char *code = (char *)&shared_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua") + || nlua_pcall(lstate, 0, 0)) { + nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); + return; + } + } + + { + const char *code = (char *)&lua_load_package_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(lua_load_package_module) - 1, "@vim/_load_package.lua") + || lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s")); + return; + } + } + + { + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "loaded"); // [package, loaded] + + const char *code = (char *)&inspect_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua") + || nlua_pcall(lstate, 0, 1)) { + nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); + return; + } + + // [package, loaded, inspect] + lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] + } + + { + const char *code = (char *)&lua_F_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua") + || nlua_pcall(lstate, 0, 1)) { + nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); + return; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + + lua_pop(lstate, 2); // [] + } +} + /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. @@ -362,80 +642,22 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_wait); lua_setfield(lstate, -2, "wait"); - // vim.NIL - lua_newuserdata(lstate, 0); - lua_createtable(lstate, 0, 0); - lua_pushcfunction(lstate, &nlua_nil_tostring); - lua_setfield(lstate, -2, "__tostring"); - lua_setmetatable(lstate, -2); - nlua_nil_ref = nlua_ref(lstate, -1); - lua_pushvalue(lstate, -1); - lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); - lua_setfield(lstate, -2, "NIL"); - - // vim._empty_dict_mt - lua_createtable(lstate, 0, 0); - lua_pushcfunction(lstate, &nlua_empty_dict_tostring); - lua_setfield(lstate, -2, "__tostring"); - nlua_empty_dict_ref = nlua_ref(lstate, -1); - lua_pushvalue(lstate, -1); - lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); - lua_setfield(lstate, -2, "_empty_dict_mt"); + nlua_common_vim_init(lstate, false); // internal vim._treesitter... API nlua_add_treesitter(lstate); - // vim.loop - luv_set_loop(lstate, &main_loop.uv); - luv_set_callback(lstate, nlua_luv_cfpcall); - luaopen_luv(lstate); - lua_pushvalue(lstate, -1); - lua_setfield(lstate, -3, "loop"); - - // package.loaded.luv = vim.loop - // otherwise luv will be reinitialized when require'luv' - lua_getglobal(lstate, "package"); - lua_getfield(lstate, -1, "loaded"); - lua_pushvalue(lstate, -3); - lua_setfield(lstate, -2, "luv"); - lua_pop(lstate, 3); - - nlua_state_add_stdlib(lstate); + nlua_state_add_stdlib(lstate, false); lua_setglobal(lstate, "vim"); - { - const char *code = (char *)&shared_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua") - || nlua_pcall(lstate, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); - return 1; - } - } + nlua_common_package_init(lstate); { lua_getglobal(lstate, "package"); // [package] lua_getfield(lstate, -1, "loaded"); // [package, loaded] - const char *code = (char *)&inspect_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); - return 1; - } - // [package, loaded, inspect] - lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] - - code = (char *)&lua_F_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); - return 1; - } - // [package, loaded, module] - lua_setfield(lstate, -2, "vim.F"); // [package, loaded] - - code = (char *)&lua_filetype_module[0]; + char *code = (char *)&lua_filetype_module[0]; if (luaL_loadbuffer(lstate, code, sizeof(lua_filetype_module) - 1, "@vim/filetype.lua") || nlua_pcall(lstate, 0, 1)) { nlua_error(lstate, _("E5106: Error while creating vim.filetype module: %.*s")); @@ -495,9 +717,60 @@ void nlua_init(void) luaL_openlibs(lstate); nlua_state_init(lstate); + luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); + global_lstate = lstate; + + main_thread = uv_thread_self(); } +static lua_State *nlua_thread_acquire_vm(void) +{ + // If it is called from the main thread, it will attempt to rebuild the cache. + const uv_thread_t self = uv_thread_self(); + if (uv_thread_equal(&main_thread, &self)) { + runtime_search_path_validate(); + } + + lua_State *lstate = luaL_newstate(); + + // Add in the lua standard libraries + luaL_openlibs(lstate); + + // print + lua_pushcfunction(lstate, &nlua_print); + lua_setglobal(lstate, "print"); + + lua_pushinteger(lstate, 0); + lua_setfield(lstate, LUA_REGISTRYINDEX, "nlua.refcount"); + + // vim + lua_newtable(lstate); + + nlua_common_vim_init(lstate, true); + + nlua_state_add_stdlib(lstate, true); + + lua_setglobal(lstate, "vim"); + + nlua_common_package_init(lstate); + + lua_getglobal(lstate, "vim"); + lua_getglobal(lstate, "package"); + lua_getfield(lstate, -1, "loaded"); + lua_getfield(lstate, -1, "vim.inspect"); + lua_setfield(lstate, -4, "inspect"); + lua_pop(lstate, 3); + + lua_getglobal(lstate, "vim"); + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); + lua_setfield(lstate, -2, "nvim__get_runtime"); + lua_setfield(lstate, -2, "api"); + lua_pop(lstate, 1); + + return lstate; +} void nlua_free_all_mem(void) { @@ -505,26 +778,30 @@ void nlua_free_all_mem(void) return; } lua_State *lstate = global_lstate; + nlua_common_free_all_mem(lstate); +} - nlua_unref(lstate, nlua_nil_ref); - nlua_unref(lstate, nlua_empty_dict_ref); +static void nlua_common_free_all_mem(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + nlua_unref(lstate, nlua_get_nil_ref(lstate)); + nlua_unref(lstate, nlua_get_empty_dict_ref(lstate)); #ifdef NLUA_TRACK_REFS - if (nlua_refcount) { - fprintf(stderr, "%d lua references were leaked!", nlua_refcount); + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + if (ref_state->ref_count) { + fprintf(stderr, "%d lua references were leaked!", ref_state->ref_count); } if (nlua_track_refs) { // in case there are leaked luarefs, leak the associated memory // to get LeakSanitizer stacktraces on exit - pmap_destroy(handle_T)(&nlua_ref_markers); + pmap_destroy(handle_T)(&ref_state->ref_markers); } #endif - nlua_refcount = 0; lua_close(lstate); } - static void nlua_print_event(void **argv) { char *str = argv[0]; @@ -602,9 +879,18 @@ static int nlua_print(lua_State *const lstate) #undef PRINT_ERROR ga_append(&msg_ga, NUL); - if (in_fast_callback) { + lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread"); + bool is_thread = lua_toboolean(lstate, -1); + lua_pop(lstate, 1); + + if (is_thread) { + loop_schedule_deferred(&main_loop, + event_create(nlua_print_event, 2, + msg_ga.ga_data, + (intptr_t)msg_ga.ga_len)); + } else if (in_fast_callback) { multiqueue_put(main_loop.events, nlua_print_event, - 2, msg_ga.ga_data, msg_ga.ga_len); + 2, msg_ga.ga_data, (intptr_t)msg_ga.ga_len); } else { nlua_print_event((void *[]){ msg_ga.ga_data, (void *)(intptr_t)msg_ga.ga_len }); @@ -613,10 +899,12 @@ static int nlua_print(lua_State *const lstate) nlua_print_error: ga_clear(&msg_ga); + char *buff = xmalloc(IOSIZE); const char *fmt = _("E5114: Error while converting print argument #%i: %.*s"); - size_t len = (size_t)vim_snprintf((char *)IObuff, IOSIZE, fmt, curargidx, + size_t len = (size_t)vim_snprintf(buff, IOSIZE, fmt, curargidx, (int)errmsg_len, errmsg); - lua_pushlstring(lstate, (char *)IObuff, len); + lua_pushlstring(lstate, buff, len); + xfree(buff); return lua_error(lstate); } @@ -806,16 +1094,19 @@ static int nlua_getenv(lua_State *lstate) /// add the value to the registry +/// The current implementation does not support calls from threads. LuaRef nlua_ref(lua_State *lstate, int index) { + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + lua_pushvalue(lstate, index); LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX); if (ref > 0) { - nlua_refcount++; + ref_state->ref_count++; #ifdef NLUA_TRACK_REFS if (nlua_track_refs) { // dummy allocation to make LeakSanitizer track our luarefs - pmap_put(handle_T)(&nlua_ref_markers, ref, xmalloc(3)); + pmap_put(handle_T)(&ref_state->ref_markers, ref, xmalloc(3)); } #endif } @@ -825,12 +1116,14 @@ LuaRef nlua_ref(lua_State *lstate, int index) /// remove the value from the registry void nlua_unref(lua_State *lstate, LuaRef ref) { + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + if (ref > 0) { - nlua_refcount--; + ref_state->ref_count--; #ifdef NLUA_TRACK_REFS // NB: don't remove entry from map to track double-unref if (nlua_track_refs) { - xfree(pmap_get(handle_T)(&nlua_ref_markers, ref)); + xfree(pmap_get(handle_T)(&ref_state->ref_markers, ref)); } #endif luaL_unref(lstate, LUA_REGISTRYINDEX, ref); @@ -1391,6 +1684,13 @@ cleanup: return ret; } +static int nlua_is_thread(lua_State *lstate) +{ + lua_getfield(lstate, LUA_REGISTRYINDEX, "nvim.thread"); + + return 1; +} + // Required functions for lua c functions as VimL callbacks int nlua_CFunction_func_call(int argcount, typval_T *argvars, typval_T *rettv, void *state) diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index bf78f7ec5e..6fca8e6c09 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -4,6 +4,7 @@ #include #include +#include "nvim/assert.h" #include "nvim/api/private/defs.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" @@ -14,11 +15,6 @@ // Generated by msgpack-gen.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; -EXTERN LuaRef nlua_nil_ref INIT(= LUA_NOREF); -EXTERN LuaRef nlua_empty_dict_ref INIT(= LUA_NOREF); - -EXTERN int nlua_refcount INIT(= 0); - #define set_api_error(s, err) \ do { \ Error *err_ = (err); \ diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 55b23cf0c8..6a2aef6683 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -471,43 +471,49 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL } -void nlua_state_add_stdlib(lua_State *const lstate) +void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) { - // stricmp - lua_pushcfunction(lstate, &nlua_stricmp); - lua_setfield(lstate, -2, "stricmp"); - // str_utfindex - lua_pushcfunction(lstate, &nlua_str_utfindex); - lua_setfield(lstate, -2, "str_utfindex"); - // str_byteindex - lua_pushcfunction(lstate, &nlua_str_byteindex); - lua_setfield(lstate, -2, "str_byteindex"); - // str_utf_pos - lua_pushcfunction(lstate, &nlua_str_utf_pos); - lua_setfield(lstate, -2, "str_utf_pos"); - // str_utf_start - lua_pushcfunction(lstate, &nlua_str_utf_start); - lua_setfield(lstate, -2, "str_utf_start"); - // str_utf_end - lua_pushcfunction(lstate, &nlua_str_utf_end); - lua_setfield(lstate, -2, "str_utf_end"); - // regex - lua_pushcfunction(lstate, &nlua_regex); - lua_setfield(lstate, -2, "regex"); - luaL_newmetatable(lstate, "nvim_regex"); - luaL_register(lstate, NULL, regex_meta); - - lua_pushvalue(lstate, -1); // [meta, meta] - lua_setfield(lstate, -2, "__index"); // [meta] - lua_pop(lstate, 1); // don't use metatable now - - // _getvar - lua_pushcfunction(lstate, &nlua_getvar); - lua_setfield(lstate, -2, "_getvar"); - - // _setvar - lua_pushcfunction(lstate, &nlua_setvar); - lua_setfield(lstate, -2, "_setvar"); + if (!is_thread) { + // stricmp + lua_pushcfunction(lstate, &nlua_stricmp); + lua_setfield(lstate, -2, "stricmp"); + // str_utfindex + lua_pushcfunction(lstate, &nlua_str_utfindex); + lua_setfield(lstate, -2, "str_utfindex"); + // str_byteindex + lua_pushcfunction(lstate, &nlua_str_byteindex); + lua_setfield(lstate, -2, "str_byteindex"); + // str_utf_pos + lua_pushcfunction(lstate, &nlua_str_utf_pos); + lua_setfield(lstate, -2, "str_utf_pos"); + // str_utf_start + lua_pushcfunction(lstate, &nlua_str_utf_start); + lua_setfield(lstate, -2, "str_utf_start"); + // str_utf_end + lua_pushcfunction(lstate, &nlua_str_utf_end); + lua_setfield(lstate, -2, "str_utf_end"); + // regex + lua_pushcfunction(lstate, &nlua_regex); + lua_setfield(lstate, -2, "regex"); + luaL_newmetatable(lstate, "nvim_regex"); + luaL_register(lstate, NULL, regex_meta); + + lua_pushvalue(lstate, -1); // [meta, meta] + lua_setfield(lstate, -2, "__index"); // [meta] + lua_pop(lstate, 1); // don't use metatable now + + // _getvar + lua_pushcfunction(lstate, &nlua_getvar); + lua_setfield(lstate, -2, "_getvar"); + + // _setvar + lua_pushcfunction(lstate, &nlua_setvar); + lua_setfield(lstate, -2, "_setvar"); + + // vim.spell + luaopen_spell(lstate); + lua_setfield(lstate, -2, "spell"); + } // vim.mpack luaopen_mpack(lstate); @@ -526,10 +532,7 @@ void nlua_state_add_stdlib(lua_State *const lstate) lua_pushcfunction(lstate, &nlua_xdl_diff); lua_setfield(lstate, -2, "diff"); - // vim.spell - luaopen_spell(lstate); - lua_setfield(lstate, -2, "spell"); - + // vim.json lua_cjson_new(lstate); lua_setfield(lstate, -2, "json"); } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index f5993c3f55..68de4f960c 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -43,53 +43,6 @@ assert(vim.inspect) vim.filetype = package.loaded['vim.filetype'] assert(vim.filetype) -local pathtrails = {} -vim._so_trails = {} -for s in (package.cpath..';'):gmatch('[^;]*;') do - s = s:sub(1, -2) -- Strip trailing semicolon - -- Find out path patterns. pathtrail should contain something like - -- /?.so, \?.dll. This allows not to bother determining what correct - -- suffixes are. - local pathtrail = s:match('[/\\][^/\\]*%?.*$') - if pathtrail and not pathtrails[pathtrail] then - pathtrails[pathtrail] = true - table.insert(vim._so_trails, pathtrail) - end -end - -function vim._load_package(name) - local basename = name:gsub('%.', '/') - local paths = {"lua/"..basename..".lua", "lua/"..basename.."/init.lua"} - local found = vim.api.nvim__get_runtime(paths, false, {is_lua=true}) - if #found > 0 then - local f, err = loadfile(found[1]) - return f or error(err) - end - - local so_paths = {} - for _,trail in ipairs(vim._so_trails) do - local path = "lua"..trail:gsub('?', basename) -- so_trails contains a leading slash - table.insert(so_paths, path) - end - - found = vim.api.nvim__get_runtime(so_paths, false, {is_lua=true}) - if #found > 0 then - -- Making function name in Lua 5.1 (see src/loadlib.c:mkfuncname) is - -- a) strip prefix up to and including the first dash, if any - -- b) replace all dots by underscores - -- c) prepend "luaopen_" - -- So "foo-bar.baz" should result in "luaopen_bar_baz" - local dash = name:find("-", 1, true) - local modname = dash and name:sub(dash + 1) or name - local f, err = package.loadlib(found[1], "luaopen_"..modname:gsub("%.", "_")) - return f or error(err) - end - return nil -end - --- Insert vim._load_package after the preloader at position 2 -table.insert(package.loaders, 2, vim._load_package) - -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c setmetatable(vim, { diff --git a/src/nvim/main.c b/src/nvim/main.c index 8991ae7f00..dfd5e5dbe0 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -154,10 +154,10 @@ bool event_teardown(void) void early_init(mparm_T *paramp) { env_init(); - fs_init(); eval_init(); // init global variables init_path(argv0 ? argv0 : "nvim"); init_normal_cmds(); // Init the table of Normal mode commands. + runtime_search_path_init(); highlight_init(); #ifdef WIN32 @@ -230,6 +230,10 @@ int main(int argc, char **argv) // `argc` and `argv` are also copied, so that they can be changed. init_params(¶ms, argc, argv); + // Since os_open is called during the init_startuptime, we need to call + // fs_init before it. + fs_init(); + init_startuptime(¶ms); // Need to find "--clean" before actually parsing arguments. diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 24c7678633..cca76e175d 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -40,8 +40,10 @@ bool did_try_to_free = false; \ uv_call_start: {} \ uv_fs_t req; \ + fs_loop_lock(); \ ret = func(&fs_loop, &req, __VA_ARGS__); \ uv_fs_req_cleanup(&req); \ + fs_loop_unlock(); \ if (ret == UV_ENOMEM && !did_try_to_free) { \ try_to_free_memory(); \ did_try_to_free = true; \ @@ -52,12 +54,24 @@ uv_call_start: {} \ // Many fs functions from libuv return that value on success. static const int kLibuvSuccess = 0; static uv_loop_t fs_loop; +static uv_mutex_t fs_loop_mutex; // Initialize the fs module void fs_init(void) { uv_loop_init(&fs_loop); + uv_mutex_init_recursive(&fs_loop_mutex); +} + +void fs_loop_lock(void) +{ + uv_mutex_lock(&fs_loop_mutex); +} + +void fs_loop_unlock(void) +{ + uv_mutex_unlock(&fs_loop_mutex); } @@ -98,9 +112,12 @@ bool os_isrealdir(const char *name) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; + fs_loop_lock(); if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) { + fs_loop_unlock(); return false; } + fs_loop_unlock(); if (S_ISLNK(request.statbuf.st_mode)) { return false; } else { @@ -738,7 +755,9 @@ static int os_stat(const char *name, uv_stat_t *statbuf) return UV_EINVAL; } uv_fs_t request; + fs_loop_lock(); int result = uv_fs_stat(&fs_loop, &request, name, NULL); + fs_loop_unlock(); if (result == kLibuvSuccess) { *statbuf = request.statbuf; } @@ -935,9 +954,11 @@ int os_mkdtemp(const char *template, char *path) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; + fs_loop_lock(); int result = uv_fs_mkdtemp(&fs_loop, &request, template, NULL); + fs_loop_unlock(); if (result == kLibuvSuccess) { - STRNCPY(path, request.path, TEMP_FILE_PATH_MAXLEN); + xstrlcpy(path, request.path, TEMP_FILE_PATH_MAXLEN); } uv_fs_req_cleanup(&request); return result; @@ -962,7 +983,9 @@ int os_rmdir(const char *path) bool os_scandir(Directory *dir, const char *path) FUNC_ATTR_NONNULL_ALL { + fs_loop_lock(); int r = uv_fs_scandir(&fs_loop, &dir->request, path, 0, NULL); + fs_loop_unlock(); if (r < 0) { os_closedir(dir); } @@ -1023,7 +1046,9 @@ bool os_fileinfo_link(const char *path, FileInfo *file_info) return false; } uv_fs_t request; + fs_loop_lock(); bool ok = uv_fs_lstat(&fs_loop, &request, path, NULL) == kLibuvSuccess; + fs_loop_unlock(); if (ok) { file_info->stat = request.statbuf; } @@ -1041,6 +1066,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info) { uv_fs_t request; memset(file_info, 0, sizeof(*file_info)); + fs_loop_lock(); bool ok = uv_fs_fstat(&fs_loop, &request, file_descriptor, @@ -1049,6 +1075,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info) file_info->stat = request.statbuf; } uv_fs_req_cleanup(&request); + fs_loop_unlock(); return ok; } @@ -1165,6 +1192,7 @@ char *os_realpath(const char *name, char *buf) FUNC_ATTR_NONNULL_ARG(1) { uv_fs_t request; + fs_loop_lock(); int result = uv_fs_realpath(&fs_loop, &request, name, NULL); if (result == kLibuvSuccess) { if (buf == NULL) { @@ -1173,6 +1201,7 @@ char *os_realpath(const char *name, char *buf) xstrlcpy(buf, request.ptr, MAXPATHL + 1); } uv_fs_req_cleanup(&request); + fs_loop_unlock(); return result == kLibuvSuccess ? buf : NULL; } diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 1c04cb16b3..4497e2b055 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -24,6 +24,23 @@ static bool runtime_search_path_valid = false; static int *runtime_search_path_ref = NULL; static RuntimeSearchPath runtime_search_path; +static RuntimeSearchPath runtime_search_path_thread; +static uv_mutex_t runtime_search_path_mutex; + +void runtime_search_path_init(void) +{ + uv_mutex_init(&runtime_search_path_mutex); +} + +void runtime_search_path_lock(void) +{ + uv_mutex_lock(&runtime_search_path_mutex); +} + +void runtime_search_path_unlock(void) +{ + uv_mutex_unlock(&runtime_search_path_mutex); +} /// ":runtime [what] {name}" void ex_runtime(exarg_T *eap) @@ -172,6 +189,17 @@ RuntimeSearchPath runtime_search_path_get_cached(int *ref) return runtime_search_path; } +RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src) +{ + RuntimeSearchPath dst = KV_INITIAL_VALUE; + for (size_t j = 0; j < kv_size(src); j++) { + SearchPathItem src_item = kv_A(src, j); + kv_push(dst, ((SearchPathItem){ xstrdup(src_item.path), src_item.after, src_item.has_lua })); + } + + return dst; +} + void runtime_search_path_unref(RuntimeSearchPath path, int *ref) FUNC_ATTR_NONNULL_ALL { @@ -302,15 +330,33 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) { int ref; RuntimeSearchPath path = runtime_search_path_get_cached(&ref); - ArrayOf(String) rv = ARRAY_DICT_INIT; - static char buf[MAXPATHL]; + ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path); + + runtime_search_path_unref(path, &ref); + return rv; +} + +ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all) +{ + runtime_search_path_lock(); + ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread); + runtime_search_path_unlock(); + return rv; +} + +ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, + RuntimeSearchPath path) +{ + ArrayOf(String) rv = ARRAY_DICT_INIT; + size_t buf_len = MAXPATHL; + char *buf = xmalloc(MAXPATHL); for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); if (lua) { if (item->has_lua == kNone) { - size_t size = (size_t)snprintf(buf, sizeof buf, "%s/lua/", item->path); - item->has_lua = (size < sizeof buf && os_isdir((char_u *)buf)) ? kTrue : kFalse; + size_t size = (size_t)snprintf(buf, buf_len, "%s/lua/", item->path); + item->has_lua = (size < buf_len && os_isdir((char_u *)buf)); } if (item->has_lua == kFalse) { continue; @@ -320,9 +366,9 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) for (size_t j = 0; j < pat.size; j++) { Object pat_item = pat.items[j]; if (pat_item.type == kObjectTypeString) { - size_t size = (size_t)snprintf(buf, sizeof buf, "%s/%s", + size_t size = (size_t)snprintf(buf, buf_len, "%s/%s", item->path, pat_item.data.string.data); - if (size < sizeof buf) { + if (size < buf_len) { if (os_file_is_readable(buf)) { ADD(rv, STRING_OBJ(cstr_to_string(buf))); if (!all) { @@ -333,9 +379,8 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) } } } - done: - runtime_search_path_unref(path, &ref); + xfree(buf); return rv; } @@ -569,6 +614,10 @@ void runtime_search_path_validate(void) runtime_search_path = runtime_search_path_build(); runtime_search_path_valid = true; runtime_search_path_ref = NULL; // initially unowned + runtime_search_path_lock(); + runtime_search_path_free(runtime_search_path_thread); + runtime_search_path_thread = copy_runtime_search_path(runtime_search_path); + runtime_search_path_unlock(); } } -- cgit From acf38245d8961125f02d4c4168053e0d83dbc6df Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 25 Dec 2021 14:38:26 +0100 Subject: refactor(lua): use references directly on main thread --- src/nvim/lua/converter.c | 22 ++++++++--------- src/nvim/lua/executor.c | 64 ++++++++++++++++++++++-------------------------- src/nvim/lua/executor.h | 12 +++++++++ 3 files changed, 52 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 47bc9ab91c..2b54e56df1 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -156,7 +156,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate) && ret.string_keys_num == 0)) { ret.type = kObjectTypeArray; if (tsize == 0 && lua_getmetatable(lstate, -1)) { - nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); + nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); if (lua_rawequal(lstate, -2, -1)) { ret.type = kObjectTypeDictionary; } @@ -316,7 +316,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) LuaRef table_ref = LUA_NOREF; if (lua_getmetatable(lstate, -1)) { lua_pop(lstate, 1); - table_ref = nlua_ref(lstate, -1); + table_ref = nlua_ref_global(lstate, -1); } const LuaTableProps table_props = nlua_traverse_table(lstate); @@ -389,7 +389,7 @@ nlua_pop_typval_table_processing_end: } case LUA_TFUNCTION: { LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); - state->lua_callable.func_ref = nlua_ref(lstate, -1); + state->lua_callable.func_ref = nlua_ref_global(lstate, -1); char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, @@ -401,7 +401,7 @@ nlua_pop_typval_table_processing_end: } case LUA_TUSERDATA: { // TODO(bfredl): check mt.__call and convert to function? - nlua_pushref(lstate, nlua_get_nil_ref(lstate)); + nlua_pushref(lstate, nlua_global_refs->nil_ref); bool is_nil = lua_rawequal(lstate, -2, -1); lua_pop(lstate, 1); if (is_nil) { @@ -445,7 +445,7 @@ static bool typval_conv_special = false; if (typval_conv_special) { \ lua_pushnil(lstate); \ } else { \ - nlua_pushref(lstate, nlua_get_nil_ref(lstate)); \ + nlua_pushref(lstate, nlua_global_refs->nil_ref); \ } \ } while (0) @@ -495,7 +495,7 @@ static bool typval_conv_special = false; nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); \ } else { \ lua_createtable(lstate, 0, 0); \ - nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); \ + nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); \ lua_setmetatable(lstate, -2); \ } \ } while (0) @@ -734,7 +734,7 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special } else { lua_createtable(lstate, 0, (int)dict.size); if (dict.size == 0 && !special) { - nlua_pushref(lstate, nlua_get_empty_dict_ref(lstate)); + nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); lua_setmetatable(lstate, -2); } } @@ -782,7 +782,7 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special) if (special) { lua_pushnil(lstate); } else { - nlua_pushref(lstate, nlua_get_nil_ref(lstate)); + nlua_pushref(lstate, nlua_global_refs->nil_ref); } break; case kObjectTypeLuaRef: { @@ -1199,14 +1199,14 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) case LUA_TFUNCTION: if (ref) { - *cur.obj = LUAREF_OBJ(nlua_ref(lstate, -1)); + *cur.obj = LUAREF_OBJ(nlua_ref_global(lstate, -1)); } else { goto type_error; } break; case LUA_TUSERDATA: { - nlua_pushref(lstate, nlua_get_nil_ref(lstate)); + nlua_pushref(lstate, nlua_global_refs->nil_ref); bool is_nil = lua_rawequal(lstate, -2, -1); lua_pop(lstate, 1); if (is_nil) { @@ -1240,7 +1240,7 @@ type_error: LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err) { - LuaRef rv = nlua_ref(lstate, -1); + LuaRef rv = nlua_ref_global(lstate, -1); lua_pop(lstate, 1); return rv; } diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 38f21a933e..d207f48435 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -52,15 +52,6 @@ typedef struct { String lua_err_str; } LuaError; -typedef struct { - LuaRef nil_ref; - LuaRef empty_dict_ref; - int ref_count; -#if __has_feature(address_sanitizer) - PMap(handle_T) ref_markers; -#endif -} nlua_ref_state_t; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.c.generated.h" # include "lua/vim_module.generated.h" @@ -296,7 +287,7 @@ static void nlua_schedule_event(void **argv) LuaRef cb = (LuaRef)(ptrdiff_t)argv[0]; lua_State *const lstate = global_lstate; nlua_pushref(lstate, cb); - nlua_unref(lstate, cb); + nlua_unref_global(lstate, cb); if (nlua_pcall(lstate, 0, 0)) { nlua_error(lstate, _("Error executing vim.schedule lua callback: %.*s")); } @@ -313,7 +304,7 @@ static int nlua_schedule(lua_State *const lstate) return lua_error(lstate); } - LuaRef cb = nlua_ref(lstate, 1); + LuaRef cb = nlua_ref_global(lstate, 1); multiqueue_put(main_loop.events, nlua_schedule_event, 1, (void *)(ptrdiff_t)cb); @@ -438,6 +429,9 @@ static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread) memset(ref_state, 0, sizeof(*ref_state)); ref_state->nil_ref = LUA_NOREF; ref_state->empty_dict_ref = LUA_NOREF; + if (!is_thread) { + nlua_global_refs = ref_state; + } return ref_state; } @@ -465,17 +459,9 @@ LuaRef nlua_get_empty_dict_ref(lua_State *lstate) return ref_state->empty_dict_ref; } -static int nlua_get_ref_count(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL -{ - nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); - return ref_state->ref_count; -} - int nlua_get_global_ref_count(void) { - lua_State *const lstate = global_lstate; - return nlua_get_ref_count(lstate); + return nlua_global_refs->ref_count; } static void nlua_common_vim_init(lua_State *lstate, bool is_thread) @@ -496,7 +482,7 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_pushcfunction(lstate, &nlua_nil_tostring); lua_setfield(lstate, -2, "__tostring"); lua_setmetatable(lstate, -2); - ref_state->nil_ref = nlua_ref(lstate, -1); + ref_state->nil_ref = nlua_ref(lstate, ref_state, -1); lua_pushvalue(lstate, -1); lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.NIL"); lua_setfield(lstate, -2, "NIL"); @@ -505,7 +491,7 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_createtable(lstate, 0, 0); lua_pushcfunction(lstate, &nlua_empty_dict_tostring); lua_setfield(lstate, -2, "__tostring"); - ref_state->empty_dict_ref = nlua_ref(lstate, -1); + ref_state->empty_dict_ref = nlua_ref(lstate, ref_state, -1); lua_pushvalue(lstate, -1); lua_setfield(lstate, LUA_REGISTRYINDEX, "mpack.empty_dict"); lua_setfield(lstate, -2, "_empty_dict_mt"); @@ -784,11 +770,11 @@ void nlua_free_all_mem(void) static void nlua_common_free_all_mem(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - nlua_unref(lstate, nlua_get_nil_ref(lstate)); - nlua_unref(lstate, nlua_get_empty_dict_ref(lstate)); + nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + nlua_unref(lstate, ref_state, ref_state->nil_ref); + nlua_unref(lstate, ref_state, ref_state->empty_dict_ref); #ifdef NLUA_TRACK_REFS - nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); if (ref_state->ref_count) { fprintf(stderr, "%d lua references were leaked!", ref_state->ref_count); } @@ -1095,10 +1081,8 @@ static int nlua_getenv(lua_State *lstate) /// add the value to the registry /// The current implementation does not support calls from threads. -LuaRef nlua_ref(lua_State *lstate, int index) +LuaRef nlua_ref(lua_State *lstate, nlua_ref_state_t *ref_state, int index) { - nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); - lua_pushvalue(lstate, index); LuaRef ref = luaL_ref(lstate, LUA_REGISTRYINDEX); if (ref > 0) { @@ -1113,11 +1097,15 @@ LuaRef nlua_ref(lua_State *lstate, int index) return ref; } -/// remove the value from the registry -void nlua_unref(lua_State *lstate, LuaRef ref) + +LuaRef nlua_ref_global(lua_State *lstate, int index) { - nlua_ref_state_t *ref_state = nlua_get_ref_state(lstate); + return nlua_ref(lstate, nlua_global_refs, index); +} +/// remove the value from the registry +void nlua_unref(lua_State *lstate, nlua_ref_state_t *ref_state, LuaRef ref) +{ if (ref > 0) { ref_state->ref_count--; #ifdef NLUA_TRACK_REFS @@ -1130,9 +1118,15 @@ void nlua_unref(lua_State *lstate, LuaRef ref) } } +void nlua_unref_global(lua_State *lstate, LuaRef ref) +{ + nlua_unref(lstate, nlua_global_refs, ref); +} + + void api_free_luaref(LuaRef ref) { - nlua_unref(global_lstate, ref); + nlua_unref_global(global_lstate, ref); } /// push a value referenced in the registry @@ -1154,7 +1148,7 @@ LuaRef api_new_luaref(LuaRef original_ref) lua_State *const lstate = global_lstate; nlua_pushref(lstate, original_ref); - LuaRef new_ref = nlua_ref(lstate, -1); + LuaRef new_ref = nlua_ref_global(lstate, -1); lua_pop(lstate, 1); return new_ref; } @@ -1707,7 +1701,7 @@ void nlua_CFunction_func_free(void *state) lua_State *const lstate = global_lstate; LuaCFunctionState *funcstate = (LuaCFunctionState *)state; - nlua_unref(lstate, funcstate->lua_callable.func_ref); + nlua_unref_global(lstate, funcstate->lua_callable.func_ref); xfree(funcstate); } @@ -1757,7 +1751,7 @@ char_u *nlua_register_table_as_callable(typval_T *const arg) lua_pop(lstate, 2); // [table] LuaCFunctionState *state = xmalloc(sizeof(LuaCFunctionState)); - state->lua_callable.func_ref = nlua_ref(lstate, -1); + state->lua_callable.func_ref = nlua_ref_global(lstate, -1); char_u *name = register_cfunc(&nlua_CFunction_func_call, &nlua_CFunction_func_free, state); diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 6fca8e6c09..957c61f2f9 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -15,6 +15,15 @@ // Generated by msgpack-gen.lua void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; +typedef struct { + LuaRef nil_ref; + LuaRef empty_dict_ref; + int ref_count; +#if __has_feature(address_sanitizer) + PMap(handle_T) ref_markers; +#endif +} nlua_ref_state_t; + #define set_api_error(s, err) \ do { \ Error *err_ = (err); \ @@ -35,4 +44,7 @@ void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.h.generated.h" #endif + +EXTERN nlua_ref_state_t *nlua_global_refs INIT(= NULL); + #endif // NVIM_LUA_EXECUTOR_H -- cgit From 850b3e19c9fc8d84d960e6932a9ad4f0bcad2a8e Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 26 Feb 2022 11:03:39 +0100 Subject: refactor(lua): cleanup and docs for threads --- src/nvim/lua/stdlib.c | 3 +++ src/nvim/main.c | 2 +- src/nvim/os/fs.c | 3 +++ src/nvim/runtime.c | 33 ++++++++++++--------------------- 4 files changed, 19 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index 6a2aef6683..c2ce899a74 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -474,6 +474,9 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread) { if (!is_thread) { + // TODO(bfredl): some of basic string functions should already be + // (or be easy to make) threadsafe + // stricmp lua_pushcfunction(lstate, &nlua_stricmp); lua_setfield(lstate, -2, "stricmp"); diff --git a/src/nvim/main.c b/src/nvim/main.c index dfd5e5dbe0..ec64e407b2 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -157,7 +157,7 @@ void early_init(mparm_T *paramp) eval_init(); // init global variables init_path(argv0 ? argv0 : "nvim"); init_normal_cmds(); // Init the table of Normal mode commands. - runtime_search_path_init(); + runtime_init(); highlight_init(); #ifdef WIN32 diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index cca76e175d..daf974ee74 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -64,6 +64,9 @@ void fs_init(void) uv_mutex_init_recursive(&fs_loop_mutex); } +/// TODO(bfredl): some of these operations should +/// be possible to do the private libuv loop of the +/// thread, instead of contending the global fs loop void fs_loop_lock(void) { uv_mutex_lock(&fs_loop_mutex); diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 4497e2b055..1ec3e40abe 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -27,21 +27,11 @@ static RuntimeSearchPath runtime_search_path; static RuntimeSearchPath runtime_search_path_thread; static uv_mutex_t runtime_search_path_mutex; -void runtime_search_path_init(void) +void runtime_init(void) { uv_mutex_init(&runtime_search_path_mutex); } -void runtime_search_path_lock(void) -{ - uv_mutex_lock(&runtime_search_path_mutex); -} - -void runtime_search_path_unlock(void) -{ - uv_mutex_unlock(&runtime_search_path_mutex); -} - /// ":runtime [what] {name}" void ex_runtime(exarg_T *eap) { @@ -330,8 +320,9 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) { int ref; RuntimeSearchPath path = runtime_search_path_get_cached(&ref); + static char buf[MAXPATHL]; - ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path); + ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf); runtime_search_path_unref(path, &ref); return rv; @@ -339,18 +330,19 @@ ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all) ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all) { - runtime_search_path_lock(); - ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread); - runtime_search_path_unlock(); + // TODO(bfredl): avoid contention between multiple worker threads? + uv_mutex_lock(&runtime_search_path_mutex); + static char buf[MAXPATHL]; + ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread, + buf, sizeof buf); + uv_mutex_unlock(&runtime_search_path_mutex); return rv; } ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, - RuntimeSearchPath path) + RuntimeSearchPath path, char *buf, size_t buf_len) { ArrayOf(String) rv = ARRAY_DICT_INIT; - size_t buf_len = MAXPATHL; - char *buf = xmalloc(MAXPATHL); for (size_t i = 0; i < kv_size(path); i++) { SearchPathItem *item = &kv_A(path, i); if (lua) { @@ -380,7 +372,6 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all, } } done: - xfree(buf); return rv; } @@ -614,10 +605,10 @@ void runtime_search_path_validate(void) runtime_search_path = runtime_search_path_build(); runtime_search_path_valid = true; runtime_search_path_ref = NULL; // initially unowned - runtime_search_path_lock(); + uv_mutex_lock(&runtime_search_path_mutex); runtime_search_path_free(runtime_search_path_thread); runtime_search_path_thread = copy_runtime_search_path(runtime_search_path); - runtime_search_path_unlock(); + uv_mutex_unlock(&runtime_search_path_mutex); } } -- cgit From f6cc604af2a4d95ec9bcaa5bee705cec2e06d541 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 26 Feb 2022 13:27:43 +0000 Subject: fix(api): convert blob to NUL-terminated API string Looks like I did an oopsie; although API strings carry a size field, they should still be usable as C-strings! (even though they may contain embedded NULs) --- src/nvim/api/private/converter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 3d4ff202fe..49e3cf7df7 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -57,7 +57,7 @@ typedef struct { const size_t len_ = (size_t)(len); \ const blob_T *const blob_ = (blob); \ kvi_push(edata->stack, STRING_OBJ(((String) { \ - .data = len_ != 0 ? xmemdup(blob_->bv_ga.ga_data, len_) : NULL, \ + .data = len_ != 0 ? xmemdupz(blob_->bv_ga.ga_data, len_) : xstrdup(""), \ .size = len_ \ }))); \ } while (0) -- cgit From da89725f340ac702cc6199962fa82ea8edcbede8 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sat, 26 Feb 2022 13:06:34 +0100 Subject: fix(coverity/175977): big parameter passed by value --- src/nvim/viml/parser/expressions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 8a14710351..9d1318724e 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -1592,7 +1592,7 @@ typedef struct { /// string is a regex. /// @param[in] is_invalid Whether currently processed token is not valid. static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const node, - const LexExprToken token, const ExprASTStack ast_stack, + const LexExprToken token, const ExprASTStack *ast_stack, const bool is_invalid) FUNC_ATTR_NONNULL_ALL { @@ -2907,7 +2907,7 @@ viml_pexpr_parse_no_paren_closing_error: {} ? kExprNodeDoubleQuotedString : kExprNodeSingleQuotedString)); *top_node_p = cur_node; - parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid); + parse_quoted_string(pstate, cur_node, cur_token, &ast_stack, is_invalid); want_node = kENodeOperator; break; } -- cgit From 24557a7f63aeff924df745a4393bc397d2bba267 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sat, 26 Feb 2022 23:36:55 +0100 Subject: test(ci): remove non-existent ci-fold from tests --- src/nvim/testdir/runnvim.sh | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh index fdd3f3144b..72d88f9f93 100755 --- a/src/nvim/testdir/runnvim.sh +++ b/src/nvim/testdir/runnvim.sh @@ -65,9 +65,6 @@ main() {( fi fi fi - if test "$FAILED" = 1 ; then - ci_fold start "$test_name" - fi valgrind_check . if test -n "$LOG_DIR" ; then check_sanitizer "$LOG_DIR" @@ -77,9 +74,6 @@ main() {( cat "$tlog" fi rm -f "$tlog" - if test "$FAILED" = 1 ; then - ci_fold end "" - fi if test "$FAILED" = 1 ; then echo "Test $test_name failed, see output above and summary for more details" >> test.log # When Neovim crashed/aborted it might not have created messages. -- cgit From 0347875a5c11258ebb6377a1ab79b04fe9c55bc9 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Fri, 7 Jan 2022 17:28:14 +0600 Subject: feat: ignore nore on maps --- src/nvim/getchar.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 34cde9a7c4..5b181da50e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1710,6 +1710,15 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) int keylen = *keylenp; int i; int local_State = get_real_state(); + bool is_plug_map = false; + + // Check if typehead starts with a mapping. + // In that case we will ignore nore flag on it. + if (typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL + && typebuf.tb_buf[typebuf.tb_off+1] == KS_EXTRA + && typebuf.tb_buf[typebuf.tb_off+2] == KE_PLUG) { + is_plug_map = true; + } // Check for a mappable key sequence. // Walk through one maphash[] list until we find an entry that matches. @@ -1725,7 +1734,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) tb_c1 = typebuf.tb_buf[typebuf.tb_off]; if (no_mapping == 0 && maphash_valid && (no_zero_mapping == 0 || tb_c1 != '0') - && (typebuf.tb_maplen == 0 + && (typebuf.tb_maplen == 0 || is_plug_map || (p_remap && !(typebuf.tb_noremap[typebuf.tb_off] & (RM_NONE|RM_ABBR)))) && !(p_paste && (State & (INSERT + CMDLINE))) @@ -1813,7 +1822,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) break; } } - if (n >= 0) { + if (!is_plug_map && n >= 0) { continue; } -- cgit From 1b5767aa3480c0cdc43f7a4b78f36a14e85a182f Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sun, 27 Feb 2022 14:35:06 -0500 Subject: feat(lua): add to user commands callback (#17522) Works similar to ex . It only splits the arguments if the command has more than one posible argument. In cases were the command can only have 1 argument opts.fargs = { opts.args } --- src/nvim/api/vim.c | 2 ++ src/nvim/ex_docmd.c | 24 ++++++++++++++++++++++++ src/nvim/lua/executor.c | 25 ++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index cd9d61ed24..302dccbde7 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2415,6 +2415,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * /// from Lua, the command can also be a Lua function. The function is called with a /// single table argument that contains the following keys: /// - args: (string) The args passed to the command, if any || +/// - fargs: (table) The args split by unescaped whitespace (when more than one +/// argument is allowed), if any || /// - bang: (boolean) "true" if the command was executed with a ! modifier || /// - line1: (number) The starting line of the command range || /// - line2: (number) The final line of the command range || diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 48749afcb3..152a41c407 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5802,6 +5802,30 @@ static void ex_delcommand(exarg_T *eap) } } +/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback. +/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator. +/// +/// @note If no separator is found start = 0 and end = length - 1 +/// @param[in] arg String to split +/// @param[in] iter Iteration counter +/// @param[out] start Start of the split +/// @param[out] end End of the split +/// @param[in] length Length of the string +/// @return false if it's the last split (don't call again), true otherwise (call again). +bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int length) +{ + int pos; + *start = *end + (iter > 1 ? 2 : 0); // Skip whitespace after the first split + for (pos = *start; pos < length - 2; pos++) { + if (arg[pos] != '\\' && ascii_iswhite(arg[pos + 1])) { + *end = pos; + return true; + } + } + *end = length - 1; + return false; +} + /* * split and quote args for */ diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index d207f48435..3c1676581c 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1814,8 +1814,31 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) lua_pushinteger(lstate, eap->line2); lua_setfield(lstate, -2, "line2"); + lua_newtable(lstate); // f-args table lua_pushstring(lstate, (const char *)eap->arg); - lua_setfield(lstate, -2, "args"); + lua_pushvalue(lstate, -1); // Reference for potential use on f-args + lua_setfield(lstate, -4, "args"); + + // Split args by unescaped whitespace || (nargs dependent) + if (cmd->uc_argt & EX_NOSPC) { + // Commands where nargs = 1 or "?" fargs is the same as args + lua_rawseti(lstate, -2, 1); + } else { + // Commands with more than one possible argument we split + lua_pop(lstate, 1); // Pop the reference of opts.args + int length = (int)STRLEN(eap->arg); + int start = 0; + int end = 0; + int i = 1; + bool res = true; + while (res) { + res = uc_split_args_iter(eap->arg, i, &start, &end, length); + lua_pushlstring(lstate, (const char *)eap->arg + start, (size_t)(end - start + 1)); + lua_rawseti(lstate, -2, i); + i++; + } + } + lua_setfield(lstate, -2, "fargs"); lua_pushstring(lstate, (const char *)&eap->regname); lua_setfield(lstate, -2, "reg"); -- cgit From 991e472881bf29805982b402c1a010cde051ded3 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Fri, 28 May 2021 15:45:34 -0400 Subject: feat(lua): add api and lua autocmds --- src/nvim/api/autocmd.c | 669 +++++++++++++++++++ src/nvim/api/autocmd.h | 11 + src/nvim/api/keysets.lua | 29 + src/nvim/api/private/dispatch.c | 20 +- src/nvim/api/private/helpers.c | 34 +- src/nvim/api/private/helpers.h | 17 + src/nvim/aucmd.c | 123 ---- src/nvim/aucmd.h | 11 - src/nvim/autocmd.c | 1347 +++++++++++++++++++++++++++------------ src/nvim/autocmd.h | 44 +- src/nvim/diff.c | 9 +- src/nvim/eval.c | 12 +- src/nvim/eval/funcs.c | 1 + src/nvim/eval/typval.c | 19 +- src/nvim/eval/typval.h | 3 + src/nvim/ex_cmds.c | 2 +- src/nvim/ex_cmds_defs.h | 30 + src/nvim/ex_getln.c | 4 +- src/nvim/fileio.c | 2 +- src/nvim/globals.h | 20 +- src/nvim/keymap.h | 3 + src/nvim/lua/executor.c | 10 + src/nvim/lua/executor.h | 8 - src/nvim/main.c | 2 +- src/nvim/map.c | 1 + src/nvim/map.h | 1 + src/nvim/tui/input.c | 4 +- src/nvim/ui.c | 2 +- 28 files changed, 1834 insertions(+), 604 deletions(-) create mode 100644 src/nvim/api/autocmd.c create mode 100644 src/nvim/api/autocmd.h delete mode 100644 src/nvim/aucmd.c delete mode 100644 src/nvim/aucmd.h (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c new file mode 100644 index 0000000000..deb8ec8cf3 --- /dev/null +++ b/src/nvim/api/autocmd.c @@ -0,0 +1,669 @@ +#include +#include + +#include "lauxlib.h" +#include "nvim/api/autocmd.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/eval/typval.h" +#include "nvim/fileio.h" +#include "nvim/lua/executor.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/autocmd.c.generated.h" +#endif + +#define AUCMD_MAX_PATTERNS 256 + +// Check whether every item in the array is a kObjectTypeString +#define CHECK_STRING_ARRAY(__array, k, v, goto_name) \ + for (size_t j = 0; j < __array.size; j++) { \ + Object item = __array.items[j]; \ + if (item.type != kObjectTypeString) { \ + api_set_error(err, \ + kErrorTypeValidation, \ + "All entries in '%s' must be strings", \ + k); \ + goto goto_name; \ + } \ + } + +// Copy string or array of strings into an empty array. +#define UNPACK_STRING_OR_ARRAY(__array, k, v, goto_name) \ + if (v->type == kObjectTypeString) { \ + ADD(__array, copy_object(*v)); \ + } else if (v->type == kObjectTypeArray) { \ + CHECK_STRING_ARRAY(__array, k, v, goto_name); \ + __array = copy_array(v->data.array); \ + } else { \ + api_set_error(err, \ + kErrorTypeValidation, \ + "'%s' must be an array or a string.", \ + k); \ + goto goto_name; \ + } + +// Get the event number, unless it is an error. Then goto `goto_name`. +#define GET_ONE_EVENT(event_nr, event_str, goto_name) \ + char_u *__next_ev; \ + event_T event_nr = \ + event_name2nr((char_u *)event_str.data.string.data, &__next_ev); \ + if (event_nr >= NUM_EVENTS) { \ + api_set_error(err, kErrorTypeValidation, "unexpected event"); \ + goto goto_name; \ + } + + +// ID for associating autocmds created via nvim_create_autocmd +// Used to delete autocmds from nvim_del_autocmd +static int64_t next_autocmd_id = 1; + +/// Get autocmds that match the requirements passed to {opts}. +/// group +/// event +/// pattern +/// +/// -- @param {string} event - event or events to match against +/// vim.api.nvim_get_autocmds({ event = "FileType" }) +/// +Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) + FUNC_API_SINCE(9) +{ + Array autocmd_list = ARRAY_DICT_INIT; + char_u *pattern_filters[AUCMD_MAX_PATTERNS]; + char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + + bool event_set[NUM_EVENTS] = { false }; + bool check_event = false; + + int group = 0; + + if (opts->group.type != kObjectTypeNil) { + Object v = opts->group; + if (v.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "group must be a string."); + goto cleanup; + } + + group = augroup_find(v.data.string.data); + + if (group < 0) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + } + + if (opts->event.type != kObjectTypeNil) { + check_event = true; + + Object v = opts->event; + if (v.type == kObjectTypeString) { + GET_ONE_EVENT(event_nr, v, cleanup); + event_set[event_nr] = true; + } else if (v.type == kObjectTypeArray) { + FOREACH_ITEM(v.data.array, event_v, { + if (event_v.type != kObjectTypeString) { + api_set_error(err, + kErrorTypeValidation, + "Every event must be a string in 'event'"); + goto cleanup; + } + + GET_ONE_EVENT(event_nr, event_v, cleanup); + event_set[event_nr] = true; + }) + } else { + api_set_error(err, + kErrorTypeValidation, + "Not a valid 'event' value. Must be a string or an array"); + goto cleanup; + } + } + + int pattern_filter_count = 0; + if (opts->pattern.type != kObjectTypeNil) { + Object v = opts->pattern; + if (v.type == kObjectTypeString) { + pattern_filters[pattern_filter_count] = (char_u *)v.data.string.data; + pattern_filter_count += 1; + } else if (v.type == kObjectTypeArray) { + FOREACH_ITEM(v.data.array, item, { + pattern_filters[pattern_filter_count] = (char_u *)item.data.string.data; + pattern_filter_count += 1; + }); + } else { + api_set_error(err, + kErrorTypeValidation, + "Not a valid 'pattern' value. Must be a string or an array"); + goto cleanup; + } + + if (pattern_filter_count >= AUCMD_MAX_PATTERNS) { + api_set_error(err, + kErrorTypeValidation, + "Too many patterns. Please limit yourself to less"); + goto cleanup; + } + } + + FOR_ALL_AUEVENTS(event) { + if (check_event && !event_set[event]) { + continue; + } + + for (AutoPat *ap = au_get_autopat_for_event(event); + ap != NULL; + ap = ap->next) { + if (ap == NULL || ap->cmds == NULL) { + continue; + } + + // Skip autocmds from invalid groups if passed. + if (group != 0 && ap->group != group) { + continue; + } + + // Skip 'pattern' from invalid patterns if passed. + if (pattern_filter_count > 0) { + bool passed = false; + for (int i = 0; i < pattern_filter_count; i++) { + assert(i < AUCMD_MAX_PATTERNS); + assert(pattern_filters[i]); + + char_u *pat = pattern_filters[i]; + int patlen = (int)STRLEN(pat); + + if (aupat_is_buflocal(pat, patlen)) { + aupat_normalize_buflocal_pat(pattern_buflocal, + pat, + patlen, + aupat_get_buflocal_nr(pat, patlen)); + + pat = pattern_buflocal; + } + + if (strequal((char *)ap->pat, (char *)pat)) { + passed = true; + break; + } + } + + if (!passed) { + continue; + } + } + + for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { + if (aucmd_exec_is_deleted(ac->exec)) { + continue; + } + Dictionary autocmd_info = ARRAY_DICT_INIT; + + if (ap->group != AUGROUP_DEFAULT) { + PUT(autocmd_info, "group", INTEGER_OBJ(ap->group)); + } + + if (ac->id > 0) { + PUT(autocmd_info, "id", INTEGER_OBJ(ac->id)); + } + + if (ac->desc != NULL) { + PUT(autocmd_info, "desc", CSTR_TO_OBJ(ac->desc)); + } + + PUT(autocmd_info, + "command", + STRING_OBJ(cstr_to_string(aucmd_exec_to_string(ac, ac->exec)))); + + PUT(autocmd_info, + "pattern", + STRING_OBJ(cstr_to_string((char *)ap->pat))); + + PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); + + if (ap->buflocal_nr) { + PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(true)); + PUT(autocmd_info, "buffer", INTEGER_OBJ(ap->buflocal_nr)); + } else { + PUT(autocmd_info, "buflocal", BOOLEAN_OBJ(false)); + } + + // TODO(sctx): It would be good to unify script_ctx to actually work with lua + // right now it's just super weird, and never really gives you the info that + // you would expect from this. + // + // I think we should be able to get the line number, filename, etc. from lua + // when we're executing something, and it should be easy to then save that + // info here. + // + // I think it's a big loss not getting line numbers of where options, autocmds, + // etc. are set (just getting "Sourced (lua)" or something is not that helpful. + // + // Once we do that, we can put these into the autocmd_info, but I don't think it's + // useful to do that at this time. + // + // PUT(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid)); + // PUT(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum)); + + ADD(autocmd_list, DICTIONARY_OBJ(autocmd_info)); + } + } + } + +cleanup: + return autocmd_list; +} + +/// Define an autocmd. +/// @param opts Dictionary +/// Required keys: +/// event: string | ArrayOf(string) +/// event = "pat1,pat2,pat3", +/// event = "pat1" +/// event = {"pat1"} +/// event = {"pat1", "pat2", "pat3"} +/// +/// +/// -- @param {string} name - augroup name +/// -- @param {string | table} event - event or events to match against +/// -- @param {string | table} pattern - pattern or patterns to match against +/// -- @param {string | function} callback - function or string to execute on autocmd +/// -- @param {string} command - optional, vimscript command +/// Eg. command = "let g:value_set = v:true" +/// -- @param {boolean} once - optional, defaults to false +/// +/// -- pattern = comma delimited list of patterns | pattern | { pattern, ... } +/// +/// pattern = "*.py,*.pyi" +/// pattern = "*.py" +/// pattern = {"*.py"} +/// pattern = { "*.py", "*.pyi" } +/// +/// -- not supported +/// pattern = {"*.py,*.pyi"} +/// +/// -- event = string | string[] +/// event = "FileType,CursorHold" +/// event = "BufPreWrite" +/// event = {"BufPostWrite"} +/// event = {"CursorHold", "BufPreWrite", "BufPostWrite"} +Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Error *err) + FUNC_API_SINCE(9) +{ + int64_t autocmd_id = -1; + + const char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + int au_group = AUGROUP_DEFAULT; + char *desc = NULL; + + Array patterns = ARRAY_DICT_INIT; + Array event_array = ARRAY_DICT_INIT; + + AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; + Callback cb = CALLBACK_NONE; + + if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "cannot pass both: 'callback' and 'command' for the same autocmd"); + goto cleanup; + } else if (opts->callback.type != kObjectTypeNil) { + // TODO(tjdevries): It's possible we could accept callable tables, + // but we don't do that many other places, so for the moment let's + // not do that. + + Object *callback = &opts->callback; + if (callback->type == kObjectTypeLuaRef) { + if (callback->data.luaref == LUA_NOREF) { + api_set_error(err, + kErrorTypeValidation, + "must pass an actual value"); + goto cleanup; + } + + if (!nlua_ref_is_function(callback->data.luaref)) { + api_set_error(err, + kErrorTypeValidation, + "must pass a function for callback"); + goto cleanup; + } + + cb.type = kCallbackLua; + cb.data.luaref = api_new_luaref(callback->data.luaref); + } else if (callback->type == kObjectTypeString) { + cb.type = kCallbackFuncref; + cb.data.funcref = vim_strsave((char_u *)callback->data.string.data); + } else { + api_set_error(err, + kErrorTypeException, + "'callback' must be a lua function or name of vim function"); + goto cleanup; + } + + aucmd.type = CALLABLE_CB; + aucmd.callable.cb = cb; + } else if (opts->command.type != kObjectTypeNil) { + Object *command = &opts->command; + if (command->type == kObjectTypeString) { + aucmd.type = CALLABLE_EX; + aucmd.callable.cmd = vim_strsave((char_u *)command->data.string.data); + } else { + api_set_error(err, + kErrorTypeValidation, + "'command' must be a string"); + goto cleanup; + } + } else { + api_set_error(err, kErrorTypeValidation, "must pass one of: 'command', 'callback'"); + goto cleanup; + } + + if (opts->event.type != kObjectTypeNil) { + UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) + } + + bool is_once = api_object_to_bool(opts->once, "once", false, err); + bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); + + // TOOD: accept number for namespace instead + if (opts->group.type != kObjectTypeNil) { + Object *v = &opts->group; + if (v->type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "'group' must be a string"); + goto cleanup; + } + + au_group = augroup_find(v->data.string.data); + + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeException, + "invalid augroup: %s", v->data.string.data); + + goto cleanup; + } + } + + if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + goto cleanup; + } else if (opts->pattern.type != kObjectTypeNil) { + Object *v = &opts->pattern; + + if (v->type == kObjectTypeString) { + char_u *pat = (char_u *)v->data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } else if (v->type == kObjectTypeArray) { + CHECK_STRING_ARRAY(patterns, "pattern", v, cleanup); + + Array array = v->data.array; + for (size_t i = 0; i < array.size; i++) { + char_u *pat = (char_u *)array.items[i].data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } + } else { + api_set_error(err, + kErrorTypeValidation, + "'pattern' must be a string"); + goto cleanup; + } + } else if (opts->buffer.type != kObjectTypeNil) { + if (opts->buffer.type != kObjectTypeInteger) { + api_set_error(err, + kErrorTypeValidation, + "'buffer' must be an integer"); + goto cleanup; + } + + buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err); + if (ERROR_SET(err)) { + goto cleanup; + } + + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); + ADD(patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); + } + + if (aucmd.type == CALLABLE_NONE) { + api_set_error(err, + kErrorTypeValidation, + "'command' or 'callback' is required"); + goto cleanup; + } + + if (opts->desc.type != kObjectTypeNil) { + if (opts->desc.type == kObjectTypeString) { + desc = opts->desc.data.string.data; + } else { + api_set_error(err, + kErrorTypeValidation, + "'desc' must be a string"); + goto cleanup; + } + } + + if (patterns.size == 0) { + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING("*"))); + } + + if (event_array.size == 0) { + api_set_error(err, kErrorTypeValidation, "'event' is a required key"); + goto cleanup; + } + + autocmd_id = next_autocmd_id++; + FOREACH_ITEM(event_array, event_str, { + GET_ONE_EVENT(event_nr, event_str, cleanup); + + int retval; + + for (size_t i = 0; i < patterns.size; i++) { + Object pat = patterns.items[i]; + + // See: TODO(sctx) + WITH_SCRIPT_CONTEXT(channel_id, { + retval = autocmd_register(autocmd_id, + event_nr, + (char_u *)pat.data.string.data, + (int)pat.data.string.size, + au_group, + is_once, + is_nested, + desc, + aucmd); + }); + + if (retval == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to set autocmd"); + goto cleanup; + } + } + }); + + +cleanup: + aucmd_exec_free(&aucmd); + api_free_array(event_array); + api_free_array(patterns); + + return autocmd_id; +} + +/// Delete an autocmd by ID. Autocmds only return IDs when created +/// via the API. +/// +/// @param id Integer The ID returned by nvim_create_autocmd +void nvim_del_autocmd(Integer id) + FUNC_API_SINCE(9) +{ + autocmd_delete_id(id); +} + +/// Create or get an augroup. +/// +/// To get an existing augroup ID, do: +///
+///     local id = vim.api.nvim_create_augroup({ name = name, clear = false });
+/// 
+/// +/// @param opts Parameters +/// - name (string): The name of the augroup +/// - clear (bool): Whether to clear existing commands or not. +// Defaults to true. +/// See |autocmd-groups| +Integer nvim_create_augroup(uint64_t channel_id, Dict(create_augroup) *opts, Error *err) + FUNC_API_SINCE(9) +{ + bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err); + + if (opts->name.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "'name' is required and must be a string"); + return -1; + } + char *name = opts->name.data.string.data; + + int augroup = -1; + WITH_SCRIPT_CONTEXT(channel_id, { + augroup = augroup_add(name); + if (augroup == AUGROUP_ERROR) { + api_set_error(err, kErrorTypeException, "Failed to set augroup"); + return -1; + } + + if (clear_autocmds) { + FOR_ALL_AUEVENTS(event) { + aupat_del_for_event_and_group(event, augroup); + } + } + }); + + return augroup; +} + +/// NOTE: behavior differs from augroup-delete. +/// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. +/// This augroup will no longer exist +void nvim_del_augroup_by_id(Integer id) + FUNC_API_SINCE(9) +{ + char *name = augroup_name((int)id); + augroup_del(name, false); +} + +/// NOTE: behavior differs from augroup-delete. +/// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. +/// This augroup will no longer exist +void nvim_del_augroup_by_name(String name) + FUNC_API_SINCE(9) +{ + augroup_del(name.data, false); +} + +/// -- @param {string} group - autocmd group name +/// -- @param {number} buffer - buffer number +/// -- @param {string | table} event - event or events to match against +/// -- @param {string | table} pattern - optional, defaults to "*". +/// vim.api.nvim_do_autcmd({ group, buffer, pattern, event, modeline }) +void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) + FUNC_API_SINCE(9) +{ + int au_group = AUGROUP_ALL; + bool modeline = true; + + buf_T *buf = curbuf; + bool set_buf = false; + + char_u *pattern = NULL; + bool set_pattern = false; + + Array event_array = ARRAY_DICT_INIT; + + if (opts->group.type != kObjectTypeNil) { + if (opts->group.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "'group' must be a string"); + goto cleanup; + } + + au_group = augroup_find(opts->group.data.string.data); + + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeException, + "invalid augroup: %s", opts->group.data.string.data); + + goto cleanup; + } + } + + if (opts->buffer.type != kObjectTypeNil) { + Object buf_obj = opts->buffer; + if (buf_obj.type != kObjectTypeInteger && buf_obj.type != kObjectTypeBuffer) { + api_set_error(err, kErrorTypeException, "invalid buffer: %d", buf_obj.type); + goto cleanup; + } + + buf = find_buffer_by_handle((Buffer)buf_obj.data.integer, err); + set_buf = true; + + if (ERROR_SET(err)) { + goto cleanup; + } + } + + if (opts->pattern.type != kObjectTypeNil) { + if (opts->pattern.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "'pattern' must be a string"); + goto cleanup; + } + + pattern = vim_strsave((char_u *)opts->pattern.data.string.data); + set_pattern = true; + } + + if (opts->event.type != kObjectTypeNil) { + UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) + } + + if (opts->modeline.type != kObjectTypeNil) { + modeline = api_object_to_bool(opts->modeline, "modeline", true, err); + } + + if (set_pattern && set_buf) { + api_set_error(err, kErrorTypeValidation, "must not set 'buffer' and 'pattern'"); + goto cleanup; + } + + bool did_aucmd = false; + FOREACH_ITEM(event_array, event_str, { + GET_ONE_EVENT(event_nr, event_str, cleanup) + + did_aucmd |= apply_autocmds_group(event_nr, pattern, NULL, true, au_group, buf, NULL); + }) + + if (did_aucmd && modeline) { + do_modelines(0); + } + +cleanup: + api_free_array(event_array); + XFREE_CLEAR(pattern); +} + + +#undef UNPACK_STRING_OR_ARRAY +#undef CHECK_STRING_ARRAY +#undef GET_ONE_EVENT diff --git a/src/nvim/api/autocmd.h b/src/nvim/api/autocmd.h new file mode 100644 index 0000000000..f9432830d9 --- /dev/null +++ b/src/nvim/api/autocmd.h @@ -0,0 +1,11 @@ +#ifndef NVIM_API_AUTOCMD_H +#define NVIM_API_AUTOCMD_H + +#include + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/autocmd.h.generated.h" +#endif +#endif // NVIM_API_AUTOCMD_H diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 45a57b9257..be71c446b1 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -110,5 +110,34 @@ return { "reverse"; "nocombine"; }; + -- Autocmds + create_autocmd = { + "buffer"; + "callback"; + "command"; + "desc"; + "event"; + "group"; + "once"; + "nested"; + "pattern"; + }; + do_autocmd = { + "buffer"; + "event"; + "group"; + "modeline"; + "pattern"; + }; + get_autocmds = { + "event"; + "group"; + "id"; + "pattern"; + }; + create_augroup = { + "clear"; + "name"; + }; } diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 8ab7743e01..f670f06357 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -6,22 +6,30 @@ #include #include -#include "nvim/api/buffer.h" #include "nvim/api/deprecated.h" -#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" +#include "nvim/log.h" +#include "nvim/map.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/vim.h" + +// =========================================================================== +// NEW API FILES MUST GO HERE. +// +// When creating a new API file, you must include it here, +// so that the dispatcher can find the C functions that you are creating! +// =========================================================================== +#include "nvim/api/autocmd.h" +#include "nvim/api/buffer.h" +#include "nvim/api/extmark.h" #include "nvim/api/tabpage.h" #include "nvim/api/ui.h" #include "nvim/api/vim.h" #include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" -#include "nvim/log.h" -#include "nvim/map.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/vim.h" static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 971fa1cb0f..3d4a04f096 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -396,19 +396,14 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object stringval = value.data.string.data; } - const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_sid = - channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; - current_sctx.sc_lnum = 0; - current_channel_id = channel_id; - - const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) - ? 0 : (type == SREQ_GLOBAL) - ? OPT_GLOBAL : OPT_LOCAL; - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); - - current_sctx = save_current_sctx; + WITH_SCRIPT_CONTEXT(channel_id, { + const int opt_flags = (type == SREQ_WIN && !(flags & SOPT_GLOBAL)) + ? 0 : (type == SREQ_GLOBAL) + ? OPT_GLOBAL : OPT_LOCAL; + + set_option_value_for(name.data, numval, stringval, + opt_flags, type, to, err); + }); } buf_T *find_buffer_by_handle(Buffer buffer, Error *err) @@ -1614,3 +1609,16 @@ err: NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); } + +int find_sid(uint64_t channel_id) +{ + switch (channel_id) { + case VIML_INTERNAL_CALL: + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; + case LUA_INTERNAL_CALL: + return SID_LUA; + default: + return SID_API_CLIENT; + } +} diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 6d0aec9c90..6969994c3b 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -138,10 +138,27 @@ typedef struct { msg_list = saved_msg_list; /* Restore the exception context. */ \ } while (0) +// Useful macro for executing some `code` for each item in an array. +#define FOREACH_ITEM(a, __foreach_item, code) \ + for (size_t __foreach_i = 0; __foreach_i < (a).size; __foreach_i++) { \ + Object __foreach_item = (a).items[__foreach_i]; \ + code; \ + } + + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" # include "keysets.h.generated.h" #endif +#define WITH_SCRIPT_CONTEXT(channel_id, code) \ + const sctx_T save_current_sctx = current_sctx; \ + current_sctx.sc_sid = \ + (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ + current_sctx.sc_lnum = 0; \ + current_channel_id = channel_id; \ + code; \ + current_sctx = save_current_sctx; + #endif // NVIM_API_PRIVATE_HELPERS_H diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c deleted file mode 100644 index d7f73fa4a1..0000000000 --- a/src/nvim/aucmd.c +++ /dev/null @@ -1,123 +0,0 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - -#include "nvim/aucmd.h" -#include "nvim/buffer.h" -#include "nvim/eval.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/fileio.h" -#include "nvim/main.h" -#include "nvim/os/os.h" -#include "nvim/ui.h" -#include "nvim/vim.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "aucmd.c.generated.h" -#endif - -void do_autocmd_uienter(uint64_t chanid, bool attached) -{ - static bool recursive = false; - - if (recursive) { - return; // disallow recursion - } - recursive = true; - - save_v_event_T save_v_event; - dict_T *dict = get_v_event(&save_v_event); - assert(chanid < VARNUMBER_MAX); - tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid); - tv_dict_set_keys_readonly(dict); - apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, - NULL, NULL, false, curbuf); - restore_v_event(dict, &save_v_event); - - recursive = false; -} - -void init_default_autocmds(void) -{ - // open terminals when opening files that start with term:// -#define PROTO "term://" - do_cmdline_cmd("augroup nvim_terminal"); - do_cmdline_cmd("autocmd BufReadCmd " PROTO "* ++nested " - "if !exists('b:term_title')|call termopen(" - // Capture the command string - "matchstr(expand(\"\"), " - "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " - // capture the working directory - "{'cwd': expand(get(matchlist(expand(\"\"), " - "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" - "|endif"); - do_cmdline_cmd("augroup END"); -#undef PROTO - - // limit syntax synchronization in the command window - do_cmdline_cmd("augroup nvim_cmdwin"); - do_cmdline_cmd("autocmd! CmdwinEnter [:>] syntax sync minlines=1 maxlines=1"); - do_cmdline_cmd("augroup END"); -} - -static void focusgained_event(void **argv) -{ - bool *gainedp = argv[0]; - do_autocmd_focusgained(*gainedp); - xfree(gainedp); -} -void aucmd_schedule_focusgained(bool gained) -{ - bool *gainedp = xmalloc(sizeof(*gainedp)); - *gainedp = gained; - loop_schedule_deferred(&main_loop, - event_create(focusgained_event, 1, gainedp)); -} - -static void do_autocmd_focusgained(bool gained) -{ - static bool recursive = false; - static Timestamp last_time = (time_t)0; - bool need_redraw = false; - - if (recursive) { - return; // disallow recursion - } - recursive = true; - need_redraw |= apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST), - NULL, NULL, false, curbuf); - - // When activated: Check if any file was modified outside of Vim. - // Only do this when not done within the last two seconds as: - // 1. Some filesystems have modification time granularity in seconds. Fat32 - // has a granularity of 2 seconds. - // 2. We could get multiple notifications in a row. - if (gained && last_time + (Timestamp)2000 < os_now()) { - need_redraw = check_timestamps(true); - last_time = os_now(); - } - - if (need_redraw) { - // Something was executed, make sure the cursor is put back where it - // belongs. - need_wait_return = false; - - if (State & CMDLINE) { - redrawcmdline(); - } else if ((State & NORMAL) || (State & INSERT)) { - if (must_redraw != 0) { - update_screen(0); - } - - setcursor(); - } - - ui_flush(); - } - - if (need_maketitle) { - maketitle(); - } - - recursive = false; -} diff --git a/src/nvim/aucmd.h b/src/nvim/aucmd.h deleted file mode 100644 index 9a4dd79a78..0000000000 --- a/src/nvim/aucmd.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NVIM_AUCMD_H -#define NVIM_AUCMD_H - -#include - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "aucmd.h.generated.h" -#endif - -#endif // NVIM_AUCMD_H - diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 9117dde089..a9e1978ac0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2,10 +2,15 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com // autocmd.c: Autocommand related functions +#include +#include "nvim/autocmd.h" + +// #include "nvim/api/private/handle.h" + +#include "lauxlib.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" -#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -13,8 +18,11 @@ #include "nvim/eval.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/lua/executor.h" +#include "nvim/map.h" #include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/regexp.h" @@ -28,6 +36,14 @@ # include "autocmd.c.generated.h" #endif +// Naming Conventions: +// - general autocmd behavior start with au_ +// - AutoCmd start with aucmd_ +// - Autocmd.exec stat with aucmd_exec +// - AutoPat start with aupat_ +// - Groups start with augroup_ +// - Events start with event_ + // // The autocommands are stored in a list for each event. // Autocommands for the same pattern, that are consecutive, are joined @@ -67,21 +83,20 @@ // Code for automatic commands. static AutoPatCmd *active_apc_list = NULL; // stack of active autocommands -/// List of autocmd group names -static garray_T augroups = { 0, 0, sizeof(char_u *), 10, NULL }; -#define AUGROUP_NAME(i) (((char **)augroups.ga_data)[i]) -#define BUFLOCAL_PAT_LEN 25 +// ID for associating autocmds created via nvim_create_autocmd +// Used to delete autocmds from nvim_del_autocmd +static int next_augroup_id = 1; // use get_deleted_augroup() to get this static const char *deleted_augroup = NULL; -// The ID of the current group. Group 0 is the default one. +// The ID of the current group. static int current_augroup = AUGROUP_DEFAULT; -static int au_need_clean = false; // need to delete marked patterns +// Whether we need to delete marked patterns. +// While deleting autocmds, they aren't actually remover, just marked. +static int au_need_clean = false; -static event_T last_event; -static int last_group; static int autocmd_blocked = 0; // block all autocmds static bool autocmd_nested = false; @@ -89,6 +104,33 @@ static bool autocmd_include_groups = false; static char_u *old_termresponse = NULL; +/// Iterates over all the AutoPats for a particular event +#define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ + for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT + +/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested +#define ARG_GET_FLAG(errored, cmd, flag, pattern, len) \ + if (STRNCMP(cmd, pattern, len) == 0 && ascii_iswhite((cmd)[len])) { \ + if (flag) { \ + semsg(_(e_duparg2), pattern); \ + (errored) = true; \ + } \ + (flag) = true; \ + (cmd) = skipwhite((cmd) + (len)); \ + } + +// Map of autocmd group names. +// name -> ID +static Map(String, int) augroup_map = MAP_INIT; + +static void augroup_map_del(char *name) +{ + String key = map_key(String, int)(&augroup_map, cstr_as_string(name)); + map_del(String, int)(&augroup_map, key); + api_free_string(key); +} + + static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE { if (deleted_augroup == NULL) { @@ -98,7 +140,7 @@ static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE } // Show the autocommands for one AutoPat. -static void show_autocmd(AutoPat *ap, event_T event) +static void aupat_show(AutoPat *ap) { AutoCmd *ac; @@ -107,39 +149,21 @@ static void show_autocmd(AutoPat *ap, event_T event) if (got_int) { return; } + // pattern has been removed if (ap->pat == NULL) { return; } - msg_putchar('\n'); - if (got_int) { - return; - } - if (event != last_event || ap->group != last_group) { - if (ap->group != AUGROUP_DEFAULT) { - if (AUGROUP_NAME(ap->group) == NULL) { - msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E)); - } else { - msg_puts_attr(AUGROUP_NAME(ap->group), HL_ATTR(HLF_T)); - } - msg_puts(" "); - } - msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T)); - last_event = event; - last_group = ap->group; - msg_putchar('\n'); - if (got_int) { - return; - } - } msg_col = 4; msg_outtrans(ap->pat); for (ac = ap->cmds; ac != NULL; ac = ac->next) { - if (ac->cmd == NULL) { // skip removed commands + // skip removed commands + if (aucmd_exec_is_deleted(ac->exec)) { continue; } + if (msg_col >= 14) { msg_putchar('\n'); } @@ -147,7 +171,7 @@ static void show_autocmd(AutoPat *ap, event_T event) if (got_int) { return; } - msg_outtrans(ac->cmd); + msg_outtrans((char_u *)aucmd_exec_to_string(ac, ac->exec)); if (p_verbose > 0) { last_set_msg(ac->script_ctx); } @@ -163,27 +187,96 @@ static void show_autocmd(AutoPat *ap, event_T event) } } +static void au_show_for_all_events(int group) +{ + FOR_ALL_AUEVENTS(event) { + au_show_for_event(group, event); + } +} + +static void au_show_for_event(int group, event_T event) +{ + // Return early if there are no autocmds for this event + if (au_event_is_empty(event)) { + return; + } + + int previous_group = AUGROUP_ERROR; + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + if (group != AUGROUP_ALL && group != ap->group) { + continue; + } + + char *name = augroup_name(ap->group); + + msg_putchar('\n'); + // When switching groups, we need to show the new group information. + if (ap->group != previous_group) { + // show the group name, if it's not the default group + if (ap->group != AUGROUP_DEFAULT) { + if (name == NULL) { + msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E)); + } else { + msg_puts_attr(name, HL_ATTR(HLF_T)); + } + msg_puts(" "); + } + + // show the event name + msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T)); + msg_putchar('\n'); + } + + if (got_int) { + return; + } + + aupat_show(ap); + + previous_group = ap->group; + } +} + // Mark an autocommand handler for deletion. -static void au_remove_pat(AutoPat *ap) +static void aupat_del(AutoPat *ap) { XFREE_CLEAR(ap->pat); ap->buflocal_nr = -1; au_need_clean = true; } +void aupat_del_for_event_and_group(event_T event, int group) +{ + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + if (ap->group == group) { + aupat_del(ap); + } + } + + au_need_clean = true; + au_cleanup(); // may really delete removed patterns/commands now +} + // Mark all commands for a pattern for deletion. -static void au_remove_cmds(AutoPat *ap) +static void aupat_remove_cmds(AutoPat *ap) { for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { - XFREE_CLEAR(ac->cmd); + aucmd_exec_free(&ac->exec); + + if (ac->desc != NULL) { + XFREE_CLEAR(ac->desc); + } } au_need_clean = true; } // Delete one command from an autocmd pattern. -static void au_del_cmd(AutoCmd *ac) +static void aucmd_del(AutoCmd *ac) { - XFREE_CLEAR(ac->cmd); + aucmd_exec_free(&ac->exec); + if (ac->desc != NULL) { + XFREE_CLEAR(ac->desc); + } au_need_clean = true; } @@ -191,16 +284,15 @@ static void au_del_cmd(AutoCmd *ac) /// This is only done when not executing autocommands. static void au_cleanup(void) { - AutoPat *ap, **prev_ap; - event_T event; + AutoPat *ap; + AutoPat **prev_ap; if (autocmd_busy || !au_need_clean) { return; } // Loop over all events. - for (event = (event_T)0; (int)event < NUM_EVENTS; - event = (event_T)((int)event + 1)) { + FOR_ALL_AUEVENTS(event) { // Loop over all autocommand patterns. prev_ap = &(first_autopat[(int)event]); for (ap = *prev_ap; ap != NULL; ap = *prev_ap) { @@ -211,9 +303,13 @@ static void au_cleanup(void) for (AutoCmd *ac = *prev_ac; ac != NULL; ac = *prev_ac) { // Remove the command if the pattern is to be deleted or when // the command has been marked for deletion. - if (ap->pat == NULL || ac->cmd == NULL) { + if (ap->pat == NULL || aucmd_exec_is_deleted(ac->exec)) { *prev_ac = ac->next; - xfree(ac->cmd); + aucmd_exec_free(&ac->exec); + if (ac->desc != NULL) { + XFREE_CLEAR(ac->desc); + } + xfree(ac); } else { has_cmd = true; @@ -224,7 +320,7 @@ static void au_cleanup(void) if (ap->pat != NULL && !has_cmd) { // Pattern was not marked for deletion, but all of its commands were. // So mark the pattern for deletion. - au_remove_pat(ap); + aupat_del(ap); } // Remove the pattern if it has been marked for deletion. @@ -250,12 +346,17 @@ static void au_cleanup(void) au_need_clean = false; } + +// Get the first AutoPat for a particular event. +AutoPat *au_get_autopat_for_event(event_T event) +{ + return first_autopat[(int)event]; +} + // Called when buffer is freed, to remove/invalidate related buffer-local // autocmds. void aubuflocal_remove(buf_T *buf) { - AutoPat *ap; - event_T event; AutoPatCmd *apc; // invalidate currently executing autocommands @@ -266,12 +367,11 @@ void aubuflocal_remove(buf_T *buf) } // invalidate buflocals looping through events - for (event = (event_T)0; (int)event < NUM_EVENTS; - event = (event_T)((int)event + 1)) { - // loop over all autocommand patterns - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { + FOR_ALL_AUEVENTS(event) { + FOR_ALL_AUPATS_IN_EVENT(event, ap) { if (ap->buflocal_nr == buf->b_fnum) { - au_remove_pat(ap); + aupat_del(ap); + if (p_verbose >= 6) { verbose_enter(); smsg(_("auto-removing autocommand: %s "), @@ -284,60 +384,61 @@ void aubuflocal_remove(buf_T *buf) au_cleanup(); } -// Add an autocmd group name. +// Add an autocmd group name or return existing group matching name. // Return its ID. -static int au_new_group(char_u *name) +int augroup_add(char *name) { - int i = au_find_group(name); - if (i == AUGROUP_ERROR) { // the group doesn't exist yet, add it. - // First try using a free entry. - for (i = 0; i < augroups.ga_len; i++) { - if (AUGROUP_NAME(i) == NULL) { - break; - } - } - if (i == augroups.ga_len) { - ga_grow(&augroups, 1); - } + assert(STRICMP(name, "end") != 0); - AUGROUP_NAME(i) = xstrdup((char *)name); - if (i == augroups.ga_len) { - augroups.ga_len++; - } + int existing_id = augroup_find(name); + if (existing_id > 0) { + assert(existing_id != AUGROUP_DELETED); + return existing_id; } - return i; + if (existing_id == AUGROUP_DELETED) { + augroup_map_del(name); + } + + int next_id = next_augroup_id++; + String name_copy = cstr_to_string(name); + map_put(String, int)(&augroup_map, name_copy, next_id); + + return next_id; } -static void au_del_group(char_u *name) +/// Delete the augroup that matches name. +void augroup_del(char *name, bool stupid_legacy_mode) { - int i = au_find_group(name); + int i = augroup_find(name); if (i == AUGROUP_ERROR) { // the group doesn't exist semsg(_("E367: No such group: \"%s\""), name); } else if (i == current_augroup) { emsg(_("E936: Cannot delete the current group")); } else { - event_T event; - AutoPat *ap; - int in_use = false; - - for (event = (event_T)0; (int)event < NUM_EVENTS; - event = (event_T)((int)event + 1)) { - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { - if (ap->group == i && ap->pat != NULL) { - give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true); - in_use = true; - event = NUM_EVENTS; - break; + if (stupid_legacy_mode) { + FOR_ALL_AUEVENTS(event) { + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + if (ap->group == i && ap->pat != NULL) { + give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true); + map_put(String, int)(&augroup_map, cstr_as_string(name), AUGROUP_DELETED); + return; + } } } - } - xfree(AUGROUP_NAME(i)); - if (in_use) { - AUGROUP_NAME(i) = (char *)get_deleted_augroup(); } else { - AUGROUP_NAME(i) = NULL; + FOR_ALL_AUEVENTS(event) { + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + if (ap->group == i) { + aupat_del(ap); + } + } + } } + + // Remove the group because it's not currently in use. + augroup_map_del(name); + au_cleanup(); } } @@ -346,25 +447,70 @@ static void au_del_group(char_u *name) /// @param name augroup name /// /// @return the ID or AUGROUP_ERROR (< 0) for error. -static int au_find_group(const char_u *name) +int augroup_find(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - for (int i = 0; i < augroups.ga_len; i++) { - if (AUGROUP_NAME(i) != NULL && AUGROUP_NAME(i) != get_deleted_augroup() - && STRCMP(AUGROUP_NAME(i), name) == 0) { - return i; - } + int existing_id = map_get(String, int)(&augroup_map, cstr_as_string((char *)name)); + if (existing_id == AUGROUP_DELETED) { + return existing_id; + } + + if (existing_id > 0) { + return existing_id; } + return AUGROUP_ERROR; } +/// Gets the name for a particular group. +char *augroup_name(int group) +{ + assert(group != 0); + + if (group == AUGROUP_DELETED) { + return (char *)get_deleted_augroup(); + } + + if (group == AUGROUP_ALL) { + group = current_augroup; + } + + // next_augroup_id is the "source of truth" about what autocmds have existed + // + // The map_size is not the source of truth because groups can be removed from + // the map. When this happens, the map size is reduced. That's why this function + // relies on next_augroup_id instead. + + // "END" is always considered the last augroup ID. + // Used for expand_get_event_name and expand_get_augroup_name + if (group == next_augroup_id) { + return "END"; + } + + // If it's larger than the largest group, then it doesn't have a name + if (group > next_augroup_id) { + return NULL; + } + + String key; + int value; + map_foreach(&augroup_map, key, value, { + if (value == group) { + return key.data; + } + }); + + // If it's not in the map anymore, then it must have been deleted. + return (char *)get_deleted_augroup(); +} + /// Return true if augroup "name" exists. /// /// @param name augroup name -bool au_has_group(const char_u *name) +bool augroup_exists(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return au_find_group(name) != AUGROUP_ERROR; + return augroup_find(name) > 0; } /// ":augroup {name}". @@ -374,20 +520,27 @@ void do_augroup(char_u *arg, int del_group) if (*arg == NUL) { emsg(_(e_argreq)); } else { - au_del_group(arg); + augroup_del((char *)arg, true); } } else if (STRICMP(arg, "end") == 0) { // ":aug end": back to group 0 current_augroup = AUGROUP_DEFAULT; } else if (*arg) { // ":aug xxx": switch to group xxx - current_augroup = au_new_group(arg); + current_augroup = augroup_add((char *)arg); } else { // ":aug": list the group names msg_start(); - for (int i = 0; i < augroups.ga_len; i++) { - if (AUGROUP_NAME(i) != NULL) { - msg_puts(AUGROUP_NAME(i)); - msg_puts(" "); + + String name; + int value; + map_foreach(&augroup_map, name, value, { + if (value > 0) { + msg_puts(name.data); + } else { + msg_puts(augroup_name(value)); } - } + + msg_puts(" "); + }); + msg_clr_eos(); msg_end(); } @@ -396,25 +549,30 @@ void do_augroup(char_u *arg, int del_group) #if defined(EXITFREE) void free_all_autocmds(void) { - for (current_augroup = -1; current_augroup < augroups.ga_len; - current_augroup++) { - do_autocmd((char_u *)"", true); - } - - for (int i = 0; i < augroups.ga_len; i++) { - char *const s = ((char **)(augroups.ga_data))[i]; - if ((const char *)s != get_deleted_augroup()) { - xfree(s); + FOR_ALL_AUEVENTS(event) { + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + aupat_del(ap); } } - ga_clear(&augroups); + + au_need_clean = true; + au_cleanup(); // may really delete removed patterns/commands now + + // Delete the augroup_map, including free the data + String name; + int id; + map_foreach(&augroup_map, name, id, { + (void)id; + api_free_string(name); + }) + map_destroy(String, int)(&augroup_map); } #endif // Return the event number for event name "start". // Return NUM_EVENTS if the event name was not found. // Return a pointer to the next event name in "end". -static event_T event_name2nr(const char_u *start, char_u **end) +event_T event_name2nr(const char_u *start, char_u **end) { const char_u *p; int i; @@ -457,33 +615,6 @@ static const char *event_nr2name(event_T event) return "Unknown"; } -/// Scan over the events. "*" stands for all events. -/// true when group name was found -static char_u *find_end_event(char_u *arg, int have_group) -{ - char_u *pat; - char_u *p; - - if (*arg == '*') { - if (arg[1] && !ascii_iswhite(arg[1])) { - semsg(_("E215: Illegal character after *: %s"), arg); - return NULL; - } - pat = arg + 1; - } else { - for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) { - if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) { - if (have_group) { - semsg(_("E216: No such event: %s"), pat); - } else { - semsg(_("E216: No such group or event: %s"), pat); - } - return NULL; - } - } - } - return pat; -} /// Return true if "event" is included in 'eventignore'. /// @@ -601,12 +732,12 @@ void do_autocmd(char_u *arg_in, int forceit) group = AUGROUP_ALL; // no argument, use all groups } else { // Check for a legal group name. If not, use AUGROUP_ALL. - group = au_get_grouparg(&arg); + group = arg_augroup_get(&arg); } // Scan over the events. // If we find an illegal name, return here, don't do anything. - pat = find_end_event(arg, group != AUGROUP_ALL); + pat = arg_event_skip(arg, group != AUGROUP_ALL); if (pat == NULL) { return; } @@ -643,37 +774,22 @@ void do_autocmd(char_u *arg_in, int forceit) } cmd = skipwhite(cmd); + + bool invalid_flags = false; for (size_t i = 0; i < 2; i++) { if (*cmd != NUL) { - // Check for "++once" flag. - if (STRNCMP(cmd, "++once", 6) == 0 && ascii_iswhite(cmd[6])) { - if (once) { - semsg(_(e_duparg2), "++once"); - } - once = true; - cmd = skipwhite(cmd + 6); - } + ARG_GET_FLAG(invalid_flags, cmd, once, "++once", 6); + ARG_GET_FLAG(invalid_flags, cmd, nested, "++nested", 8); - // Check for "++nested" flag. - if ((STRNCMP(cmd, "++nested", 8) == 0 && ascii_iswhite(cmd[8]))) { - if (nested) { - semsg(_(e_duparg2), "++nested"); - } - nested = true; - cmd = skipwhite(cmd + 8); - } - - // Check for the old (deprecated) "nested" flag. - if (STRNCMP(cmd, "nested", 6) == 0 && ascii_iswhite(cmd[6])) { - if (nested) { - semsg(_(e_duparg2), "nested"); - } - nested = true; - cmd = skipwhite(cmd + 6); - } + // Check the deprecated "nested" flag. + ARG_GET_FLAG(invalid_flags, cmd, nested, "nested", 6); } } + if (invalid_flags) { + return; + } + // Find the start of the commands. // Expand in it. if (*cmd != NUL) { @@ -685,36 +801,37 @@ void do_autocmd(char_u *arg_in, int forceit) } } + bool is_showing = !forceit && *cmd == NUL; + // Print header when showing autocommands. - if (!forceit && *cmd == NUL) { + if (is_showing) { // Highlight title msg_puts_title(_("\n--- Autocommands ---")); - } - // Loop over the events. - last_event = (event_T)-1; // for listing the event name - last_group = AUGROUP_ERROR; // for listing the group name - if (*arg == '*' || *arg == NUL || *arg == '|') { - if (!forceit && *cmd != NUL) { - emsg(_(e_cannot_define_autocommands_for_all_events)); + if (*arg == '*' || *arg == '|' || *arg == NUL) { + au_show_for_all_events(group); } else { - for (event_T event = (event_T)0; event < NUM_EVENTS; - event = (event_T)(event + 1)) { + event_T event = event_name2nr(arg, &arg); + assert(event < NUM_EVENTS); + au_show_for_event(group, event); + } + } else { + if (*arg == '*' || *arg == NUL || *arg == '|') { + if (!forceit && *cmd != NUL) { + emsg(_(e_cannot_define_autocommands_for_all_events)); + } else { + do_all_autocmd_events(pat, once, nested, cmd, forceit, group); + } + } else { + while (*arg && *arg != '|' && !ascii_iswhite(*arg)) { + event_T event = event_name2nr(arg, &arg); + assert(event < NUM_EVENTS); if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) == FAIL) { break; } } } - } else { - while (*arg && *arg != '|' && !ascii_iswhite(*arg)) { - event_T event = event_name2nr(arg, &arg); - assert(event < NUM_EVENTS); - if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) - == FAIL) { - break; - } - } } if (need_free) { @@ -723,30 +840,14 @@ void do_autocmd(char_u *arg_in, int forceit) xfree(envpat); } -// Find the group ID in a ":autocmd" or ":doautocmd" argument. -// The "argp" argument is advanced to the following argument. -// -// Returns the group ID or AUGROUP_ALL. -static int au_get_grouparg(char_u **argp) +void do_all_autocmd_events(char_u *pat, bool once, int nested, char_u *cmd, bool delete, int group) { - char_u *group_name; - char_u *p; - char_u *arg = *argp; - int group = AUGROUP_ALL; - - for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) { - } - if (p > arg) { - group_name = vim_strnsave(arg, (size_t)(p - arg)); - group = au_find_group(group_name); - if (group == AUGROUP_ERROR) { - group = AUGROUP_ALL; // no match, use all groups - } else { - *argp = skipwhite(p); // match, skip over group name + FOR_ALL_AUEVENTS(event) { + if (do_autocmd_event(event, pat, once, nested, cmd, delete, group) + == FAIL) { + return; } - xfree(group_name); } - return group; } // do_autocmd() for one event. @@ -756,216 +857,293 @@ static int au_get_grouparg(char_u **argp) // If *cmd == NUL: show entries. // If forceit == true: delete entries. // If group is not AUGROUP_ALL: only use this group. -static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u *cmd, - int forceit, int group) +int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u *cmd, bool delete, + int group) + FUNC_ATTR_NONNULL_ALL { + // Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events + assert(*pat != NUL || delete); + AutoPat *ap; AutoPat **prev_ap; - AutoCmd *ac; - AutoCmd **prev_ac; - int brace_level; - char_u *endpat; int findgroup; - int allgroups; int patlen; int is_buflocal; int buflocal_nr; char_u buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" + bool is_adding_cmd = *cmd != NUL; + if (group == AUGROUP_ALL) { findgroup = current_augroup; } else { findgroup = group; } - allgroups = (group == AUGROUP_ALL && !forceit && *cmd == NUL); - // Show or delete all patterns for an event. - if (*pat == NUL) { - for (ap = first_autopat[event]; ap != NULL; ap = ap->next) { - if (forceit) { // delete the AutoPat, if it's in the current group - if (ap->group == findgroup) { - au_remove_pat(ap); - } - } else if (group == AUGROUP_ALL || ap->group == group) { - show_autocmd(ap, event); - } - } + // Delete all aupat for an event. + if (*pat == NUL && delete) { + aupat_del_for_event_and_group(event, findgroup); + return OK; } // Loop through all the specified patterns. - for (; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat)) { - // Find end of the pattern. - // Watch out for a comma in braces, like "*.\{obj,o\}". - endpat = pat; - // ignore single comma - if (*endpat == ',') { - continue; - } - brace_level = 0; - for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\'); - endpat++) { - if (*endpat == '{') { - brace_level++; - } else if (*endpat == '}') { - brace_level--; - } - } - patlen = (int)(endpat - pat); - - // detect special buffer-local patterns - is_buflocal = false; + patlen = (int)aucmd_pattern_length(pat); + while (patlen) { + // detect special buffer-local patterns + is_buflocal = aupat_is_buflocal(pat, patlen); buflocal_nr = 0; - if (patlen >= 8 && STRNCMP(pat, "') { - // "": Error will be printed only for addition. - // printing and removing will proceed silently. - is_buflocal = true; - if (patlen == 8) { - // "" - buflocal_nr = curbuf->b_fnum; - } else if (patlen > 9 && pat[7] == '=') { - if (patlen == 13 && STRNICMP(pat, "", 13) == 0) { - // "" - buflocal_nr = autocmd_bufnr; - } else if (skipdigits(pat + 8) == pat + patlen - 1) { - // "" - buflocal_nr = atoi((char *)pat + 8); - } - } - } - if (is_buflocal) { + buflocal_nr = aupat_get_buflocal_nr(pat, patlen); + // normalize pat into standard "#N" form - snprintf((char *)buflocal_pat, - BUFLOCAL_PAT_LEN, - "", - buflocal_nr); + aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr); - pat = buflocal_pat; // can modify pat and patlen - patlen = (int)STRLEN(buflocal_pat); // but not endpat + pat = buflocal_pat; + patlen = (int)STRLEN(buflocal_pat); } - // Find AutoPat entries with this pattern. When adding a command it - // always goes at or after the last one, so start at the end. - if (!forceit && *cmd != NUL && last_autopat[(int)event] != NULL) { - prev_ap = &last_autopat[(int)event]; - } else { + if (delete) { + assert(*pat != NUL); + + // Find AutoPat entries with this pattern. + // always goes at or after the last one, so start at the end. prev_ap = &first_autopat[(int)event]; - } - while ((ap = *prev_ap) != NULL) { - if (ap->pat != NULL) { - // Accept a pattern when: - // - a group was specified and it's that group, or a group was - // not specified and it's the current group, or a group was - // not specified and we are listing - // - the length of the pattern matches - // - the pattern matches. - // For , this condition works because we normalize - // all buffer-local patterns. - if ((allgroups || ap->group == findgroup) && ap->patlen == patlen - && STRNCMP(pat, ap->pat, patlen) == 0) { - // Remove existing autocommands. - // If adding any new autocmd's for this AutoPat, don't - // delete the pattern from the autopat list, append to - // this list. - if (forceit) { - if (*cmd != NUL && ap->next == NULL) { - au_remove_cmds(ap); + while ((ap = *prev_ap) != NULL) { + if (ap->pat != NULL) { + // Accept a pattern when: + // - a group was specified and it's that group + // - the length of the pattern matches + // - the pattern matches. + // For , this condition works because we normalize + // all buffer-local patterns. + if (ap->group == findgroup + && ap->patlen == patlen + && STRNCMP(pat, ap->pat, patlen) == 0) { + // Remove existing autocommands. + // If adding any new autocmd's for this AutoPat, don't + // delete the pattern from the autopat list, append to + // this list. + if (is_adding_cmd && ap->next == NULL) { + aupat_remove_cmds(ap); break; } - au_remove_pat(ap); - } else if (*cmd == NUL) { - // Show autocmd's for this autopat, or buflocals - show_autocmd(ap, event); - } else if (ap->next == NULL) { - // Add autocmd to this autopat, if it's the last one. - break; + aupat_del(ap); } } + prev_ap = &ap->next; } - prev_ap = &ap->next; } - // Add a new command. - if (*cmd != NUL) { - // If the pattern we want to add a command to does appear at the - // end of the list (or not is not in the list at all), add the - // pattern at the end of the list. - if (ap == NULL) { - // refuse to add buffer-local ap if buffer number is invalid - if (is_buflocal - && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) { - semsg(_("E680: : invalid buffer number "), buflocal_nr); - return FAIL; - } + if (is_adding_cmd) { + AucmdExecutable exec = AUCMD_EXECUTABLE_INIT; + exec.type = CALLABLE_EX; + exec.callable.cmd = cmd; + autocmd_register(0, event, pat, patlen, group, once, nested, NULL, exec); + } - ap = xmalloc(sizeof(AutoPat)); - ap->pat = vim_strnsave(pat, (size_t)patlen); - ap->patlen = patlen; + pat = aucmd_next_pattern(pat, (size_t)patlen); + patlen = (int)aucmd_pattern_length(pat); + } - if (is_buflocal) { - ap->buflocal_nr = buflocal_nr; - ap->reg_prog = NULL; - } else { - char_u *reg_pat; + au_cleanup(); + return OK; +} - ap->buflocal_nr = 0; - reg_pat = file_pat_to_reg_pat(pat, endpat, &ap->allow_dirs, true); - if (reg_pat != NULL) { - ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC); - } - xfree(reg_pat); - if (reg_pat == NULL || ap->reg_prog == NULL) { - xfree(ap->pat); - xfree(ap); - return FAIL; - } - } +int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int group, bool once, + bool nested, char *desc, AucmdExecutable aucmd) +{ + // 0 is not a valid group. + assert(group != 0); - // need to initialize last_mode for the first ModeChanged autocmd - if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) { - xfree(last_mode); - last_mode = get_mode(); - } + AutoPat *ap; + AutoPat **prev_ap; + AutoCmd *ac; + AutoCmd **prev_ac; + int is_buflocal; + int buflocal_nr; + int findgroup; + char_u buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" - // If the event is CursorMoved, update the last cursor position - // position to avoid immediately triggering the autocommand - if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) { - curwin->w_last_cursormoved = curwin->w_cursor; - } + if (patlen > (int)STRLEN(pat)) { + return FAIL; + } - ap->cmds = NULL; - *prev_ap = ap; - last_autopat[(int)event] = ap; - ap->next = NULL; - if (group == AUGROUP_ALL) { - ap->group = current_augroup; - } else { - ap->group = group; + if (group == AUGROUP_ALL) { + findgroup = current_augroup; + } else { + findgroup = group; + } + + + // detect special buffer-local patterns + is_buflocal = aupat_is_buflocal(pat, patlen); + buflocal_nr = 0; + + if (is_buflocal) { + buflocal_nr = aupat_get_buflocal_nr(pat, patlen); + + // normalize pat into standard "#N" form + aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, buflocal_nr); + + pat = buflocal_pat; + patlen = (int)STRLEN(buflocal_pat); + } + + if (last_autopat[(int)event] != NULL) { + prev_ap = &last_autopat[(int)event]; + } else { + prev_ap = &first_autopat[(int)event]; + } + + while ((ap = *prev_ap) != NULL) { + if (ap->pat != NULL) { + // Accept a pattern when: + // - a group was specified and it's that group + // - the length of the pattern matches + // - the pattern matches. + // For , this condition works because we normalize + // all buffer-local patterns. + if (ap->group == findgroup + && ap->patlen == patlen + && STRNCMP(pat, ap->pat, patlen) == 0) { + if (ap->next == NULL) { + // Add autocmd to this autopat, if it's the last one. + break; } } + } + prev_ap = &ap->next; + } + + // If the pattern we want to add a command to does appear at the + // end of the list (or not is not in the list at all), add the + // pattern at the end of the list. + if (ap == NULL) { + // refuse to add buffer-local ap if buffer number is invalid + if (is_buflocal + && (buflocal_nr == 0 || buflist_findnr(buflocal_nr) == NULL)) { + semsg(_("E680: : invalid buffer number "), buflocal_nr); + return FAIL; + } + + ap = xmalloc(sizeof(AutoPat)); + ap->pat = vim_strnsave(pat, (size_t)patlen); + ap->patlen = patlen; + + if (is_buflocal) { + ap->buflocal_nr = buflocal_nr; + ap->reg_prog = NULL; + } else { + char_u *reg_pat; - // Add the autocmd at the end of the AutoCmd list. - prev_ac = &(ap->cmds); - while ((ac = *prev_ac) != NULL) { - prev_ac = &ac->next; + ap->buflocal_nr = 0; + reg_pat = file_pat_to_reg_pat(pat, pat + patlen, &ap->allow_dirs, true); + if (reg_pat != NULL) { + ap->reg_prog = vim_regcomp(reg_pat, RE_MAGIC); + } + xfree(reg_pat); + if (reg_pat == NULL || ap->reg_prog == NULL) { + xfree(ap->pat); + xfree(ap); + return FAIL; } - ac = xmalloc(sizeof(AutoCmd)); - ac->cmd = vim_strsave(cmd); - ac->script_ctx = current_sctx; - ac->script_ctx.sc_lnum += sourcing_lnum; - ac->next = NULL; - *prev_ac = ac; - ac->once = once; - ac->nested = nested; + } + + // need to initialize last_mode for the first ModeChanged autocmd + if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) { + xfree(last_mode); + last_mode = get_mode(); + } + + // If the event is CursorMoved, update the last cursor position + // position to avoid immediately triggering the autocommand + if (event == EVENT_CURSORMOVED && !has_event(EVENT_CURSORMOVED)) { + curwin->w_last_cursormoved = curwin->w_cursor; + } + + ap->cmds = NULL; + *prev_ap = ap; + last_autopat[(int)event] = ap; + ap->next = NULL; + if (group == AUGROUP_ALL) { + ap->group = current_augroup; + } else { + ap->group = group; } } - au_cleanup(); // may really delete removed patterns/commands now + // Add the autocmd at the end of the AutoCmd list. + prev_ac = &(ap->cmds); + while ((ac = *prev_ac) != NULL) { + prev_ac = &ac->next; + } + + ac = xmalloc(sizeof(AutoCmd)); + *prev_ac = ac; + + ac->id = id; + ac->exec = aucmd_exec_copy(aucmd); + ac->script_ctx = current_sctx; + ac->script_ctx.sc_lnum += sourcing_lnum; + ac->next = NULL; + ac->once = once; + ac->nested = nested; + ac->desc = NULL; + + // TODO(tjdevries): What to do about :autocmd and where/how to show lua stuffs there. + // perhaps: DESCRIPTION or similar + if (desc != NULL) { + ac->desc = xstrdup(desc); + } else { + ac->desc = aucmd_exec_default_desc(aucmd); + } + return OK; } +size_t aucmd_pattern_length(char_u *pat) +{ + if (*pat == NUL) { + return 0; + } + + char_u *endpat; + + for (; *pat; pat = (*endpat == ',' ? endpat + 1 : endpat)) { + // Find end of the pattern. + // Watch out for a comma in braces, like "*.\{obj,o\}". + endpat = pat; + // ignore single comma + if (*endpat == ',') { + continue; + } + int brace_level = 0; + for (; *endpat && (*endpat != ',' || brace_level || endpat[-1] == '\\'); + endpat++) { + if (*endpat == '{') { + brace_level++; + } else if (*endpat == '}') { + brace_level--; + } + } + + return (size_t)(endpat - pat); + } + + return STRLEN(pat); +} + +char_u *aucmd_next_pattern(char_u *pat, size_t patlen) +{ + pat = pat + patlen; + if (*pat == ',') { + pat = pat + 1; + } + + return pat; +} + /// Implementation of ":doautocmd [group] event [fname]". /// Return OK for success, FAIL for failure; /// @@ -981,7 +1159,7 @@ int do_doautocmd(char_u *arg, bool do_msg, bool *did_something) } // Check for a legal group name. If not, use AUGROUP_ALL. - group = au_get_grouparg(&arg); + group = arg_augroup_get(&arg); if (*arg == '*') { emsg(_("E217: Can't execute autocommands for ALL events")); @@ -990,7 +1168,7 @@ int do_doautocmd(char_u *arg, bool do_msg, bool *did_something) // Scan over the events. // If we find an illegal name, return here, don't do anything. - fname = find_end_event(arg, group != AUGROUP_ALL); + fname = arg_event_skip(arg, group != AUGROUP_ALL); if (fname == NULL) { return FAIL; } @@ -1378,8 +1556,8 @@ bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT /// @param eap Ex command arguments /// /// @return true if some commands were executed. -static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool force, - int group, buf_T *buf, exarg_T *eap) +bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool force, int group, + buf_T *buf, exarg_T *eap) { char_u *sfname = NULL; // short file name char_u *tail; @@ -1402,7 +1580,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, proftime_T wait_time; bool did_save_redobuff = false; save_redo_T save_redo; - const bool save_KeyTyped = KeyTyped; + const bool save_KeyTyped = KeyTyped; // NOLINT // Quickly return if there are no autocommands for this event or // autocommands are blocked. @@ -1814,6 +1992,11 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last) /// @return allocated string, or NULL for end of autocommands. char_u *getnextac(int c, void *cookie, int indent, bool do_concat) { + // These arguments are required for do_cmdline. + (void)c; + (void)indent; + (void)do_concat; + AutoPatCmd *acp = (AutoPatCmd *)cookie; char_u *retval; AutoCmd *ac; @@ -1826,7 +2009,8 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) // repeat until we find an autocommand to execute for (;;) { // skip removed commands - while (acp->nextcmd != NULL && acp->nextcmd->cmd == NULL) { + while (acp->nextcmd != NULL + && aucmd_exec_is_deleted(acp->nextcmd->exec)) { if (acp->nextcmd->last) { acp->nextcmd = NULL; } else { @@ -1856,14 +2040,32 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) if (p_verbose >= 9) { verbose_enter_scroll(); - smsg(_("autocommand %s"), ac->cmd); + smsg(_("autocommand %s"), aucmd_exec_to_string(ac, ac->exec)); msg_puts("\n"); // don't overwrite this either verbose_leave_scroll(); } - retval = vim_strsave(ac->cmd); + + if (ac->exec.type == CALLABLE_CB) { + typval_T argsin = TV_INITIAL_VALUE; + typval_T rettv = TV_INITIAL_VALUE; + callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv); + + // Major Hack Alert: + // We just return "not-null" and continue going. + // This would be a good candidate for a refactor. You would need to refactor: + // 1. do_cmdline to accept something besides a string + // OR + // 2. make where we call do_cmdline for autocmds not have to return anything, + // and instead we loop over all the matches and just execute one-by-one. + // However, my expectation woudl be that could be expensive. + retval = vim_strsave((char_u *)""); + } else { + retval = vim_strsave(ac->exec.callable.cmd); + } + // Remove one-shot ("once") autocmd in anticipation of its execution. if (ac->once) { - au_del_cmd(ac); + aucmd_del(ac); } autocmd_nested = ac->nested; current_sctx = ac->script_ctx; @@ -1928,19 +2130,12 @@ bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSE // Function given to ExpandGeneric() to obtain the list of autocommand group // names. -char_u *get_augroup_name(expand_T *xp, int idx) +char_u *expand_get_augroup_name(expand_T *xp, int idx) { - if (idx == augroups.ga_len) { // add "END" add the end - return (char_u *)"END"; - } - if (idx >= augroups.ga_len) { // end of list - return NULL; - } - if (AUGROUP_NAME(idx) == NULL || AUGROUP_NAME(idx) == get_deleted_augroup()) { - // skip deleted entries - return (char_u *)""; - } - return (char_u *)AUGROUP_NAME(idx); + // Required for ExpandGeneric + (void)xp; + + return (char_u *)augroup_name(idx + 1); } /// @param doautocmd true for :doauto*, false for :autocmd @@ -1952,7 +2147,7 @@ char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd) // check for a group name, skip it if present autocmd_include_groups = false; p = arg; - group = au_get_grouparg(&arg); + group = arg_augroup_get(&arg); // If there only is a group name that's what we expand. if (*arg == NUL && group != AUGROUP_ALL && !ascii_iswhite(arg[-1])) { @@ -1993,16 +2188,25 @@ char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd) } // Function given to ExpandGeneric() to obtain the list of event names. -char_u *get_event_name(expand_T *xp, int idx) +char_u *expand_get_event_name(expand_T *xp, int idx) { - if (idx < augroups.ga_len) { // First list group names, if wanted - if (!autocmd_include_groups || AUGROUP_NAME(idx) == NULL - || AUGROUP_NAME(idx) == get_deleted_augroup()) { - return (char_u *)""; // skip deleted entries + // xp is a required parameter to be used with ExpandGeneric + (void)xp; + + + // List group names + char *name = augroup_name(idx + 1); + if (name != NULL) { + // skip when not including groups or skip deleted entries + if (!autocmd_include_groups || name == get_deleted_augroup()) { + return (char_u *)""; } - return (char_u *)AUGROUP_NAME(idx); + + return (char_u *)name; } - return (char_u *)event_names[idx - augroups.ga_len].name; + + // List event names + return (char_u *)event_names[idx - next_augroup_id].name; } /// Check whether given autocommand is supported @@ -2045,7 +2249,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT } // First, look for an autocmd group name. - group = au_find_group((char_u *)arg_save); + group = augroup_find(arg_save); char *event_name; if (group == AUGROUP_ERROR) { // Didn't match a group name, assume the first argument is an event. @@ -2109,3 +2313,346 @@ theend: xfree(arg_save); return retval; } + +// Checks if a pattern is buflocal +bool aupat_is_buflocal(char_u *pat, int patlen) +{ + return patlen >= 8 + && STRNCMP(pat, "'; +} + +int aupat_get_buflocal_nr(char_u *pat, int patlen) +{ + assert(aupat_is_buflocal(pat, patlen)); + + // "" + if (patlen == 8) { + return curbuf->b_fnum; + } + + if (patlen > 9 && (pat)[7] == '=') { + // "" + if (patlen == 13 && STRNICMP(pat, "", 13) == 0) { + return autocmd_bufnr; + } + + // "" + if (skipdigits(pat + 8) == pat + patlen - 1) { + return atoi((char *)pat + 8); + } + } + + return 0; +} + +// normalize buffer pattern +void aupat_normalize_buflocal_pat(char_u *dest, char_u *pat, int patlen, int buflocal_nr) +{ + assert(aupat_is_buflocal(pat, patlen)); + + if (buflocal_nr == 0) { + buflocal_nr = curbuf->handle; + } + + // normalize pat into standard "#N" form + snprintf((char *)dest, + BUFLOCAL_PAT_LEN, + "", + buflocal_nr); +} + +int autocmd_delete_event(int group, event_T event, char_u *pat) + FUNC_ATTR_NONNULL_ALL +{ + return do_autocmd_event(event, pat, false, false, (char_u *)"", true, group); +} + +/// Deletes an autocmd by ID. +/// Only autocmds created via the API have IDs associated with them. There +/// is no way to delete a specific autocmd created via :autocmd +void autocmd_delete_id(int64_t id) +{ + FOR_ALL_AUEVENTS(event) { + FOR_ALL_AUPATS_IN_EVENT(event, ap) { + for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { + if (ac->id == id) { + aucmd_del(ac); + } + } + } + } +} + +// =========================================================================== +// AucmdExecutable Functions +// =========================================================================== +char *aucmd_exec_default_desc(AucmdExecutable acc) +{ + size_t msglen = 100; + + switch (acc.type) { + case CALLABLE_CB: + switch (acc.callable.cb.type) { + case kCallbackLua: { + char *msg = (char *)xmallocz(msglen); + snprintf(msg, msglen, "", acc.callable.cb.data.luaref); + return msg; + } + case kCallbackFuncref: { + // TODO(tjdevries): Is this enough space for this? + char *msg = (char *)xmallocz(msglen); + snprintf(msg, msglen, "", acc.callable.cb.data.funcref); + return msg; + } + case kCallbackPartial: { + char *msg = (char *)xmallocz(msglen); + snprintf(msg, msglen, "", acc.callable.cb.data.partial->pt_name); + return msg; + } + default: + return NULL; + } + default: + return NULL; + } + + abort(); +} +char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) +{ + switch (acc.type) { + case CALLABLE_EX: + return (char *)acc.callable.cmd; + case CALLABLE_CB: + return ac->desc; + case CALLABLE_NONE: + return "This is not possible"; + } + + abort(); +} + +void aucmd_exec_free(AucmdExecutable *acc) +{ + switch (acc->type) { + case CALLABLE_EX: + XFREE_CLEAR(acc->callable.cmd); + break; + case CALLABLE_CB: + callback_free(&acc->callable.cb); + break; + case CALLABLE_NONE: + return; + } + + acc->type = CALLABLE_NONE; +} + +AucmdExecutable aucmd_exec_copy(AucmdExecutable src) +{ + AucmdExecutable dest = AUCMD_EXECUTABLE_INIT; + + switch (src.type) { + case CALLABLE_EX: + dest.type = CALLABLE_EX; + dest.callable.cmd = vim_strsave(src.callable.cmd); + return dest; + case CALLABLE_CB: + dest.type = CALLABLE_CB; + callback_copy(&dest.callable.cb, &src.callable.cb); + return dest; + case CALLABLE_NONE: + return dest; + } + + abort(); +} + +bool aucmd_exec_is_deleted(AucmdExecutable acc) +{ + switch (acc.type) { + case CALLABLE_EX: + return acc.callable.cmd == NULL; + case CALLABLE_CB: + return callback_is_freed(acc.callable.cb); + case CALLABLE_NONE: + return true; + } + + abort(); +} + +bool au_event_is_empty(event_T event) +{ + return first_autopat[event] == NULL; +} + +// Arg Parsing Functions + +/// Scan over the events. "*" stands for all events. +/// true when group name was found +static char_u *arg_event_skip(char_u *arg, int have_group) +{ + char_u *pat; + char_u *p; + + if (*arg == '*') { + if (arg[1] && !ascii_iswhite(arg[1])) { + semsg(_("E215: Illegal character after *: %s"), arg); + return NULL; + } + pat = arg + 1; + } else { + for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) { + if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) { + if (have_group) { + semsg(_("E216: No such event: %s"), pat); + } else { + semsg(_("E216: No such group or event: %s"), pat); + } + return NULL; + } + } + } + return pat; +} + +// Find the group ID in a ":autocmd" or ":doautocmd" argument. +// The "argp" argument is advanced to the following argument. +// +// Returns the group ID or AUGROUP_ALL. +static int arg_augroup_get(char_u **argp) +{ + char_u *group_name; + char_u *p; + char_u *arg = *argp; + int group = AUGROUP_ALL; + + for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) { + } + if (p > arg) { + group_name = vim_strnsave(arg, (size_t)(p - arg)); + group = augroup_find((char *)group_name); + if (group == AUGROUP_ERROR) { + group = AUGROUP_ALL; // no match, use all groups + } else { + *argp = skipwhite(p); // match, skip over group name + } + xfree(group_name); + } + return group; +} + + +// UI Enter +void do_autocmd_uienter(uint64_t chanid, bool attached) +{ + static bool recursive = false; + + if (recursive) { + return; // disallow recursion + } + recursive = true; + + save_v_event_T save_v_event; + dict_T *dict = get_v_event(&save_v_event); + assert(chanid < VARNUMBER_MAX); + tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid); + tv_dict_set_keys_readonly(dict); + apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE, + NULL, NULL, false, curbuf); + restore_v_event(dict, &save_v_event); + + recursive = false; +} + +// FocusGained + +static void focusgained_event(void **argv) +{ + bool *gainedp = argv[0]; + do_autocmd_focusgained(*gainedp); + xfree(gainedp); +} + +void autocmd_schedule_focusgained(bool gained) +{ + bool *gainedp = xmalloc(sizeof(*gainedp)); + *gainedp = gained; + loop_schedule_deferred(&main_loop, + event_create(focusgained_event, 1, gainedp)); +} + +static void do_autocmd_focusgained(bool gained) +{ + static bool recursive = false; + static Timestamp last_time = (time_t)0; + bool need_redraw = false; + + if (recursive) { + return; // disallow recursion + } + recursive = true; + need_redraw |= apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST), + NULL, NULL, false, curbuf); + + // When activated: Check if any file was modified outside of Vim. + // Only do this when not done within the last two seconds as: + // 1. Some filesystems have modification time granularity in seconds. Fat32 + // has a granularity of 2 seconds. + // 2. We could get multiple notifications in a row. + if (gained && last_time + (Timestamp)2000 < os_now()) { + need_redraw = check_timestamps(true); + last_time = os_now(); + } + + if (need_redraw) { + // Something was executed, make sure the cursor is put back where it + // belongs. + need_wait_return = false; + + if (State & CMDLINE) { + redrawcmdline(); + } else if ((State & NORMAL) || (State & INSERT)) { + if (must_redraw != 0) { + update_screen(0); + } + + setcursor(); + } + + ui_flush(); + } + + if (need_maketitle) { + maketitle(); + } + + recursive = false; +} + +// initialization + +void init_default_autocmds(void) +{ + // open terminals when opening files that start with term:// +#define PROTO "term://" + do_cmdline_cmd("augroup nvim_terminal"); + do_cmdline_cmd("autocmd BufReadCmd " PROTO "* ++nested " + "if !exists('b:term_title')|call termopen(" + // Capture the command string + "matchstr(expand(\"\"), " + "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " + // capture the working directory + "{'cwd': expand(get(matchlist(expand(\"\"), " + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, ''))})" + "|endif"); + do_cmdline_cmd("augroup END"); +#undef PROTO + + // limit syntax synchronization in the command window + do_cmdline_cmd("augroup nvim_cmdwin"); + do_cmdline_cmd("autocmd! CmdwinEnter [:>] syntax sync minlines=1 maxlines=1"); + do_cmdline_cmd("augroup END"); +} diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 63c5abd4f8..0a1f036183 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -4,6 +4,11 @@ #include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" +// event_T definition +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "auevents_enum.generated.h" +#endif + // Struct to save values in before executing autocommands for a buffer that is // not the current buffer. typedef struct { @@ -18,22 +23,23 @@ typedef struct { } aco_save_T; typedef struct AutoCmd { - char_u *cmd; // Command to be executed (NULL when - // command has been removed) + AucmdExecutable exec; bool once; // "One shot": removed after execution bool nested; // If autocommands nest here bool last; // last command in list + int64_t id; // TODO(tjdevries): Explain sctx_T script_ctx; // script context where defined - struct AutoCmd *next; // Next AutoCmd in list + char *desc; // Description for the autocmd. + struct AutoCmd *next; // Next AutoCmd in list } AutoCmd; typedef struct AutoPat { - struct AutoPat *next; // next AutoPat in AutoPat list; MUST - // be the first entry - char_u *pat; // pattern as typed (NULL when pattern - // has been removed) - regprog_T *reg_prog; // compiled regprog for pattern - AutoCmd *cmds; // list of commands to do + struct AutoPat *next; // next AutoPat in AutoPat list; MUST + // be the first entry + char_u *pat; // pattern as typed (NULL when pattern + // has been removed) + regprog_T *reg_prog; // compiled regprog for pattern + AutoCmd *cmds; // list of commands to do int group; // group ID int patlen; // strlen() of pat int buflocal_nr; // !=0 for buffer-local AutoPat @@ -41,13 +47,7 @@ typedef struct AutoPat { char last; // last pattern for apply_autocmds() } AutoPat; -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "auevents_enum.generated.h" -#endif - -/// /// Struct used to keep status while executing autocommands for an event. -/// typedef struct AutoPatCmd { AutoPat *curpat; // next AutoPat to examine AutoCmd *nextcmd; // next AutoCmd to execute @@ -75,8 +75,16 @@ EXTERN bool au_did_filetype INIT(= false); # include "autocmd.h.generated.h" #endif -#define AUGROUP_DEFAULT -1 // default autocmd group -#define AUGROUP_ERROR -2 // erroneous autocmd group -#define AUGROUP_ALL -3 // all autocmd groups +#define AUGROUP_DEFAULT (-1) // default autocmd group +#define AUGROUP_ERROR (-2) // erroneous autocmd group +#define AUGROUP_ALL (-3) // all autocmd groups +#define AUGROUP_DELETED (-4) // all autocmd groups +// #define AUGROUP_NS -5 // TODO(tjdevries): Support namespaced based augroups + +#define BUFLOCAL_PAT_LEN 25 + +/// Iterates over all the events for auto commands +#define FOR_ALL_AUEVENTS(event) \ + for (event_T event = (event_T)0; (int)event < (int)NUM_EVENTS; event = (event_T)((int)event + 1)) // NOLINT #endif // NVIM_AUTOCMD_H diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 233753839b..80bd3229c6 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -85,9 +85,9 @@ typedef struct { // used for recording hunks from xdiff typedef struct { linenr_T lnum_orig; - long count_orig; + long count_orig; linenr_T lnum_new; - long count_new; + long count_new; } diffhunk_T; // two diff inputs and one result @@ -1285,7 +1285,7 @@ void ex_diffpatch(exarg_T *eap) ex_file(eap); // Do filetype detection with the new name. - if (au_has_group((char_u *)"filetypedetect")) { + if (augroup_exists("filetypedetect")) { do_cmdline_cmd(":doau filetypedetect BufRead"); } } @@ -3159,8 +3159,7 @@ static int parse_diff_unified(char_u *line, diffhunk_T *hunk) /// Callback function for the xdl_diff() function. /// Stores the diff output in a grow array. /// -static int xdiff_out(long start_a, long count_a, long start_b, long count_b, - void *priv) +static int xdiff_out(long start_a, long count_a, long start_b, long count_b, void *priv) { diffout_T *dout = (diffout_T *)priv; diffhunk_T *p = xmalloc(sizeof(*p)); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 07f0799bc4..0322898827 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6,6 +6,7 @@ */ #include +#include #include "auto/config.h" @@ -3258,7 +3259,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) // b: variables // In cmdwin, the alternative buffer should be used. hashtab_T *ht - = is_in_cmdwin() ? &prevwin->w_buffer->b_vars->dv_hashtab : &curbuf->b_vars->dv_hashtab; + = is_in_cmdwin() ? &prevwin->w_buffer->b_vars->dv_hashtab : &curbuf->b_vars->dv_hashtab; if (bdone < ht->ht_used) { if (bdone++ == 0) { hi = ht->ht_array; @@ -7746,6 +7747,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) callback->type = kCallbackFuncref; } } else if (nlua_is_table_from_lua(arg)) { + // TODO(tjdvries): UnifiedCallback char_u *name = nlua_register_table_as_callable(arg); if (name != NULL) { @@ -7775,6 +7777,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co { partial_T *partial; char_u *name; + Array args = ARRAY_DICT_INIT; switch (callback->type) { case kCallbackFuncref: name = callback->data.funcref; @@ -7786,6 +7789,13 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co name = partial_name(partial); break; + case kCallbackLua: + ILOG(" We tryin to call dat dang lua ref "); + nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL); + + return false; + break; + case kCallbackNone: return false; break; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c5b01701de..49dde537c3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -894,6 +894,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) partial = argvars[0].vval.v_partial; func = partial_name(partial); } else if (nlua_is_table_from_lua(&argvars[0])) { + // TODO(tjdevries): UnifiedCallback func = nlua_register_table_as_callable(&argvars[0]); owned = true; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6f8b032d41..fbda7fbc3c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -28,11 +28,11 @@ #include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/os/fileio.h" #include "nvim/os/input.h" #include "nvim/pos.h" #include "nvim/types.h" #include "nvim/vim.h" -#include "nvim/os/fileio.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.c.generated.h" @@ -1123,6 +1123,8 @@ bool tv_callback_equal(const Callback *cb1, const Callback *cb2) // FIXME: this is inconsistent with tv_equal but is needed for precision // maybe change dictwatcheradd to return a watcher id instead? return cb1->data.partial == cb2->data.partial; + case kCallbackLua: + return cb1->data.luaref == cb2->data.luaref; case kCallbackNone: return true; } @@ -1142,6 +1144,9 @@ void callback_free(Callback *callback) case kCallbackPartial: partial_unref(callback->data.partial); break; + case kCallbackLua: + NLUA_CLEAR_REF(callback->data.luaref); + break; case kCallbackNone: break; } @@ -1149,6 +1154,12 @@ void callback_free(Callback *callback) callback->data.funcref = NULL; } +/// Check if callback is freed +bool callback_is_freed(Callback callback) +{ + return false; +} + /// Copy a callback into a typval_T. void callback_put(Callback *cb, typval_T *tv) FUNC_ATTR_NONNULL_ALL @@ -1164,6 +1175,9 @@ void callback_put(Callback *cb, typval_T *tv) tv->vval.v_string = vim_strsave(cb->data.funcref); func_ref(cb->data.funcref); break; + case kCallbackLua: + // TODO(tjdevries): I'm not even sure if this is strictly necessary? + abort(); default: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; @@ -1185,6 +1199,9 @@ void callback_copy(Callback *dest, Callback *src) dest->data.funcref = vim_strsave(src->data.funcref); func_ref(src->data.funcref); break; + case kCallbackLua: + dest->data.luaref = api_new_luaref(src->data.luaref); + break; default: dest->data.funcref = NULL; break; diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index ad01c01499..40dc819754 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -72,15 +72,18 @@ typedef enum { kCallbackNone = 0, kCallbackFuncref, kCallbackPartial, + kCallbackLua, } CallbackType; typedef struct { union { char_u *funcref; partial_T *partial; + LuaRef luaref; } data; CallbackType type; } Callback; + #define CALLBACK_INIT { .type = kCallbackNone } #define CALLBACK_NONE ((Callback)CALLBACK_INIT) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 49bf9972b1..c7910e148d 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1943,7 +1943,7 @@ int do_write(exarg_T *eap) // If 'filetype' was empty try detecting it now. if (*curbuf->b_p_ft == NUL) { - if (au_has_group((char_u *)"filetypedetect")) { + if (augroup_exists("filetypedetect")) { (void)do_doautocmd((char_u *)"filetypedetect BufRead", true, NULL); } do_modelines(0); diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index eaf5f627b6..92675499be 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -4,6 +4,7 @@ #include #include +#include "nvim/eval/typval.h" #include "nvim/normal.h" #include "nvim/pos.h" // for linenr_T #include "nvim/regexp_defs.h" @@ -91,6 +92,35 @@ typedef struct exarg exarg_T; typedef void (*ex_func_T)(exarg_T *eap); +// NOTE: These possible could be removed and changed so that +// Callback could take a "command" style string, and simply +// execute that (instead of it being a function). +// +// But it's still a bit weird to do that. +// +// Another option would be that we just make a callback reference to +// "execute($INPUT)" or something like that, so whatever the user +// sends in via autocmds is just executed via this. +// +// However, that would probably have some performance cost (probably +// very marginal, but still some cost either way). +typedef enum { + CALLABLE_NONE, + CALLABLE_EX, + CALLABLE_CB, +} AucmdExecutableType; + +typedef struct aucmd_executable_t AucmdExecutable; +struct aucmd_executable_t { + AucmdExecutableType type; + union { + char_u *cmd; + Callback cb; + } callable; +}; + +#define AUCMD_EXECUTABLE_INIT { .type = CALLABLE_NONE } + typedef char_u *(*LineGetter)(int, void *, int, bool); /// Structure for command definition. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ed4475eb1a..30287cd6f2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5049,8 +5049,8 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u ** { EXPAND_SYNTAX, get_syntax_name, true, true }, { EXPAND_SYNTIME, get_syntime_arg, true, true }, { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true }, - { EXPAND_EVENTS, get_event_name, true, true }, - { EXPAND_AUGROUP, get_augroup_name, true, true }, + { EXPAND_EVENTS, expand_get_event_name, true, true }, + { EXPAND_AUGROUP, expand_get_augroup_name, true, true }, { EXPAND_CSCOPE, get_cscope_name, true, true }, { EXPAND_SIGN, get_sign_name, true, true }, { EXPAND_PROFILE, get_profile_name, true, true }, diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index f5824d83be..965aa8749d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3796,7 +3796,7 @@ static int set_rw_fname(char_u *fname, char_u *sfname) // Do filetype detection now if 'filetype' is empty. if (*curbuf->b_p_ft == NUL) { - if (au_has_group((char_u *)"filetypedetect")) { + if (augroup_exists("filetypedetect")) { (void)do_doautocmd((char_u *)"filetypedetect BufRead", false, NULL); } do_modelines(0); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 5aa564623f..35ad57906b 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -326,16 +326,16 @@ EXTERN int want_garbage_collect INIT(= false); EXTERN int garbage_collect_at_exit INIT(= false); // Special values for current_SID. -#define SID_MODELINE -1 // when using a modeline -#define SID_CMDARG -2 // for "--cmd" argument -#define SID_CARG -3 // for "-c" argument -#define SID_ENV -4 // for sourcing environment variable -#define SID_ERROR -5 // option was reset because of an error -#define SID_NONE -6 // don't set scriptID -#define SID_WINLAYOUT -7 // changing window size -#define SID_LUA -8 // for Lua scripts/chunks -#define SID_API_CLIENT -9 // for API clients -#define SID_STR -10 // for sourcing a string with no script item +#define SID_MODELINE (-1) // when using a modeline +#define SID_CMDARG (-2) // for "--cmd" argument +#define SID_CARG (-3) // for "-c" argument +#define SID_ENV (-4) // for sourcing environment variable +#define SID_ERROR (-5) // option was reset because of an error +#define SID_NONE (-6) // don't set scriptID +#define SID_WINLAYOUT (-7) // changing window size +#define SID_LUA (-8) // for Lua scripts/chunks +#define SID_API_CLIENT (-9) // for API clients +#define SID_STR (-10) // for sourcing a string with no script item // Script CTX being sourced or was sourced to define the current function. EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 }); diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index ae2ec7835e..4bf0617dfa 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -245,6 +245,7 @@ enum key_extra { KE_EVENT = 102, // event KE_LUA = 103, // lua special key KE_COMMAND = 104, // special key + KE_AUCMD_SPECIAL = 105, }; /* @@ -443,6 +444,8 @@ enum key_extra { #define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND) #define K_LUA TERMCAP2KEY(KS_EXTRA, KE_LUA) +#define K_AUCMD_SPECIAL TERMCAP2KEY(KS_EXTRA, KE_AUCMD_SPECIAL) + // Bits for modifier mask // 0x01 cannot be used, because the modifier must be 0x02 or higher #define MOD_MASK_SHIFT 0x02 diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3c1676581c..18abf04ff6 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1350,6 +1350,16 @@ Object nlua_exec(const String str, const Array args, Error *err) return nlua_pop_Object(lstate, false, err); } +bool nlua_ref_is_function(LuaRef ref) +{ + lua_State *const lstate = global_lstate; + nlua_pushref(lstate, ref); + bool is_function = (lua_type(lstate, -1) == LUA_TFUNCTION); + lua_pop(lstate, 1); + + return is_function; +} + /// call a LuaRef as a function (or table with __call metamethod) /// /// @param ref the reference to call (not consumed) diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 957c61f2f9..47ac51dadb 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -24,14 +24,6 @@ typedef struct { #endif } nlua_ref_state_t; -#define set_api_error(s, err) \ - do { \ - Error *err_ = (err); \ - err_->type = kErrorTypeException; \ - err_->set = true; \ - memcpy(&err_->msg[0], s, sizeof(s)); \ - } while (0) - #define NLUA_CLEAR_REF(x) \ do { \ /* Take the address to avoid double evaluation. #1375 */ \ diff --git a/src/nvim/main.c b/src/nvim/main.c index ec64e407b2..b02ebbe030 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -9,7 +9,7 @@ #include #include "nvim/ascii.h" -#include "nvim/aucmd.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/channel.h" #include "nvim/charset.h" diff --git a/src/nvim/map.c b/src/nvim/map.c index 77ebc2a387..091d653046 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -177,6 +177,7 @@ MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) +MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 5e56f4dd65..c9c89bf2fd 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -46,6 +46,7 @@ MAP_DECLS(handle_T, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) +MAP_DECLS(String, int) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 6b889cf97c..b262fc6c54 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -5,7 +5,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/ascii.h" -#include "nvim/aucmd.h" +#include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/ex_docmd.h" #include "nvim/macros.h" @@ -378,7 +378,7 @@ static bool handle_focus_event(TermInput *input) bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; // Advance past the sequence rbuffer_consumed(input->read_stream.buffer, 3); - aucmd_schedule_focusgained(focus_gained); + autocmd_schedule_focusgained(focus_gained); return true; } return false; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 1aadaf5c9d..31b9614c34 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -8,7 +8,7 @@ #include #include "nvim/ascii.h" -#include "nvim/aucmd.h" +#include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" -- cgit From ebfe083337701534887ac3ea3d8e7ad47f7a206a Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Sat, 8 Jan 2022 00:39:44 +0600 Subject: feat(lua): show proper verbose output for lua configuration `:verbose` didn't work properly with lua configs (For example: options or keymaps are set from lua, just say that they were set from lua, doesn't say where they were set at. This fixes that issue. Now `:verbose` will provide filename and line no when option/keymap is set from lua. Changes: - compiles lua/vim/keymap.lua as vim/keymap.lua - When souring a lua file current_sctx.sc_sid is set to SID_LUA - Moved finding scripts SID out of `do_source()` to `get_current_script_id()`. So it can be reused for lua files. - Added new function `nlua_get_sctx` that extracts current lua scripts name and line no with debug library. And creates a sctx for it. NOTE: This function ignores C functions and blacklist which currently contains only vim/_meta.lua so vim.o/opt wrappers aren't targeted. - Added function `nlua_set_sctx` that changes provided sctx to current lua scripts sctx if a lua file is being executed. - Added tests in tests/functional/lua/verbose_spec.lua - add primary support for additional types (:autocmd, :function, :syntax) to lua verbose Note: These can't yet be directly set from lua but once that's possible :verbose should work for them hopefully :D - add :verbose support for nvim_exec & nvim_command within lua Currently auto commands/commands/functions ... can only be defined by nvim_exec/nvim_command this adds support for them. Means if those Are defined within lua with vim.cmd/nvim_exec :verbose will show their location . Though note it'll show the line no on which nvim_exec call was made. --- src/nvim/CMakeLists.txt | 3 ++ src/nvim/api/buffer.c | 11 ++--- src/nvim/api/deprecated.c | 4 +- src/nvim/api/private/helpers.c | 24 ++++++++++- src/nvim/api/vim.c | 10 +++-- src/nvim/api/vimscript.c | 7 +++- src/nvim/autocmd.c | 1 + src/nvim/context.c | 3 +- src/nvim/eval/userfunc.c | 1 + src/nvim/ex_cmds2.c | 92 +++++++++++++++++++++++++----------------- src/nvim/ex_docmd.c | 1 + src/nvim/getchar.c | 2 + src/nvim/lua/executor.c | 73 +++++++++++++++++++++++++++++++++ src/nvim/lua/vim.lua | 6 +-- src/nvim/option.c | 1 + src/nvim/runtime.c | 4 +- src/nvim/shada.c | 4 +- src/nvim/syntax.c | 4 ++ 18 files changed, 191 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 3ba3923a82..a6505feb6b 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -64,6 +64,7 @@ set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua) set(LUA_LOAD_PACKAGE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_load_package.lua) +set(LUA_KEYMAP_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/keymap.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") @@ -338,6 +339,7 @@ add_custom_command( ${LUA_META_MODULE_SOURCE} lua_meta_module ${LUA_FILETYPE_MODULE_SOURCE} lua_filetype_module ${LUA_LOAD_PACKAGE_MODULE_SOURCE} lua_load_package_module + ${LUA_KEYMAP_MODULE_SOURCE} lua_keymap_module DEPENDS ${CHAR_BLOB_GENERATOR} ${LUA_VIM_MODULE_SOURCE} @@ -347,6 +349,7 @@ add_custom_command( ${LUA_META_MODULE_SOURCE} ${LUA_FILETYPE_MODULE_SOURCE} ${LUA_LOAD_PACKAGE_MODULE_SOURCE} + ${LUA_KEYMAP_MODULE_SOURCE} VERBATIM ) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 922d288da1..3dd647e76f 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -953,11 +953,11 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, Stri /// @see |nvim_set_keymap()| /// /// @param buffer Buffer handle, or 0 for current buffer -void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts, - Error *err) +void nvim_buf_set_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, String rhs, + Dict(keymap) *opts, Error *err) FUNC_API_SINCE(6) { - modify_keymap(buffer, false, mode, lhs, rhs, opts, err); + modify_keymap(channel_id, buffer, false, mode, lhs, rhs, opts, err); } /// Unmaps a buffer-local |mapping| for the given mode. @@ -965,11 +965,12 @@ void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dic /// @see |nvim_del_keymap()| /// /// @param buffer Buffer handle, or 0 for current buffer -void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err) +void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, + String lhs, Error *err) FUNC_API_SINCE(6) { String rhs = { .data = "", .size = 0 }; - modify_keymap(buffer, true, mode, lhs, rhs, NULL, err); + modify_keymap(channel_id, buffer, true, mode, lhs, rhs, NULL, err); } /// Gets a map of buffer-local |user-commands|. diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 18243fec2b..d2013c597c 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -22,11 +22,11 @@ /// @deprecated /// @see nvim_exec -String nvim_command_output(String command, Error *err) +String nvim_command_output(uint64_t channel_id, String command, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(7) { - return nvim_exec(command, true, err); + return nvim_exec(channel_id, command, true, err); } /// @deprecated Use nvim_exec_lua() instead. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3d4a04f096..5198b00f65 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -585,8 +585,8 @@ Array string_to_array(const String input, bool crlf) /// @param buffer Buffer handle for a specific buffer, or 0 for the current /// buffer, or -1 to signify global behavior ("all buffers") /// @param is_unmap When true, removes the mapping that matches {lhs}. -void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs, - Dict(keymap) *opts, Error *err) +void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mode, String lhs, + String rhs, Dict(keymap) *opts, Error *err) { LuaRef lua_funcref = LUA_NOREF; bool global = (buffer == -1); @@ -599,6 +599,8 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String return; } + const sctx_T save_current_sctx = api_set_sctx(channel_id); + if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) { lua_funcref = opts->callback.data.luaref; opts->callback.data.luaref = LUA_NOREF; @@ -710,6 +712,7 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success fail_and_free: + current_sctx = save_current_sctx; NLUA_CLEAR_REF(parsed_args.rhs_lua); xfree(parsed_args.rhs); xfree(parsed_args.orig_rhs); @@ -1622,3 +1625,20 @@ int find_sid(uint64_t channel_id) return SID_API_CLIENT; } } + +/// Sets sctx for API calls. +/// +/// @param channel_id api clients id. Used to determine if it's a internal +/// call or a rpc call. +/// @return returns previous value of current_sctx. To be used +/// to be used for restoring sctx to previous state. +sctx_T api_set_sctx(uint64_t channel_id) +{ + sctx_T old_current_sctx = current_sctx; + if (channel_id != VIML_INTERNAL_CALL) { + current_sctx.sc_sid = + channel_id == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; + current_sctx.sc_lnum = 0; + } + return old_current_sctx; +} diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 302dccbde7..9d0b096a36 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1586,6 +1586,7 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// nmap /// /// +/// @param channel_id /// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …) /// or "!" for |:map!|, or empty string for |:map|. /// @param lhs Left-hand-side |{lhs}| of the mapping. @@ -1597,10 +1598,11 @@ ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode) /// a Lua function to call when the mapping is executed. /// Values are Booleans. Unknown key is an error. /// @param[out] err Error details, if any. -void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) +void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, Dict(keymap) *opts, + Error *err) FUNC_API_SINCE(6) { - modify_keymap(-1, false, mode, lhs, rhs, opts, err); + modify_keymap(channel_id, -1, false, mode, lhs, rhs, opts, err); } /// Unmaps a global |mapping| for the given mode. @@ -1608,10 +1610,10 @@ void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Er /// To unmap a buffer-local mapping, use |nvim_buf_del_keymap()|. /// /// @see |nvim_set_keymap()| -void nvim_del_keymap(String mode, String lhs, Error *err) +void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err) FUNC_API_SINCE(6) { - nvim_buf_del_keymap(-1, mode, lhs, err); + nvim_buf_del_keymap(channel_id, -1, mode, lhs, err); } /// Gets a map of global (non-buffer-local) Ex commands. diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 640144b234..4123674fe7 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -37,7 +37,7 @@ /// @param[out] err Error details (Vim error), if any /// @return Output (non-error, non-shell |:!|) if `output` is true, /// else empty string. -String nvim_exec(String src, Boolean output, Error *err) +String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err) FUNC_API_SINCE(7) { const int save_msg_silent = msg_silent; @@ -52,11 +52,16 @@ String nvim_exec(String src, Boolean output, Error *err) if (output) { msg_silent++; } + + const sctx_T save_current_sctx = api_set_sctx(channel_id); + do_source_str(src.data, "nvim_exec()"); if (output) { capture_ga = save_capture_ga; msg_silent = save_msg_silent; } + + current_sctx = save_current_sctx; try_end(err); if (ERROR_SET(err)) { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a9e1978ac0..379c92c4ea 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1086,6 +1086,7 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro ac->exec = aucmd_exec_copy(aucmd); ac->script_ctx = current_sctx; ac->script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&ac->script_ctx); ac->next = NULL; ac->once = once; ac->nested = nested; diff --git a/src/nvim/context.c b/src/nvim/context.c index 614a3ce30e..9434b4e0ea 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -256,7 +256,8 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly) size_t cmd_len = sizeof("func! ") + STRLEN(name); char *cmd = xmalloc(cmd_len); snprintf(cmd, cmd_len, "func! %s", name); - String func_body = nvim_exec(cstr_as_string(cmd), true, &err); + String func_body = nvim_exec(VIML_INTERNAL_CALL, cstr_as_string(cmd), + true, &err); xfree(cmd); if (!ERROR_SET(&err)) { ADD(ctx->funcs, STRING_OBJ(func_body)); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index eb241eb8ae..5764f9fbd4 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2573,6 +2573,7 @@ void ex_function(exarg_T *eap) fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; fp->uf_script_ctx.sc_lnum += sourcing_lnum_top; + nlua_set_sctx(&fp->uf_script_ctx); goto ret_free; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 090422088a..8666c7e33a 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1823,7 +1823,9 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char sourcing_lnum = 0; const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_sid = SID_STR; + if (current_sctx.sc_sid != SID_LUA) { + current_sctx.sc_sid = SID_STR; + } current_sctx.sc_seq = 0; current_sctx.sc_lnum = save_sourcing_lnum; funccal_entry_T entry; @@ -1904,7 +1906,6 @@ int do_source(char *fname, int check_other, int is_vimrc) char_u *fname_exp; char_u *firstline = NULL; int retval = FAIL; - static int last_current_SID_seq = 0; int save_debug_break_level = debug_break_level; scriptitem_T *si = NULL; proftime_T wait_start; @@ -1952,7 +1953,7 @@ int do_source(char *fname, int check_other, int is_vimrc) } if (cookie.fp == NULL) { - if (p_verbose > 0) { + if (p_verbose > 1) { verbose_enter(); if (sourcing_name == NULL) { smsg(_("could not source \"%s\""), fname); @@ -2028,37 +2029,8 @@ int do_source(char *fname, int check_other, int is_vimrc) funccal_entry_T funccalp_entry; save_funccal(&funccalp_entry); - // Check if this script was sourced before to find its SID. - // If it's new, generate a new SID. - // Always use a new sequence number. const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_seq = ++last_current_SID_seq; - current_sctx.sc_lnum = 0; - FileID file_id; - bool file_id_ok = os_fileid((char *)fname_exp, &file_id); - assert(script_items.ga_len >= 0); - for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0; - current_sctx.sc_sid--) { - si = &SCRIPT_ITEM(current_sctx.sc_sid); - // Compare dev/ino when possible, it catches symbolic links. - // Also compare file names, the inode may change when the file was edited. - bool file_id_equal = file_id_ok && si->file_id_valid - && os_fileid_equal(&(si->file_id), &file_id); - if (si->sn_name != NULL - && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) { - break; - } - } - if (current_sctx.sc_sid == 0) { - si = new_script_item(fname_exp, ¤t_sctx.sc_sid); - fname_exp = vim_strsave(si->sn_name); // used for autocmd - if (file_id_ok) { - si->file_id_valid = true; - si->file_id = file_id; - } else { - si->file_id_valid = false; - } - } + si = get_current_script_id(fname_exp, ¤t_sctx); if (l_do_profiling == PROF_YES) { bool forceit = false; @@ -2091,16 +2063,14 @@ int do_source(char *fname, int check_other, int is_vimrc) firstline = p; } - if (path_with_extension((const char *)fname, "lua")) { - // TODO(shadmansaleh): Properly handle :verbose for lua - // For now change currennt_sctx before sourcing lua files - // So verbose doesn't say everything was done in line 1 since we don't know + if (path_with_extension((const char *)fname_exp, "lua")) { const sctx_T current_sctx_backup = current_sctx; const linenr_T sourcing_lnum_backup = sourcing_lnum; + current_sctx.sc_sid = SID_LUA; current_sctx.sc_lnum = 0; sourcing_lnum = 0; // Source the file as lua - nlua_exec_file((const char *)fname); + nlua_exec_file((const char *)fname_exp); current_sctx = current_sctx_backup; sourcing_lnum = sourcing_lnum_backup; } else { @@ -2173,6 +2143,52 @@ theend: } +/// Check if fname was sourced before to finds its SID. +/// If it's new, generate a new SID. +/// @param[in] fname file path of script +/// @param[out] ret_sctx sctx of this script +scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) +{ + static int last_current_SID_seq = 0; + + sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq, + .sc_lnum = 0, + .sc_sid = 0 }; + FileID file_id; + scriptitem_T *si = NULL; + + bool file_id_ok = os_fileid((char *)fname, &file_id); + assert(script_items.ga_len >= 0); + for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; + script_sctx.sc_sid--) { + si = &SCRIPT_ITEM(script_sctx.sc_sid); + // Compare dev/ino when possible, it catches symbolic links. + // Also compare file names, the inode may change when the file was edited. + bool file_id_equal = file_id_ok && si->file_id_valid + && os_fileid_equal(&(si->file_id), &file_id); + if (si->sn_name != NULL + && (file_id_equal || fnamecmp(si->sn_name, fname) == 0)) { + break; + } + } + if (script_sctx.sc_sid == 0) { + si = new_script_item(vim_strsave(fname), &script_sctx.sc_sid); + if (file_id_ok) { + si->file_id_valid = true; + si->file_id = file_id; + } else { + si->file_id_valid = false; + } + } + if (ret_sctx != NULL) { + *ret_sctx = script_sctx; + } + + return si; +} + + + /// ":scriptnames" void ex_scriptnames(exarg_T *eap) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 152a41c407..d3203bcee8 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5277,6 +5277,7 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo cmd->uc_compl = compl; cmd->uc_script_ctx = current_sctx; cmd->uc_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&cmd->uc_script_ctx); cmd->uc_compl_arg = compl_arg; cmd->uc_compl_luaref = compl_luaref; cmd->uc_addr_type = addr_type; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index f249cde9a0..85a5c176bb 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3086,6 +3086,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T mp->m_expr = args->expr; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); if (args->desc != NULL) { mp->m_desc = xstrdup(args->desc); } @@ -3166,6 +3167,7 @@ int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev, buf_T mp->m_expr = args->expr; mp->m_script_ctx = current_sctx; mp->m_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&mp->m_script_ctx); mp->m_desc = NULL; if (args->desc != NULL) { mp->m_desc = xstrdup(args->desc); diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 18abf04ff6..3e9786eb1e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -16,6 +16,7 @@ #include "nvim/change.h" #include "nvim/cursor.h" #include "nvim/eval/userfunc.h" +#include "nvim/eval/typval.h" #include "nvim/event/loop.h" #include "nvim/event/time.h" #include "nvim/ex_cmds2.h" @@ -652,6 +653,15 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL // [package, loaded, module] lua_setfield(lstate, -2, "vim.filetype"); // [package, loaded] + code = (char *)&lua_keymap_module[0]; + if (luaL_loadbuffer(lstate, code, sizeof(lua_keymap_module) - 1, "@vim/keymap.lua") + || nlua_pcall(lstate, 0, 1)) { + nlua_error(lstate, _("E5106: Error while creating vim.keymap module: %.*s")); + return 1; + } + // [package, loaded, module] + lua_setfield(lstate, -2, "vim.keymap"); // [package, loaded] + lua_pop(lstate, 2); // [] } @@ -1808,6 +1818,69 @@ void nlua_execute_on_key(int c) #endif } +// Checks if str is in blacklist array +static bool is_in_ignorelist(const char *str, char *ignorelist[], int ignorelist_size) +{ + for (int i = 0; i < ignorelist_size; i++) { + if (strncmp(ignorelist[i], str, strlen(ignorelist[i])) == 0) { + return true; + } + } + return false; +} +// Get sctx of current file being sourced if doesn't exist generate it +static sctx_T *nlua_get_sourcing_sctx(void) +{ + lua_State *const lstate = global_lstate; + sctx_T *retval = (sctx_T *)xmalloc(sizeof(sctx_T)); + retval->sc_seq = -1; + retval->sc_sid = SID_LUA; + retval->sc_lnum = -1; + lua_Debug *info = (lua_Debug *)xmalloc(sizeof(lua_Debug)); + + // Files where internal wrappers are defined so we can ignore them + // like vim.o/opt etc are defined in _meta.lua + char *ignorelist[] = { + "vim/_meta.lua", + "vim/keymap.lua", + }; + int blacklist_size = sizeof(ignorelist) / sizeof(ignorelist[0]); + + for (int level = 1; true; level++) { + if (lua_getstack(lstate, level, info) != 1) { + goto cleanup; + } + if (lua_getinfo(lstate, "nSl", info) == 0) { + goto cleanup; + } + + if (info->what[0] == 'C' || info->source[0] != '@' + || is_in_ignorelist(info->source+1, ignorelist, blacklist_size)) { + continue; + } + break; + } + char *source_path = fix_fname(info->source + 1); + get_current_script_id((char_u *)source_path, retval); + xfree(source_path); + retval->sc_lnum = info->currentline; + +cleanup: + xfree(info); + return retval; +} + +// Sets the editor "script context" during Lua execution. Used by :verbose. +// @param[out] current +void nlua_set_sctx(sctx_T *current) +{ + if (p_verbose > 0 && current->sc_sid == SID_LUA) { + sctx_T *lua_sctx = nlua_get_sourcing_sctx(); + *current = *lua_sctx; + xfree(lua_sctx); + } +} + void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) { lua_State *const lstate = global_lstate; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index 68de4f960c..c0247ad996 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -43,6 +43,9 @@ assert(vim.inspect) vim.filetype = package.loaded['vim.filetype'] assert(vim.filetype) +vim.keymap = package.loaded['vim.keymap'] +assert(vim.keymap) + -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c setmetatable(vim, { @@ -69,9 +72,6 @@ setmetatable(vim, { elseif key == 'ui' then t.ui = require('vim.ui') return t.ui - elseif key == 'keymap' then - t.keymap = require('vim.keymap') - return t.keymap end end }) diff --git a/src/nvim/option.c b/src/nvim/option.c index 4ec6ee3148..9068c90dd1 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3878,6 +3878,7 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx) { int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int indir = (int)options[opt_idx].indir; + nlua_set_sctx(&script_ctx); const LastSet last_set = { .script_ctx = { script_ctx.sc_sid, diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 1ec3e40abe..1102096b32 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -153,7 +153,7 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, if (flags & DIP_ERR) { semsg(_(e_dirnotf), basepath, name); - } else if (p_verbose > 0) { + } else if (p_verbose > 1) { verbose_enter(); smsg(_("not found in '%s': \"%s\""), basepath, name); verbose_leave(); @@ -286,7 +286,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void if (!did_one && name != NULL) { if (flags & DIP_ERR) { semsg(_(e_dirnotf), "runtime path", name); - } else if (p_verbose > 0) { + } else if (p_verbose > 1) { verbose_enter(); smsg(_("not found in runtime path: \"%s\""), name); verbose_leave(); diff --git a/src/nvim/shada.c b/src/nvim/shada.c index e75a244031..b909888783 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -831,7 +831,7 @@ static int shada_read_file(const char *const file, const int flags) ShaDaReadDef sd_reader; const int of_ret = open_shada_file_for_reading(fname, &sd_reader); - if (p_verbose > 0) { + if (p_verbose > 1) { verbose_enter(); smsg(_("Reading ShaDa file \"%s\"%s%s%s%s"), fname, @@ -3036,7 +3036,7 @@ shada_write_file_nomerge: {} return FAIL; } - if (p_verbose > 0) { + if (p_verbose > 1) { verbose_enter(); smsg(_("Writing ShaDa file \"%s\""), fname); verbose_leave(); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 2b74c45478..db10b71d38 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -27,6 +27,7 @@ #include "nvim/highlight.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" +#include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -6919,6 +6920,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) hlgroup->sg_deflink = to_id; hlgroup->sg_deflink_sctx = current_sctx; hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_deflink_sctx); } } @@ -6939,6 +6941,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) hlgroup->sg_link = to_id; hlgroup->sg_script_ctx = current_sctx; hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_script_ctx); hlgroup->sg_cleared = false; redraw_all_later(SOME_VALID); @@ -7319,6 +7322,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } HL_TABLE()[idx].sg_script_ctx = current_sctx; HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); } xfree(key); xfree(arg); -- cgit From 7b6ee3ef0a2d64657c8ca25f440e010c6dc75408 Mon Sep 17 00:00:00 2001 From: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> Date: Thu, 17 Feb 2022 12:53:17 +0600 Subject: fix: anonymous sid not working --- src/nvim/eval.c | 25 +++++++++++++++++++++-- src/nvim/lua/executor.c | 53 ++++++++++++++++++++----------------------------- 2 files changed, 44 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0322898827..d95b9560c2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9369,10 +9369,31 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons } else if (*name == 'l' && funccal != NULL) { // local variable *d = &funccal->l_vars; } else if (*name == 's' // script variable - && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR) + && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR + || current_sctx.sc_sid == SID_LUA) && current_sctx.sc_sid <= ga_scripts.ga_len) { // For anonymous scripts without a script item, create one now so script vars can be used - if (current_sctx.sc_sid == SID_STR) { + if (current_sctx.sc_sid == SID_LUA) { + // try to resolve lua filename & line no so it can be shown in lastset messages. + nlua_set_sctx(¤t_sctx); + if (current_sctx.sc_sid != SID_LUA) { + // Great we have valid location. Now here this out we'll create a new + // script context with the name and lineno of this one. why ? + // for behavioral consistency. With this different anonymous exec from + // same file can't access each others script local stuff. We need to do + // this all other cases except this will act like that otherwise. + const LastSet last_set = (LastSet){ + .script_ctx = current_sctx, + .channel_id = LUA_INTERNAL_CALL, + }; + bool should_free; + // should_free is ignored as script_sctx will be resolved to a fnmae + // & new_script_item will consume it. + char_u *sc_name = get_scriptname(last_set, &should_free); + new_script_item(sc_name, ¤t_sctx.sc_sid); + } + } + if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { new_script_item(NULL, ¤t_sctx.sc_sid); } *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 3e9786eb1e..7c62ba2283 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1818,24 +1818,14 @@ void nlua_execute_on_key(int c) #endif } -// Checks if str is in blacklist array -static bool is_in_ignorelist(const char *str, char *ignorelist[], int ignorelist_size) +// Sets the editor "script context" during Lua execution. Used by :verbose. +// @param[out] current +void nlua_set_sctx(sctx_T *current) { - for (int i = 0; i < ignorelist_size; i++) { - if (strncmp(ignorelist[i], str, strlen(ignorelist[i])) == 0) { - return true; - } + if (p_verbose <= 0 || current->sc_sid != SID_LUA) { + return; } - return false; -} -// Get sctx of current file being sourced if doesn't exist generate it -static sctx_T *nlua_get_sourcing_sctx(void) -{ lua_State *const lstate = global_lstate; - sctx_T *retval = (sctx_T *)xmalloc(sizeof(sctx_T)); - retval->sc_seq = -1; - retval->sc_sid = SID_LUA; - retval->sc_lnum = -1; lua_Debug *info = (lua_Debug *)xmalloc(sizeof(lua_Debug)); // Files where internal wrappers are defined so we can ignore them @@ -1844,7 +1834,7 @@ static sctx_T *nlua_get_sourcing_sctx(void) "vim/_meta.lua", "vim/keymap.lua", }; - int blacklist_size = sizeof(ignorelist) / sizeof(ignorelist[0]); + int ignorelist_size = sizeof(ignorelist) / sizeof(ignorelist[0]); for (int level = 1; true; level++) { if (lua_getstack(lstate, level, info) != 1) { @@ -1854,31 +1844,30 @@ static sctx_T *nlua_get_sourcing_sctx(void) goto cleanup; } - if (info->what[0] == 'C' || info->source[0] != '@' - || is_in_ignorelist(info->source+1, ignorelist, blacklist_size)) { + bool is_ignored = false; + if (info->what[0] == 'C' || info->source[0] != '@') { + is_ignored = true; + } else { + for (int i = 0; i < ignorelist_size; i++) { + if (strncmp(ignorelist[i], info->source+1, strlen(ignorelist[i])) == 0) { + is_ignored = true; + break; + } + } + } + if (is_ignored) { continue; } break; } char *source_path = fix_fname(info->source + 1); - get_current_script_id((char_u *)source_path, retval); + get_current_script_id((char_u *)source_path, current); xfree(source_path); - retval->sc_lnum = info->currentline; + current->sc_lnum = info->currentline; + current->sc_seq = -1; cleanup: xfree(info); - return retval; -} - -// Sets the editor "script context" during Lua execution. Used by :verbose. -// @param[out] current -void nlua_set_sctx(sctx_T *current) -{ - if (p_verbose > 0 && current->sc_sid == SID_LUA) { - sctx_T *lua_sctx = nlua_get_sourcing_sctx(); - *current = *lua_sctx; - xfree(lua_sctx); - } } void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) -- cgit From 0f613482b389ad259dd53d893907b024a115352e Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Fri, 28 May 2021 15:45:34 -0400 Subject: feat(lua): add missing changes to autocmds lost in the rebase Note: some of these changes are breaking, like change of API signatures --- src/nvim/api/autocmd.c | 221 +++++++++++++++++++++++------------------ src/nvim/api/keysets.lua | 4 - src/nvim/api/private/helpers.c | 5 +- src/nvim/api/private/helpers.h | 16 +-- src/nvim/autocmd.c | 54 ++++++---- src/nvim/autocmd.h | 2 +- src/nvim/eval/typval.c | 24 ++++- src/nvim/lua/executor.c | 3 + 8 files changed, 197 insertions(+), 132 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index deb8ec8cf3..6b89eb1770 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -17,34 +17,7 @@ #define AUCMD_MAX_PATTERNS 256 -// Check whether every item in the array is a kObjectTypeString -#define CHECK_STRING_ARRAY(__array, k, v, goto_name) \ - for (size_t j = 0; j < __array.size; j++) { \ - Object item = __array.items[j]; \ - if (item.type != kObjectTypeString) { \ - api_set_error(err, \ - kErrorTypeValidation, \ - "All entries in '%s' must be strings", \ - k); \ - goto goto_name; \ - } \ - } - // Copy string or array of strings into an empty array. -#define UNPACK_STRING_OR_ARRAY(__array, k, v, goto_name) \ - if (v->type == kObjectTypeString) { \ - ADD(__array, copy_object(*v)); \ - } else if (v->type == kObjectTypeArray) { \ - CHECK_STRING_ARRAY(__array, k, v, goto_name); \ - __array = copy_array(v->data.array); \ - } else { \ - api_set_error(err, \ - kErrorTypeValidation, \ - "'%s' must be an array or a string.", \ - k); \ - goto goto_name; \ - } - // Get the event number, unless it is an error. Then goto `goto_name`. #define GET_ONE_EVENT(event_nr, event_str, goto_name) \ char_u *__next_ev; \ @@ -61,16 +34,18 @@ static int64_t next_autocmd_id = 1; /// Get autocmds that match the requirements passed to {opts}. -/// group -/// event -/// pattern /// -/// -- @param {string} event - event or events to match against -/// vim.api.nvim_get_autocmds({ event = "FileType" }) +/// @param opts Optional Parameters: +/// - event : Name or list of name of events to match against +/// - group (string): Name of group to match against +/// - pattern: Pattern or list of patterns to match against /// +/// @return A list of autocmds that match Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) FUNC_API_SINCE(9) { + // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... }) + Array autocmd_list = ARRAY_DICT_INIT; char_u *pattern_filters[AUCMD_MAX_PATTERNS]; char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; @@ -199,6 +174,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) if (aucmd_exec_is_deleted(ac->exec)) { continue; } + Dictionary autocmd_info = ARRAY_DICT_INIT; if (ap->group != AUGROUP_DEFAULT) { @@ -256,40 +232,42 @@ cleanup: return autocmd_list; } -/// Define an autocmd. -/// @param opts Dictionary +/// Create an autocmd. +/// +/// @param event The event or events to register this autocmd /// Required keys: /// event: string | ArrayOf(string) -/// event = "pat1,pat2,pat3", -/// event = "pat1" -/// event = {"pat1"} -/// event = {"pat1", "pat2", "pat3"} -/// -/// -/// -- @param {string} name - augroup name -/// -- @param {string | table} event - event or events to match against -/// -- @param {string | table} pattern - pattern or patterns to match against -/// -- @param {string | function} callback - function or string to execute on autocmd -/// -- @param {string} command - optional, vimscript command -/// Eg. command = "let g:value_set = v:true" -/// -- @param {boolean} once - optional, defaults to false /// -/// -- pattern = comma delimited list of patterns | pattern | { pattern, ... } +/// Examples: +/// - event: "pat1,pat2,pat3", +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } /// -/// pattern = "*.py,*.pyi" -/// pattern = "*.py" -/// pattern = {"*.py"} -/// pattern = { "*.py", "*.pyi" } +/// @param opts Optional Parameters: +/// - callback: (string|function) +/// - (string): The name of the viml function to execute when triggering this autocmd +/// - (function): The lua function to execute when triggering this autocmd +/// - NOTE: Cannot be used with {command} +/// - command: (string) command +/// - vimscript command +/// - NOTE: Cannot be used with {callback} +/// Eg. command = "let g:value_set = v:true" +/// - pattern: (string|table) +/// - pattern or patterns to match against +/// - defaults to "*". +/// - NOTE: Cannot be used with {buffer} +/// - buffer: (bufnr) +/// - create a |autocmd-buflocal| autocmd. +/// - NOTE: Cannot be used with {pattern} +/// - group: (string) The augroup name +/// - once: (boolean) - See |autocmd-once| +/// - nested: (boolean) - See |autocmd-nested| +/// - desc: (string) - Description of the autocmd /// -/// -- not supported -/// pattern = {"*.py,*.pyi"} -/// -/// -- event = string | string[] -/// event = "FileType,CursorHold" -/// event = "BufPreWrite" -/// event = {"BufPostWrite"} -/// event = {"CursorHold", "BufPreWrite", "BufPostWrite"} -Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Error *err) +/// @returns opaque value to use with nvim_del_autocmd +Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, + Error *err) FUNC_API_SINCE(9) { int64_t autocmd_id = -1; @@ -304,6 +282,11 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT; Callback cb = CALLBACK_NONE; + + if (!unpack_string_or_array(&event_array, &event, "event", err)) { + goto cleanup; + } + if (opts->callback.type != kObjectTypeNil && opts->command.type != kObjectTypeNil) { api_set_error(err, kErrorTypeValidation, "cannot pass both: 'callback' and 'command' for the same autocmd"); @@ -359,14 +342,10 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err goto cleanup; } - if (opts->event.type != kObjectTypeNil) { - UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) - } - bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - // TOOD: accept number for namespace instead + // TODO(tjdevries): accept number for namespace instead if (opts->group.type != kObjectTypeNil) { Object *v = &opts->group; if (v->type != kObjectTypeString) { @@ -402,7 +381,9 @@ Integer nvim_create_autocmd(uint64_t channel_id, Dict(create_autocmd) *opts, Err patlen = aucmd_pattern_length(pat); } } else if (v->type == kObjectTypeArray) { - CHECK_STRING_ARRAY(patterns, "pattern", v, cleanup); + if (!check_autocmd_string_array(patterns, "pattern", err)) { + goto cleanup; + } Array array = v->data.array; for (size_t i = 0; i < array.size; i++) { @@ -503,8 +484,9 @@ cleanup: return autocmd_id; } -/// Delete an autocmd by ID. Autocmds only return IDs when created -/// via the API. +/// Delete an autocmd by {id}. Autocmds only return IDs when created +/// via the API. Will not error if called and no autocmds match +/// the {id}. /// /// @param id Integer The ID returned by nvim_create_autocmd void nvim_del_autocmd(Integer id) @@ -517,28 +499,28 @@ void nvim_del_autocmd(Integer id) /// /// To get an existing augroup ID, do: ///
-///     local id = vim.api.nvim_create_augroup({ name = name, clear = false });
+///     local id = vim.api.nvim_create_augroup(name, {
+///         clear = false
+///     })
 /// 
/// +/// @param name String: The name of the augroup to create /// @param opts Parameters -/// - name (string): The name of the augroup /// - clear (bool): Whether to clear existing commands or not. -// Defaults to true. +/// Defaults to true. /// See |autocmd-groups| -Integer nvim_create_augroup(uint64_t channel_id, Dict(create_augroup) *opts, Error *err) +/// +/// @returns opaque value to use with nvim_del_augroup_by_id +Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augroup) *opts, + Error *err) FUNC_API_SINCE(9) { + char *augroup_name = name.data; bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err); - if (opts->name.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'name' is required and must be a string"); - return -1; - } - char *name = opts->name.data.string.data; - int augroup = -1; WITH_SCRIPT_CONTEXT(channel_id, { - augroup = augroup_add(name); + augroup = augroup_add(augroup_name); if (augroup == AUGROUP_ERROR) { api_set_error(err, kErrorTypeException, "Failed to set augroup"); return -1; @@ -554,7 +536,11 @@ Integer nvim_create_augroup(uint64_t channel_id, Dict(create_augroup) *opts, Err return augroup; } +/// Delete an augroup by {id}. {id} can only be returned when augroup was +/// created with |nvim_create_augroup|. +/// /// NOTE: behavior differs from augroup-delete. +/// /// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. /// This augroup will no longer exist void nvim_del_augroup_by_id(Integer id) @@ -564,7 +550,10 @@ void nvim_del_augroup_by_id(Integer id) augroup_del(name, false); } +/// Delete an augroup by {name}. +/// /// NOTE: behavior differs from augroup-delete. +/// /// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. /// This augroup will no longer exist void nvim_del_augroup_by_name(String name) @@ -573,12 +562,17 @@ void nvim_del_augroup_by_name(String name) augroup_del(name.data, false); } -/// -- @param {string} group - autocmd group name -/// -- @param {number} buffer - buffer number -/// -- @param {string | table} event - event or events to match against -/// -- @param {string | table} pattern - optional, defaults to "*". -/// vim.api.nvim_do_autcmd({ group, buffer, pattern, event, modeline }) -void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) +/// Do one autocmd. +/// +/// @param event The event or events to execute +/// @param opts Optional Parameters: +/// - buffer (number) - buffer number +/// - NOTE: Cannot be used with {pattern} +/// - pattern (string|table) - optional, defaults to "*". +/// - NOTE: Cannot be used with {buffer} +/// - group (string) - autocmd group name +/// - modeline (boolean) - Default true, see || +void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) { int au_group = AUGROUP_ALL; @@ -592,6 +586,10 @@ void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) Array event_array = ARRAY_DICT_INIT; + if (!unpack_string_or_array(&event_array, &event, "event", err)) { + goto cleanup; + } + if (opts->group.type != kObjectTypeNil) { if (opts->group.type != kObjectTypeString) { api_set_error(err, kErrorTypeValidation, "'group' must be a string"); @@ -634,13 +632,7 @@ void nvim_do_autocmd(Dict(do_autocmd) *opts, Error *err) set_pattern = true; } - if (opts->event.type != kObjectTypeNil) { - UNPACK_STRING_OR_ARRAY(event_array, "event", (&opts->event), cleanup) - } - - if (opts->modeline.type != kObjectTypeNil) { - modeline = api_object_to_bool(opts->modeline, "modeline", true, err); - } + modeline = api_object_to_bool(opts->modeline, "modeline", true, err); if (set_pattern && set_buf) { api_set_error(err, kErrorTypeValidation, "must not set 'buffer' and 'pattern'"); @@ -663,7 +655,46 @@ cleanup: XFREE_CLEAR(pattern); } +static bool check_autocmd_string_array(Array arr, char *k, Error *err) +{ + for (size_t i = 0; i < arr.size; i++) { + if (arr.items[i].type != kObjectTypeString) { + api_set_error(err, + kErrorTypeValidation, + "All entries in '%s' must be strings", + k); + return false; + } + + // Disallow newlines in the middle of the line. + const String l = arr.items[i].data.string; + if (memchr(l.data, NL, l.size)) { + api_set_error(err, kErrorTypeValidation, + "String cannot contain newlines"); + return false; + } + } + return true; +} + +static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) +{ + if (v->type == kObjectTypeString) { + ADD(*array, copy_object(*v)); + } else if (v->type == kObjectTypeArray) { + if (!check_autocmd_string_array(v->data.array, k, err)) { + return false; + } + *array = copy_array(v->data.array); + } else { + api_set_error(err, + kErrorTypeValidation, + "'%s' must be an array or a string.", + k); + return false; + } + + return true; +} -#undef UNPACK_STRING_OR_ARRAY -#undef CHECK_STRING_ARRAY #undef GET_ONE_EVENT diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index be71c446b1..b05816f8ac 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -116,7 +116,6 @@ return { "callback"; "command"; "desc"; - "event"; "group"; "once"; "nested"; @@ -124,7 +123,6 @@ return { }; do_autocmd = { "buffer"; - "event"; "group"; "modeline"; "pattern"; @@ -132,12 +130,10 @@ return { get_autocmds = { "event"; "group"; - "id"; "pattern"; }; create_augroup = { "clear"; - "name"; }; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 3d4a04f096..c2106855d3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -406,6 +406,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object }); } + buf_T *find_buffer_by_handle(Buffer buffer, Error *err) { if (buffer == 0) { @@ -1614,8 +1615,8 @@ int find_sid(uint64_t channel_id) { switch (channel_id) { case VIML_INTERNAL_CALL: - // TODO(autocmd): Figure out what this should be - // return SID_API_CLIENT; + // TODO(autocmd): Figure out what this should be + // return SID_API_CLIENT; case LUA_INTERNAL_CALL: return SID_LUA; default: diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 6969994c3b..bc7c2e6a60 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -152,13 +152,15 @@ typedef struct { #endif #define WITH_SCRIPT_CONTEXT(channel_id, code) \ - const sctx_T save_current_sctx = current_sctx; \ - current_sctx.sc_sid = \ - (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ - current_sctx.sc_lnum = 0; \ - current_channel_id = channel_id; \ - code; \ - current_sctx = save_current_sctx; + do { \ + const sctx_T save_current_sctx = current_sctx; \ + current_sctx.sc_sid = \ + (channel_id) == LUA_INTERNAL_CALL ? SID_LUA : SID_API_CLIENT; \ + current_sctx.sc_lnum = 0; \ + current_channel_id = channel_id; \ + code; \ + current_sctx = save_current_sctx; \ + } while (0); #endif // NVIM_API_PRIVATE_HELPERS_H diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a9e1978ac0..5d53ac6b17 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -4,13 +4,10 @@ // autocmd.c: Autocommand related functions #include -#include "nvim/autocmd.h" - -// #include "nvim/api/private/handle.h" - #include "lauxlib.h" #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -108,17 +105,6 @@ static char_u *old_termresponse = NULL; #define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT -/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested -#define ARG_GET_FLAG(errored, cmd, flag, pattern, len) \ - if (STRNCMP(cmd, pattern, len) == 0 && ascii_iswhite((cmd)[len])) { \ - if (flag) { \ - semsg(_(e_duparg2), pattern); \ - (errored) = true; \ - } \ - (flag) = true; \ - (cmd) = skipwhite((cmd) + (len)); \ - } - // Map of autocmd group names. // name -> ID static Map(String, int) augroup_map = MAP_INIT; @@ -408,6 +394,16 @@ int augroup_add(char *name) } /// Delete the augroup that matches name. +/// @param stupid_legacy_mode bool: This paremeter determines whether to run the augroup +/// deletion in the same fashion as `:augroup! {name}` where if there are any remaining +/// autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---` +/// but leave the autocmds existing. These are _separate_ augroups, so if you do this for +/// multiple augroups, you will have a bunch of `--- DELETED ---` augroups at the same time. +/// There is no way, as far as I could tell, how to actually delete them at this point as a user +/// +/// I did not consider this good behavior, so now when NOT in stupid_legacy_mode, we actually +/// delete these groups and their commands, like you would expect (and don't leave hanging +/// `--- DELETED ---` groups around) void augroup_del(char *name, bool stupid_legacy_mode) { int i = augroup_find(name); @@ -723,7 +719,7 @@ void do_autocmd(char_u *arg_in, int forceit) char_u *envpat = NULL; char_u *cmd; int need_free = false; - int nested = false; + bool nested = false; bool once = false; int group; @@ -778,11 +774,11 @@ void do_autocmd(char_u *arg_in, int forceit) bool invalid_flags = false; for (size_t i = 0; i < 2; i++) { if (*cmd != NUL) { - ARG_GET_FLAG(invalid_flags, cmd, once, "++once", 6); - ARG_GET_FLAG(invalid_flags, cmd, nested, "++nested", 8); + invalid_flags |= arg_autocmd_flag_get(&once, &cmd, "++once", 6); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "++nested", 8); // Check the deprecated "nested" flag. - ARG_GET_FLAG(invalid_flags, cmd, nested, "nested", 6); + invalid_flags |= arg_autocmd_flag_get(&nested, &cmd, "nested", 6); } } @@ -2050,6 +2046,8 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) typval_T rettv = TV_INITIAL_VALUE; callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv); + // TODO(tjdevries): + // // Major Hack Alert: // We just return "not-null" and continue going. // This would be a good candidate for a refactor. You would need to refactor: @@ -2057,7 +2055,7 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) // OR // 2. make where we call do_cmdline for autocmds not have to return anything, // and instead we loop over all the matches and just execute one-by-one. - // However, my expectation woudl be that could be expensive. + // However, my expectation would be that could be expensive. retval = vim_strsave((char_u *)""); } else { retval = vim_strsave(ac->exec.callable.cmd); @@ -2544,6 +2542,22 @@ static int arg_augroup_get(char_u **argp) return group; } +/// Handles grabbing arguments from `:autocmd` such as ++once and ++nested +static bool arg_autocmd_flag_get(bool *flag, char_u **cmd_ptr, char *pattern, int len) +{ + if (STRNCMP(*cmd_ptr, pattern, len) == 0 && ascii_iswhite((*cmd_ptr)[len])) { + if (*flag) { + semsg(_(e_duparg2), pattern); + return true; + } + + *flag = true; + *cmd_ptr = skipwhite((*cmd_ptr) + len); + } + + return false; +} + // UI Enter void do_autocmd_uienter(uint64_t chanid, bool attached) diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h index 0a1f036183..53ec7f2bb7 100644 --- a/src/nvim/autocmd.h +++ b/src/nvim/autocmd.h @@ -27,7 +27,7 @@ typedef struct AutoCmd { bool once; // "One shot": removed after execution bool nested; // If autocommands nest here bool last; // last command in list - int64_t id; // TODO(tjdevries): Explain + int64_t id; // ID used for uniquely tracking an autocmd. sctx_T script_ctx; // script context where defined char *desc; // Description for the autocmd. struct AutoCmd *next; // Next AutoCmd in list diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index fbda7fbc3c..44b003d106 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -8,6 +8,7 @@ #include #include +#include "lauxlib.h" #include "nvim/ascii.h" #include "nvim/assert.h" #include "nvim/charset.h" @@ -1157,7 +1158,22 @@ void callback_free(Callback *callback) /// Check if callback is freed bool callback_is_freed(Callback callback) { - return false; + switch (callback.type) { + case kCallbackFuncref: + return false; + break; + case kCallbackPartial: + return false; + break; + case kCallbackLua: + return callback.data.luaref == LUA_NOREF; + break; + case kCallbackNone: + return true; + break; + } + + return true; } /// Copy a callback into a typval_T. @@ -1176,8 +1192,10 @@ void callback_put(Callback *cb, typval_T *tv) func_ref(cb->data.funcref); break; case kCallbackLua: - // TODO(tjdevries): I'm not even sure if this is strictly necessary? - abort(); + // TODO(tjdevries): Unified Callback. + // At this point this isn't possible, but it'd be nice to put + // these handled more neatly in one place. + // So instead, we just do the default and put nil default: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 18abf04ff6..07071d4e1e 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1354,6 +1354,9 @@ bool nlua_ref_is_function(LuaRef ref) { lua_State *const lstate = global_lstate; nlua_pushref(lstate, ref); + + // TODO(tjdevries): This should probably check for callable tables as well. + // We should put some work maybe into simplifying how all of that works bool is_function = (lua_type(lstate, -1) == LUA_TFUNCTION); lua_pop(lstate, 1); -- cgit From fed515d9e7e7783253287a55dcdafe9cb304bee7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 1 Mar 2022 07:32:04 +0800 Subject: refactor: remove unused K_AUCMD_SPECIAL --- src/nvim/keymap.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 4bf0617dfa..ae2ec7835e 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -245,7 +245,6 @@ enum key_extra { KE_EVENT = 102, // event KE_LUA = 103, // lua special key KE_COMMAND = 104, // special key - KE_AUCMD_SPECIAL = 105, }; /* @@ -444,8 +443,6 @@ enum key_extra { #define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND) #define K_LUA TERMCAP2KEY(KS_EXTRA, KE_LUA) -#define K_AUCMD_SPECIAL TERMCAP2KEY(KS_EXTRA, KE_AUCMD_SPECIAL) - // Bits for modifier mask // 0x01 cannot be used, because the modifier must be 0x02 or higher #define MOD_MASK_SHIFT 0x02 -- cgit From 37a86a2f964b5d6f9dbfae8b78acaa3a71f981bb Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 1 Mar 2022 09:07:41 +0100 Subject: fix(api): include event in get_autocmds (#17553) --- src/nvim/api/autocmd.c | 4 ++++ src/nvim/autocmd.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 6b89eb1770..ad0e78cb50 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -197,6 +197,10 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) "pattern", STRING_OBJ(cstr_to_string((char *)ap->pat))); + PUT(autocmd_info, + "event", + STRING_OBJ(cstr_to_string((char *)event_nr2name(event)))); + PUT(autocmd_info, "once", BOOLEAN_OBJ(ac->once)); if (ap->buflocal_nr) { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 5d53ac6b17..dfd6c16c96 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -598,7 +598,7 @@ event_T event_name2nr(const char_u *start, char_u **end) /// @param[in] event Event to return name for. /// /// @return Event name, static string. Returns "Unknown" for unknown events. -static const char *event_nr2name(event_T event) +const char *event_nr2name(event_T event) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST { int i; -- cgit From ed1a9c310df178e1163c910f70b194a657ad055a Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:47:20 -0700 Subject: fix: enable filetype detection and syntax highlighting with --clean (#17566) --clean is supposed to emulate a "fresh install" and since Neovim enables filetype detection and syntax highlighting by default, these should be enabled when using --clean as well. --- src/nvim/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index b02ebbe030..d0b3a435c3 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -357,7 +357,7 @@ int main(int argc, char **argv) // Execute --cmd arguments. exe_pre_commands(¶ms); - if (!vimrc_none) { + if (!vimrc_none || params.clean) { // Sources ftplugin.vim and indent.vim. We do this *before* the user startup scripts to ensure // ftplugins run before FileType autocommands defined in the init file (which allows those // autocommands to overwrite settings from ftplugins). @@ -368,7 +368,7 @@ int main(int argc, char **argv) source_startup_scripts(¶ms); // If using the runtime (-u is not NONE), enable syntax & filetype plugins. - if (!vimrc_none) { + if (!vimrc_none || params.clean) { // Sources filetype.lua and filetype.vim unless the user explicitly disabled it with :filetype // off. filetype_maybe_enable(); -- cgit From 045422e4a081e9dedff014346cc32eaef45e04e1 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Tue, 1 Mar 2022 15:58:42 -0500 Subject: fix: respect os_proc_children rv of pid not found os_proc_children returns 2 if there's a failure in the underlying syscall. Only shell out to pgrep in that case. It returns 1 if the pid isn't found. In that case, we can roll forward with returning an empty list. --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 9d0b096a36..c37df45c14 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1991,7 +1991,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) size_t proc_count; int rv = os_proc_children((int)pid, &proc_list, &proc_count); - if (rv != 0) { + if (rv == 2) { // syscall failed (possibly because of kernel options), try shelling out. DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; -- cgit From 55a189583ea0a7b2e25fb1db7b43d80817bdf4af Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Mar 2022 08:50:15 +0800 Subject: chore(PVS): add PVS header to api/autocmd.c --- src/nvim/api/autocmd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index ad0e78cb50..9a8eccd22c 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include #include -- cgit From 22d1b2423f6abe177ce8a99f460cfedd5bc7eecc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Mar 2022 15:14:11 +0800 Subject: refactor(PVS/V560): ap == NULL is always false --- src/nvim/api/autocmd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 9a8eccd22c..2e19987609 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -131,10 +131,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) continue; } - for (AutoPat *ap = au_get_autopat_for_event(event); - ap != NULL; - ap = ap->next) { - if (ap == NULL || ap->cmds == NULL) { + for (AutoPat *ap = au_get_autopat_for_event(event); ap != NULL; ap = ap->next) { + if (ap->cmds == NULL) { continue; } -- cgit From 8ba47a64571b936c651a716db40e02685aedb160 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Mar 2022 15:14:52 +0800 Subject: refactor(PVS/V547): aucmd.type == CALLABLE_NONE is always false --- src/nvim/api/autocmd.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 2e19987609..0ad7b320d0 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -424,13 +424,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc ADD(patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); } - if (aucmd.type == CALLABLE_NONE) { - api_set_error(err, - kErrorTypeValidation, - "'command' or 'callback' is required"); - goto cleanup; - } - if (opts->desc.type != kObjectTypeNil) { if (opts->desc.type == kObjectTypeString) { desc = opts->desc.data.string.data; -- cgit From 3763d7d2d5c1d2b095f32ac16e37bb128593d04e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Mar 2022 15:17:50 +0800 Subject: refactor(clang): dead assignment buflocal_nr = 0 --- src/nvim/autocmd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 66222f6a6a..a36d61420a 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -887,7 +887,6 @@ int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u * while (patlen) { // detect special buffer-local patterns is_buflocal = aupat_is_buflocal(pat, patlen); - buflocal_nr = 0; if (is_buflocal) { buflocal_nr = aupat_get_buflocal_nr(pat, patlen); -- cgit From 4154bf9b59749df0a7e68eec8f7c8fb5f2b7efc9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 2 Mar 2022 21:28:30 +0800 Subject: fix(pty_process_win/wait_eof_timer_cb): also check for proc->out.did_eof --- src/nvim/os/pty_process_win.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index f78f3e66f5..99231968a2 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -281,7 +281,7 @@ static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) PtyProcess *ptyproc = wait_eof_timer->data; Process *proc = (Process *)ptyproc; - if (proc->out.closed || !uv_is_readable(proc->out.uvstream)) { + if (proc->out.closed || proc->out.did_eof || !uv_is_readable(proc->out.uvstream)) { uv_timer_stop(&ptyproc->wait_eof_timer); pty_process_finish2(ptyproc); } -- cgit From 78bb8c4ee723f3204ad7aced2f101542e86eae46 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 3 Mar 2022 11:57:25 +0800 Subject: test(old): add more missing test files and run more tests alone Copy four files from Vim v8.2.1432. Try to match Vim's test_alot.vim. This marks Vim patch 8.2.0164 as ported: vim-patch:8.2.0164: test_alot takes too long Problem: Test_alot takes too long. Solution: Run several tests individually. https://github.com/vim/vim/commit/842931cd7af37ea95e826b7a93a5d5587d18c9bb --- src/nvim/testdir/test_alot.vim | 39 +++--------- src/nvim/testdir/test_delete.vim | 114 ++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_file_perm.vim | 30 ++++++++++ src/nvim/testdir/test_searchpos.vim | 30 ++++++++++ src/nvim/testdir/test_set.vim | 29 +++++++++ src/nvim/testdir/test_sort.vim | 1 + 6 files changed, 212 insertions(+), 31 deletions(-) create mode 100644 src/nvim/testdir/test_delete.vim create mode 100644 src/nvim/testdir/test_file_perm.vim create mode 100644 src/nvim/testdir/test_searchpos.vim create mode 100644 src/nvim/testdir/test_set.vim (limited to 'src') diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 5a3d1d56bb..bb744f7b41 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -3,56 +3,33 @@ source test_backup.vim source test_behave.vim -source test_cd.vim -source test_changedtick.vim source test_compiler.vim -source test_cursor_func.vim -source test_cursorline.vim source test_ex_equal.vim source test_ex_undo.vim source test_ex_z.vim source test_ex_mode.vim -source test_execute_func.vim +source test_expand.vim source test_expand_func.vim source test_feedkeys.vim -source test_filter_cmd.vim -source test_filter_map.vim -source test_findfile.vim -source test_float_func.vim -source test_functions.vim +source test_file_perm.vim +source test_fnamemodify.vim source test_ga.vim +source test_glob2regpat.vim source test_global.vim -source test_goto.vim -source test_join.vim source test_jumps.vim -source test_fileformat.vim -source test_filetype.vim -source test_filetype_lua.vim -source test_lambda.vim +source test_lispwords.vim source test_menu.vim -source test_messages.vim -source test_modeline.vim source test_move.vim -source test_partial.vim -source test_popup.vim source test_put.vim -source test_rename.vim +source test_reltime.vim source test_scroll_opt.vim +source test_searchpos.vim +source test_set.vim source test_shift.vim -" Test fails on windows CI when using the MSVC compiler. -" source test_sort.vim source test_sha256.vim -source test_suspend.vim -source test_syn_attr.vim source test_tabline.vim -source test_tabpage.vim source test_tagcase.vim source test_tagfunc.vim -source test_tagjump.vim -source test_taglist.vim -source test_true_false.vim source test_unlet.vim source test_version.vim -source test_virtualedit.vim -source test_window_cmd.vim source test_wnext.vim diff --git a/src/nvim/testdir/test_delete.vim b/src/nvim/testdir/test_delete.vim new file mode 100644 index 0000000000..b23a3bd025 --- /dev/null +++ b/src/nvim/testdir/test_delete.vim @@ -0,0 +1,114 @@ +" Test for delete(). + +func Test_file_delete() + split Xfile + call setline(1, ['a', 'b']) + wq + call assert_equal(['a', 'b'], readfile('Xfile')) + call assert_equal(0, delete('Xfile')) + call assert_fails('call readfile("Xfile")', 'E484:') + call assert_equal(-1, delete('Xfile')) + bwipe Xfile +endfunc + +func Test_dir_delete() + call mkdir('Xdir1') + call assert_true(isdirectory('Xdir1')) + call assert_equal(0, delete('Xdir1', 'd')) + call assert_false(isdirectory('Xdir1')) + call assert_equal(-1, delete('Xdir1', 'd')) +endfunc + +func Test_recursive_delete() + call mkdir('Xdir1') + call mkdir('Xdir1/subdir') + call mkdir('Xdir1/empty') + split Xdir1/Xfile + call setline(1, ['a', 'b']) + w + w Xdir1/subdir/Xfile + close + call assert_true(isdirectory('Xdir1')) + call assert_equal(['a', 'b'], readfile('Xdir1/Xfile')) + call assert_true(isdirectory('Xdir1/subdir')) + call assert_equal(['a', 'b'], readfile('Xdir1/subdir/Xfile')) + call assert_true('Xdir1/empty'->isdirectory()) + call assert_equal(0, delete('Xdir1', 'rf')) + call assert_false(isdirectory('Xdir1')) + call assert_equal(-1, delete('Xdir1', 'd')) + bwipe Xdir1/Xfile + bwipe Xdir1/subdir/Xfile +endfunc + +func Test_symlink_delete() + if !has('unix') + return + endif + split Xfile + call setline(1, ['a', 'b']) + wq + silent !ln -s Xfile Xlink + " Delete the link, not the file + call assert_equal(0, delete('Xlink')) + call assert_equal(-1, delete('Xlink')) + call assert_equal(0, delete('Xfile')) + bwipe Xfile +endfunc + +func Test_symlink_dir_delete() + if !has('unix') + return + endif + call mkdir('Xdir1') + silent !ln -s Xdir1 Xlink + call assert_true(isdirectory('Xdir1')) + call assert_true(isdirectory('Xlink')) + " Delete the link, not the directory + call assert_equal(0, delete('Xlink')) + call assert_equal(-1, delete('Xlink')) + call assert_equal(0, delete('Xdir1', 'd')) +endfunc + +func Test_symlink_recursive_delete() + if !has('unix') + return + endif + call mkdir('Xdir3') + call mkdir('Xdir3/subdir') + call mkdir('Xdir4') + split Xdir3/Xfile + call setline(1, ['a', 'b']) + w + w Xdir3/subdir/Xfile + w Xdir4/Xfile + close + silent !ln -s ../Xdir4 Xdir3/Xlink + + call assert_true(isdirectory('Xdir3')) + call assert_equal(['a', 'b'], readfile('Xdir3/Xfile')) + call assert_true(isdirectory('Xdir3/subdir')) + call assert_equal(['a', 'b'], readfile('Xdir3/subdir/Xfile')) + call assert_true(isdirectory('Xdir4')) + call assert_true(isdirectory('Xdir3/Xlink')) + call assert_equal(['a', 'b'], readfile('Xdir4/Xfile')) + + call assert_equal(0, delete('Xdir3', 'rf')) + call assert_false(isdirectory('Xdir3')) + call assert_equal(-1, delete('Xdir3', 'd')) + " symlink is deleted, not the directory it points to + call assert_true(isdirectory('Xdir4')) + call assert_equal(['a', 'b'], readfile('Xdir4/Xfile')) + call assert_equal(0, delete('Xdir4/Xfile')) + call assert_equal(0, delete('Xdir4', 'd')) + + bwipe Xdir3/Xfile + bwipe Xdir3/subdir/Xfile + bwipe Xdir4/Xfile +endfunc + +func Test_delete_errors() + call assert_fails('call delete('''')', 'E474:') + call assert_fails('call delete(''foo'', 0)', 'E15:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_file_perm.vim b/src/nvim/testdir/test_file_perm.vim new file mode 100644 index 0000000000..1cb09e8647 --- /dev/null +++ b/src/nvim/testdir/test_file_perm.vim @@ -0,0 +1,30 @@ +" Test getting and setting file permissions. + +func Test_file_perm() + call assert_equal('', getfperm('Xtest')) + call assert_equal(0, 'Xtest'->setfperm('r--------')) + + call writefile(['one'], 'Xtest') + call assert_true(len('Xtest'->getfperm()) == 9) + + call assert_equal(1, setfperm('Xtest', 'rwx------')) + if has('win32') + call assert_equal('rw-rw-rw-', getfperm('Xtest')) + else + call assert_equal('rwx------', getfperm('Xtest')) + endif + + call assert_equal(1, setfperm('Xtest', 'r--r--r--')) + call assert_equal('r--r--r--', getfperm('Xtest')) + + call assert_fails("setfperm('Xtest', '---')") + + call assert_equal(1, setfperm('Xtest', 'rwx------')) + call delete('Xtest') + + call assert_fails("call setfperm(['Xfile'], 'rw-rw-rw-')", 'E730:') + call assert_fails("call setfperm('Xfile', [])", 'E730:') + call assert_fails("call setfperm('Xfile', 'rwxrwxrwxrw')", 'E475:') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_searchpos.vim b/src/nvim/testdir/test_searchpos.vim new file mode 100644 index 0000000000..dd13c305c5 --- /dev/null +++ b/src/nvim/testdir/test_searchpos.vim @@ -0,0 +1,30 @@ +" Tests for searchpos() + +func Test_searchpos() + new one + 0put ='1a3' + 1put ='123xyz' + call cursor(1, 1) + call assert_equal([1, 1, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')) + call cursor(1, 2) + call assert_equal([2, 1, 1], '\%(\([a-z]\)\|\_.\)\{-}xyz'->searchpos('pcW')) + set cpo-=c + call cursor(1, 2) + call assert_equal([1, 2, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')) + call cursor(1, 3) + call assert_equal([1, 3, 1], searchpos('\%(\([a-z]\)\|\_.\)\{-}xyz', 'pcW')) + + " Now with \zs, first match is in column 0, "a" is matched. + call cursor(1, 3) + call assert_equal([2, 4, 2], searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcW')) + " With z flag start at cursor column, don't see the "a". + call cursor(1, 3) + call assert_equal([2, 4, 1], searchpos('\%(\([a-z]\)\|\_.\)\{-}\zsxyz', 'pcWz')) + + set cpo+=c + " close the window + q! + +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_set.vim b/src/nvim/testdir/test_set.vim new file mode 100644 index 0000000000..2b1e9eeee0 --- /dev/null +++ b/src/nvim/testdir/test_set.vim @@ -0,0 +1,29 @@ +" Tests for the :set command + +function Test_set_backslash() + let isk_save = &isk + + set isk=a,b,c + set isk+=d + call assert_equal('a,b,c,d', &isk) + set isk+=\\,e + call assert_equal('a,b,c,d,\,e', &isk) + set isk-=e + call assert_equal('a,b,c,d,\', &isk) + set isk-=\\ + call assert_equal('a,b,c,d', &isk) + + let &isk = isk_save +endfunction + +function Test_set_add() + let wig_save = &wig + + set wildignore=*.png, + set wildignore+=*.jpg + call assert_equal('*.png,*.jpg', &wig) + + let &wig = wig_save +endfunction + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index 570c4415c6..f72368fc59 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -12,6 +12,7 @@ func Compare2(a, b) abort endfunc func Test_sort_strings() + CheckNotMSWindows " FIXME: Why does this fail with MSVC? " numbers compared as strings call assert_equal([1, 2, 3], sort([3, 2, 1])) call assert_equal([13, 28, 3], sort([3, 28, 13])) -- cgit From dcd03f5d9d280510d0a76365e7c729859ed5103b Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 3 Mar 2022 14:28:27 +0100 Subject: refactor(autocmd): simplify check for freed callback When a callback is freed the type is always set to kCallbackNone. --- src/nvim/autocmd.c | 2 +- src/nvim/eval/typval.c | 21 --------------------- 2 files changed, 1 insertion(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a36d61420a..e7e1d5fd1b 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2473,7 +2473,7 @@ bool aucmd_exec_is_deleted(AucmdExecutable acc) case CALLABLE_EX: return acc.callable.cmd == NULL; case CALLABLE_CB: - return callback_is_freed(acc.callable.cb); + return acc.callable.cb.type == kCallbackNone; case CALLABLE_NONE: return true; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 44b003d106..d492c67877 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1155,27 +1155,6 @@ void callback_free(Callback *callback) callback->data.funcref = NULL; } -/// Check if callback is freed -bool callback_is_freed(Callback callback) -{ - switch (callback.type) { - case kCallbackFuncref: - return false; - break; - case kCallbackPartial: - return false; - break; - case kCallbackLua: - return callback.data.luaref == LUA_NOREF; - break; - case kCallbackNone: - return true; - break; - } - - return true; -} - /// Copy a callback into a typval_T. void callback_put(Callback *cb, typval_T *tv) FUNC_ATTR_NONNULL_ALL -- cgit From e8107f07486ce9dffdc020baf12836e55bf90ce5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 3 Mar 2022 21:59:28 +0800 Subject: vim-patch:8.2.4498: using with "noremap" does not work Problem: Using with "noremap" does not work. Solution: Always remap . (closes vim/vim#9879, closes vim/vim#9789) https://github.com/vim/vim/commit/1fc34225acbee5ddca2b9ec3f82b3014d385b7f8 --- src/nvim/getchar.c | 7 +++---- src/nvim/testdir/test_mapping.vim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 85a5c176bb..8426cdb98c 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1712,11 +1712,10 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) int local_State = get_real_state(); bool is_plug_map = false; - // Check if typehead starts with a mapping. - // In that case we will ignore nore flag on it. + // If typehead starts with then remap, even for a "noremap" mapping. if (typebuf.tb_buf[typebuf.tb_off] == K_SPECIAL - && typebuf.tb_buf[typebuf.tb_off+1] == KS_EXTRA - && typebuf.tb_buf[typebuf.tb_off+2] == KE_PLUG) { + && typebuf.tb_buf[typebuf.tb_off + 1] == KS_EXTRA + && typebuf.tb_buf[typebuf.tb_off + 2] == KE_PLUG) { is_plug_map = true; } diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 1080a3c85b..98440ccdd7 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -646,4 +646,34 @@ func Test_abbreviate_multi_byte() bwipe! endfunc +" Test for always being mapped, even when used with "noremap". +func Test_plug_remap() + let g:foo = 0 + nnoremap (Increase_x) let g:foo += 1 + nmap (Increase_x) + nnoremap (Increase_x) + call feedkeys("\", 'xt') + call assert_equal(1, g:foo) + call feedkeys("\", 'xt') + call assert_equal(2, g:foo) + nnoremap x + nmap x(Increase_x)x + nnoremap x(Increase_x)x + call setline(1, 'Some text') + normal! gg$ + call feedkeys("\", 'xt') + call assert_equal(3, g:foo) + call assert_equal('Some text', getline(1)) + call feedkeys("\", 'xt') + call assert_equal(4, g:foo) + call assert_equal('Some te', getline(1)) + nunmap (Increase_x) + nunmap + nunmap + nunmap + nunmap + unlet g:foo + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From f9faba88fdc46b2dd1a979a37ba61b000830ff3a Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 1 Mar 2022 14:27:19 +0100 Subject: refactor(lua): reorganize builtin modules, phase 1 --- src/nvim/CMakeLists.txt | 16 ++-- src/nvim/generators/gen_char_blob.lua | 20 +++- src/nvim/lua/executor.c | 175 +++++++++++++++------------------- src/nvim/lua/executor.h | 1 + src/nvim/lua/vim.lua | 19 ++-- src/nvim/main.c | 8 +- 6 files changed, 115 insertions(+), 124 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index a6505feb6b..21150965f9 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -332,14 +332,14 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env "LUAC_PRG=${LUAC_PRG}" ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE} - ${LUA_VIM_MODULE_SOURCE} vim_module - ${LUA_SHARED_MODULE_SOURCE} shared_module - ${LUA_INSPECT_MODULE_SOURCE} inspect_module - ${LUA_F_MODULE_SOURCE} lua_F_module - ${LUA_META_MODULE_SOURCE} lua_meta_module - ${LUA_FILETYPE_MODULE_SOURCE} lua_filetype_module - ${LUA_LOAD_PACKAGE_MODULE_SOURCE} lua_load_package_module - ${LUA_KEYMAP_MODULE_SOURCE} lua_keymap_module + ${LUA_LOAD_PACKAGE_MODULE_SOURCE} "vim._load_package" + ${LUA_INSPECT_MODULE_SOURCE} "vim.inspect" + ${LUA_VIM_MODULE_SOURCE} "vim" + ${LUA_SHARED_MODULE_SOURCE} "vim.shared" + ${LUA_F_MODULE_SOURCE} "vim.F" + ${LUA_META_MODULE_SOURCE} "vim._meta" + ${LUA_FILETYPE_MODULE_SOURCE} "vim.filetype" + ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap" DEPENDS ${CHAR_BLOB_GENERATOR} ${LUA_VIM_MODULE_SOURCE} diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua index 3ec1ff2caf..11f6cbcc13 100644 --- a/src/nvim/generators/gen_char_blob.lua +++ b/src/nvim/generators/gen_char_blob.lua @@ -28,16 +28,19 @@ local target = io.open(target_file, 'w') target:write('#include \n\n') +local index_items = {} + local warn_on_missing_compiler = true -local varnames = {} +local modnames = {} for argi = 2, #arg, 2 do local source_file = arg[argi] - local varname = arg[argi + 1] - if varnames[varname] then - error(string.format("varname %q is already specified for file %q", varname, varnames[varname])) + local modname = arg[argi + 1] + if modnames[modname] then + error(string.format("modname %q is already specified for file %q", modname, modnames[modname])) end - varnames[varname] = source_file + modnames[modname] = source_file + local varname = string.gsub(modname,'%.','_dot_').."_module" target:write(('static const uint8_t %s[] = {\n'):format(varname)) local output @@ -78,6 +81,13 @@ for argi = 2, #arg, 2 do end target:write(' 0};\n') + if modname ~= "_" then + table.insert(index_items, ' { "'..modname..'", '..varname..', sizeof '..varname..' },\n\n') + end end +target:write('static ModuleDef builtin_modules[] = {\n') +target:write(table.concat(index_items)) +target:write('};\n') + target:close() diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 7ac80f01f0..e233e0e6d0 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -53,6 +53,12 @@ typedef struct { String lua_err_str; } LuaError; +typedef struct { + char *name; + const uint8_t *data; + size_t size; +} ModuleDef; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/executor.c.generated.h" # include "lua/vim_module.generated.h" @@ -519,60 +525,70 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_pop(lstate, 3); } -static void nlua_common_package_init(lua_State *lstate) - FUNC_ATTR_NONNULL_ALL +static void nlua_preload_modules(lua_State *lstate) { - { - const char *code = (char *)&shared_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(shared_module) - 1, "@vim/shared.lua") - || nlua_pcall(lstate, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); - return; - } - } + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "preload"); // [package, preload] + for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { + ModuleDef def = builtin_modules[i]; + lua_pushinteger(lstate, (long)i); // [package, preload, i] + lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] + lua_setfield(lstate, -2, def.name); // [package, preload] - { - const char *code = (char *)&lua_load_package_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_load_package_module) - 1, "@vim/_load_package.lua") - || lua_pcall(lstate, 0, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s")); - return; + if (nlua_disable_preload && strequal(def.name, "vim")) { + break; } } - { - lua_getglobal(lstate, "package"); // [package] - lua_getfield(lstate, -1, "loaded"); // [package, loaded] + lua_pop(lstate, 2); // [] +} - const char *code = (char *)&inspect_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(inspect_module) - 1, "@vim/inspect.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating inspect module: %.*s\n")); - return; - } +static int nlua_module_preloader(lua_State *lstate) +{ + size_t i = (size_t)lua_tointeger(lstate, lua_upvalueindex(1)); + ModuleDef def = builtin_modules[i]; + char name[256]; + name[0] = '@'; + size_t off = xstrlcpy(name+1, def.name, (sizeof name) - 2); + strchrsub(name+1, '.', '/'); + xstrlcpy(name+1+off, ".lua", (sizeof name)-2-off); - // [package, loaded, inspect] - lua_setfield(lstate, -2, "vim.inspect"); // [package, loaded] + if (luaL_loadbuffer(lstate, (const char *)def.data, def.size - 1, name)) { + return lua_error(lstate); } - { - const char *code = (char *)&lua_F_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_F_module) - 1, "@vim/F.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim.F module: %.*s\n")); - return; - } - // [package, loaded, module] - lua_setfield(lstate, -2, "vim.F"); // [package, loaded] + lua_call(lstate, 0, 1); // propagates error to caller + return 1; +} - lua_pop(lstate, 2); // [] +static bool nlua_common_package_init(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + nlua_preload_modules(lstate); + + lua_getglobal(lstate, "require"); + lua_pushstring(lstate, "vim._load_package"); + if (nlua_pcall(lstate, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s\n")); + return false; + } + + // TODO(bfredl): ideally all initialization should be done as a single require + // call. + lua_getglobal(lstate, "require"); + lua_pushstring(lstate, "vim.shared"); + if (nlua_pcall(lstate, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); + return false; } + + return true; } /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. -static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL +static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { // print lua_pushcfunction(lstate, &nlua_print); @@ -638,59 +654,18 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); - nlua_common_package_init(lstate); - - { - lua_getglobal(lstate, "package"); // [package] - lua_getfield(lstate, -1, "loaded"); // [package, loaded] - - char *code = (char *)&lua_filetype_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_filetype_module) - 1, "@vim/filetype.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim.filetype module: %.*s")); - return 1; - } - // [package, loaded, module] - lua_setfield(lstate, -2, "vim.filetype"); // [package, loaded] - - code = (char *)&lua_keymap_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_keymap_module) - 1, "@vim/keymap.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim.keymap module: %.*s")); - return 1; - } - // [package, loaded, module] - lua_setfield(lstate, -2, "vim.keymap"); // [package, loaded] - - lua_pop(lstate, 2); // [] - } - - { - const char *code = (char *)&vim_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(vim_module) - 1, "@vim.lua") - || nlua_pcall(lstate, 0, 0)) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); - return 1; - } + if (!nlua_common_package_init(lstate)) { + return false; } - { - lua_getglobal(lstate, "package"); // [package] - lua_getfield(lstate, -1, "loaded"); // [package, loaded] - - const char *code = (char *)&lua_meta_module[0]; - if (luaL_loadbuffer(lstate, code, sizeof(lua_meta_module) - 1, "@vim/_meta.lua") - || nlua_pcall(lstate, 0, 1)) { - nlua_error(lstate, _("E5106: Error while creating vim._meta module: %.*s\n")); - return 1; - } - // [package, loaded, module] - lua_setfield(lstate, -2, "vim._meta"); // [package, loaded] - - lua_pop(lstate, 2); // [] + lua_getglobal(lstate, "require"); + lua_pushstring(lstate, "vim"); + if (nlua_pcall(lstate, 1, 0)) { + nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); + return false; } - return 0; + return true; } /// Initialize global lua interpreter @@ -707,11 +682,14 @@ void nlua_init(void) lua_State *lstate = luaL_newstate(); if (lstate == NULL) { - emsg(_("E970: Failed to initialize lua interpreter")); - preserve_exit(); + mch_errmsg(_("E970: Failed to initialize lua interpreter\n")); + os_exit(1); } luaL_openlibs(lstate); - nlua_state_init(lstate); + if (!nlua_state_init(lstate)) { + mch_errmsg(_("E970: Failed to initialize builtin lua modules\n")); + os_exit(1); + } luv_set_thread_cb(nlua_thread_acquire_vm, nlua_common_free_all_mem); @@ -747,23 +725,20 @@ static lua_State *nlua_thread_acquire_vm(void) nlua_state_add_stdlib(lstate, true); + lua_createtable(lstate, 0, 0); + lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); + lua_setfield(lstate, -2, "nvim__get_runtime"); + lua_setfield(lstate, -2, "api"); + lua_setglobal(lstate, "vim"); nlua_common_package_init(lstate); - lua_getglobal(lstate, "vim"); lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); - lua_getfield(lstate, -1, "vim.inspect"); - lua_setfield(lstate, -4, "inspect"); - lua_pop(lstate, 3); - lua_getglobal(lstate, "vim"); - lua_createtable(lstate, 0, 0); - lua_pushcfunction(lstate, nlua_thr_api_nvim__get_runtime); - lua_setfield(lstate, -2, "nvim__get_runtime"); - lua_setfield(lstate, -2, "api"); - lua_pop(lstate, 1); + lua_setfield(lstate, -2, "vim"); + lua_pop(lstate, 2); return lstate; } diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index 47ac51dadb..d978dc55d3 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -38,5 +38,6 @@ typedef struct { #endif EXTERN nlua_ref_state_t *nlua_global_refs INIT(= NULL); +EXTERN bool nlua_disable_preload INIT(= false); #endif // NVIM_LUA_EXECUTOR_H diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index c0247ad996..f5f293939b 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -36,16 +36,8 @@ local vim = vim assert(vim) - -vim.inspect = package.loaded['vim.inspect'] assert(vim.inspect) -vim.filetype = package.loaded['vim.filetype'] -assert(vim.filetype) - -vim.keymap = package.loaded['vim.keymap'] -assert(vim.keymap) - -- These are for loading runtime modules lazily since they aren't available in -- the nvim binary as specified in executor.c setmetatable(vim, { @@ -53,6 +45,9 @@ setmetatable(vim, { if key == 'treesitter' then t.treesitter = require('vim.treesitter') return t.treesitter + elseif key == 'filetype' then + t.filetype = require('vim.filetype') + return t.filetype elseif key == 'F' then t.F = require('vim.F') return t.F @@ -69,6 +64,9 @@ setmetatable(vim, { elseif key == 'diagnostic' then t.diagnostic = require('vim.diagnostic') return t.diagnostic + elseif key == 'keymap' then + t.keymap = require('vim.keymap') + return t.keymap elseif key == 'ui' then t.ui = require('vim.ui') return t.ui @@ -662,4 +660,7 @@ function vim.pretty_print(...) return ... end -return module + +require('vim._meta') + +return vim diff --git a/src/nvim/main.c b/src/nvim/main.c index d0b3a435c3..ae25ff63dd 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -253,12 +253,12 @@ int main(int argc, char **argv) // Check if we have an interactive window. check_and_set_isatty(¶ms); - nlua_init(); - // Process the command line arguments. File names are put in the global // argument list "global_alist". command_line_scan(¶ms); + nlua_init(); + if (embedded_mode) { const char *err; if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { @@ -918,6 +918,8 @@ static void command_line_scan(mparm_T *parmp) parmp->use_vimrc = "NONE"; parmp->clean = true; set_option_value("shadafile", 0L, "NONE", 0); + } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) { + nlua_disable_preload = true; } else { if (argv[0][argv_idx]) { mainerr(err_opt_unknown, argv[0]); @@ -1990,6 +1992,8 @@ static void mainerr(const char *errstr, const char *str) /// Prints version information for "nvim -v" or "nvim --version". static void version(void) { + // TODO(bfred): not like this? + nlua_init(); info_message = true; // use mch_msg(), not mch_errmsg() list_version(); msg_putchar('\n'); -- cgit From f89fb41a7a8b499159bfa44afa26dd17a845af45 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Wed, 2 Mar 2022 00:48:11 +0300 Subject: feat(tui): add support for `CSI 4 : [2,4,5] m` This commit finishes support for colored and styled underlines adding `CSI 4 : [2,4,5] m` support providing double, dashed, and dotted underlines Fixes #17362. --- src/nvim/api/keysets.lua | 6 ++++++ src/nvim/eval/funcs.c | 16 ++++++++++++---- src/nvim/hardcopy.c | 3 +++ src/nvim/hardcopy.h | 3 +++ src/nvim/highlight.c | 20 ++++++++++++++++++-- src/nvim/highlight_defs.h | 4 ++++ src/nvim/syntax.c | 8 ++++---- src/nvim/tui/tui.c | 35 ++++++++++++++++++++++++++++++----- 8 files changed, 80 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index b05816f8ac..4676ec7596 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -83,7 +83,10 @@ return { "standout"; "strikethrough"; "underline"; + "underlineline"; "undercurl"; + "underdot"; + "underdash"; "italic"; "reverse"; "nocombine"; @@ -105,7 +108,10 @@ return { "standout"; "strikethrough"; "underline"; + "underlineline"; "undercurl"; + "underdot"; + "underdash"; "italic"; "reverse"; "nocombine"; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 49dde537c3..b688e087ed 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11409,14 +11409,22 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) p = highlight_has_attr(id, HL_STANDOUT, modec); } break; - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline - p = highlight_has_attr(id, HL_UNDERLINE, modec); - } else { // undercurl + case 'u': { + int len = STRLEN(what); + if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline p = highlight_has_attr(id, HL_UNDERCURL, modec); + } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline + p = highlight_has_attr(id, HL_UNDERLINELINE, modec); + } else if (len > 5 && TOLOWER_ASC(what[6]) == 'o') { // underdot + p = highlight_has_attr(id, HL_UNDERDOT, modec); + } else { // underdash + p = highlight_has_attr(id, HL_UNDERDASH, modec); } break; } + } rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index eb10c65be9..93c8c53e33 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -420,7 +420,10 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); pattr->underline = (highlight_has_attr(hl_id, HL_UNDERLINE, modec) != NULL); + pattr->underlineline = (highlight_has_attr(hl_id, HL_UNDERLINELINE, modec) != NULL); pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); + pattr->underdot = (highlight_has_attr(hl_id, HL_UNDERDOT, modec) != NULL); + pattr->underdash = (highlight_has_attr(hl_id, HL_UNDERDASH, modec) != NULL); uint32_t fg_color = prt_get_color(hl_id, modec); diff --git a/src/nvim/hardcopy.h b/src/nvim/hardcopy.h index eba769d342..16119c5d2d 100644 --- a/src/nvim/hardcopy.h +++ b/src/nvim/hardcopy.h @@ -18,6 +18,9 @@ typedef struct { TriState italic; TriState underline; int undercurl; + int underlineline; + int underdot; + int underdash; } prt_text_attr_T; /* diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index e43a56086f..a01d8369ba 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -558,7 +558,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) cattrs = battrs; cattrs.rgb_fg_color = rgb_blend(ratio, battrs.rgb_fg_color, fattrs.rgb_bg_color); - if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) { + if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) { cattrs.rgb_sp_color = rgb_blend(ratio, battrs.rgb_sp_color, fattrs.rgb_bg_color); } else { @@ -576,7 +576,7 @@ int hl_blend_attrs(int back_attr, int front_attr, bool *through) } cattrs.rgb_fg_color = rgb_blend(ratio/2, battrs.rgb_fg_color, fattrs.rgb_fg_color); - if (cattrs.rgb_ae_attr & (HL_UNDERLINE|HL_UNDERCURL)) { + if (cattrs.rgb_ae_attr & (HL_ANY_UNDERLINE)) { cattrs.rgb_sp_color = rgb_blend(ratio/2, battrs.rgb_bg_color, fattrs.rgb_sp_color); } else { @@ -743,10 +743,23 @@ Dictionary hlattrs2dict(HlAttrs ae, bool use_rgb) PUT(hl, "underline", BOOLEAN_OBJ(true)); } + if (mask & HL_UNDERLINELINE) { + PUT(hl, "underlineline", BOOLEAN_OBJ(true)); + } + if (mask & HL_UNDERCURL) { PUT(hl, "undercurl", BOOLEAN_OBJ(true)); } + if (mask & HL_UNDERDOT) { + PUT(hl, "underdot", BOOLEAN_OBJ(true)); + } + + if (mask & HL_UNDERDASH) { + PUT(hl, "underdash", BOOLEAN_OBJ(true)); + } + + if (mask & HL_ITALIC) { PUT(hl, "italic", BOOLEAN_OBJ(true)); } @@ -813,7 +826,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(dict, mask, bold, , HL_BOLD); CHECK_FLAG(dict, mask, standout, , HL_STANDOUT); CHECK_FLAG(dict, mask, underline, , HL_UNDERLINE); + CHECK_FLAG(dict, mask, underlineline, , HL_UNDERLINELINE); CHECK_FLAG(dict, mask, undercurl, , HL_UNDERCURL); + CHECK_FLAG(dict, mask, underdot, , HL_UNDERDOT); + CHECK_FLAG(dict, mask, underdash, , HL_UNDERDASH); CHECK_FLAG(dict, mask, italic, , HL_ITALIC); CHECK_FLAG(dict, mask, reverse, , HL_INVERSE); CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 50a03e0c02..dcfb9358bb 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -24,6 +24,10 @@ typedef enum { HL_FG_INDEXED = 0x0200, HL_DEFAULT = 0x0400, HL_GLOBAL = 0x0800, + HL_UNDERLINELINE = 0x1000, + HL_UNDERDOT = 0x2000, + HL_UNDERDASH = 0x4000, + HL_ANY_UNDERLINE = HL_UNDERLINE | HL_UNDERLINELINE | HL_UNDERCURL | HL_UNDERDOT | HL_UNDERDASH, } HlAttrFlags; /// Stores a complete highlighting entry, including colors and attributes diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index db10b71d38..38cb6fa861 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -121,11 +121,11 @@ static int include_link = 0; // when 2 include "nvim/link" and "clear" /// The "term", "cterm" and "gui" arguments can be any combination of the /// following names, separated by commas (but no spaces!). static char *(hl_name_table[]) = -{ "bold", "standout", "underline", "undercurl", - "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; +{ "bold", "standout", "underline", "underlineline", "undercurl", "underdot", + "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; static int hl_attr_table[] = -{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, - HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; +{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, + HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; static char e_illegal_arg[] = N_("E390: Illegal argument: %s"); diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 58061f020d..1ad82b7290 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -528,7 +528,7 @@ static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) return a1.cterm_fg_color != a2.cterm_fg_color || a1.cterm_bg_color != a2.cterm_bg_color || a1.cterm_ae_attr != a2.cterm_ae_attr - || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL) + || (a1.cterm_ae_attr & HL_ANY_UNDERLINE && a1.rgb_sp_color != a2.rgb_sp_color); } } @@ -552,15 +552,27 @@ static void update_attrs(UI *ui, int attr_id) bool strikethrough = attr & HL_STRIKETHROUGH; bool underline; + bool underlineline; bool undercurl; + bool underdot; + bool underdash; if (data->unibi_ext.set_underline_style != -1) { underline = attr & HL_UNDERLINE; + underlineline = attr & HL_UNDERLINELINE; undercurl = attr & HL_UNDERCURL; + underdash = attr & HL_UNDERDASH; + underdot = attr & HL_UNDERDOT; } else { - underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL); + underline = attr & HL_ANY_UNDERLINE; + underlineline = false; undercurl = false; + underdot = false; + underdash = false; } + bool has_any_underline = undercurl || underline + || underdot || underdash || underlineline; + if (unibi_get_str(data->ut, unibi_set_attributes)) { if (bold || reverse || underline || standout) { UNIBI_SET_NUM_VAR(data->params[0], standout); @@ -599,11 +611,24 @@ static void update_attrs(UI *ui, int attr_id) if (strikethrough && data->unibi_ext.enter_strikethrough_mode != -1) { unibi_out_ext(ui, data->unibi_ext.enter_strikethrough_mode); } + if (underlineline && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 2); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } if (undercurl && data->unibi_ext.set_underline_style != -1) { UNIBI_SET_NUM_VAR(data->params[0], 3); unibi_out_ext(ui, data->unibi_ext.set_underline_style); } - if ((undercurl || underline) && data->unibi_ext.set_underline_color != -1) { + if (underdot && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 4); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } + if (underdash && data->unibi_ext.set_underline_style != -1) { + UNIBI_SET_NUM_VAR(data->params[0], 5); + unibi_out_ext(ui, data->unibi_ext.set_underline_style); + } + + if (has_any_underline && data->unibi_ext.set_underline_color != -1) { int color = attrs.rgb_sp_color; if (color != -1) { UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red @@ -652,13 +677,13 @@ static void update_attrs(UI *ui, int attr_id) data->default_attr = fg == -1 && bg == -1 - && !bold && !italic && !underline && !undercurl && !reverse && !standout + && !bold && !italic && !has_any_underline && !reverse && !standout && !strikethrough; // Non-BCE terminals can't clear with non-default background color. Some BCE // terminals don't support attributes either, so don't rely on it. But assume // italic and bold has no effect if there is no text. - data->can_clear_attr = !reverse && !standout && !underline && !undercurl + data->can_clear_attr = !reverse && !standout && !has_any_underline && !strikethrough && (data->bce || bg == -1); } -- cgit From d5bd7ffe51b2a8908cedd4e58e294ebf5d021e12 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Fri, 4 Mar 2022 17:25:09 +0900 Subject: fix(autocmd): clean up autocmds only when needed (#17593) --- src/nvim/autocmd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index e7e1d5fd1b..60a6207076 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -239,7 +239,6 @@ void aupat_del_for_event_and_group(event_T event, int group) } } - au_need_clean = true; au_cleanup(); // may really delete removed patterns/commands now } -- cgit From 08cf3fb099e1f30418e08788000d5db0de2b8db7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 4 Mar 2022 16:39:55 +0800 Subject: chore(autocmd): move comment to the right place --- src/nvim/autocmd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 60a6207076..7cb493f57d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -239,7 +239,7 @@ void aupat_del_for_event_and_group(event_T event, int group) } } - au_cleanup(); // may really delete removed patterns/commands now + au_cleanup(); } // Mark all commands for a pattern for deletion. @@ -551,7 +551,7 @@ void free_all_autocmds(void) } au_need_clean = true; - au_cleanup(); // may really delete removed patterns/commands now + au_cleanup(); // Delete the augroup_map, including free the data String name; @@ -940,7 +940,7 @@ int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u * patlen = (int)aucmd_pattern_length(pat); } - au_cleanup(); + au_cleanup(); // may really delete removed patterns/commands now return OK; } -- cgit From 186a559818f7a709dbdd4590fe7440ebd619a208 Mon Sep 17 00:00:00 2001 From: bfredl Date: Fri, 4 Mar 2022 16:18:58 +0100 Subject: refactor(lua): move only runtime lua file in src/ to runtime/lua reorganize so that initialization is done in lua --- src/nvim/CMakeLists.txt | 10 +- src/nvim/lua/executor.c | 56 ++-- src/nvim/lua/vim.lua | 666 ------------------------------------------------ 3 files changed, 23 insertions(+), 709 deletions(-) delete mode 100644 src/nvim/lua/vim.lua (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 21150965f9..9abefbe02a 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -57,13 +57,13 @@ set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) -set(LUA_VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) +set(LUA_EDITOR_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_editor.lua) set(LUA_SHARED_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/shared.lua) set(LUA_INSPECT_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/inspect.lua) set(LUA_F_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/F.lua) set(LUA_META_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_meta.lua) set(LUA_FILETYPE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/filetype.lua) -set(LUA_LOAD_PACKAGE_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_load_package.lua) +set(LUA_INIT_PACKAGES_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/_init_packages.lua) set(LUA_KEYMAP_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/runtime/lua/vim/keymap.lua) set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) @@ -332,9 +332,9 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env "LUAC_PRG=${LUAC_PRG}" ${LUA_PRG} ${CHAR_BLOB_GENERATOR} -c ${VIM_MODULE_FILE} - ${LUA_LOAD_PACKAGE_MODULE_SOURCE} "vim._load_package" + ${LUA_INIT_PACKAGES_MODULE_SOURCE} "vim._init_packages" ${LUA_INSPECT_MODULE_SOURCE} "vim.inspect" - ${LUA_VIM_MODULE_SOURCE} "vim" + ${LUA_EDITOR_MODULE_SOURCE} "vim._editor" ${LUA_SHARED_MODULE_SOURCE} "vim.shared" ${LUA_F_MODULE_SOURCE} "vim.F" ${LUA_META_MODULE_SOURCE} "vim._meta" @@ -342,7 +342,7 @@ add_custom_command( ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap" DEPENDS ${CHAR_BLOB_GENERATOR} - ${LUA_VIM_MODULE_SOURCE} + ${LUA_EDITOR_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} ${LUA_F_MODULE_SOURCE} diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index e233e0e6d0..29a3c515c2 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -525,24 +525,6 @@ static void nlua_common_vim_init(lua_State *lstate, bool is_thread) lua_pop(lstate, 3); } -static void nlua_preload_modules(lua_State *lstate) -{ - lua_getglobal(lstate, "package"); // [package] - lua_getfield(lstate, -1, "preload"); // [package, preload] - for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { - ModuleDef def = builtin_modules[i]; - lua_pushinteger(lstate, (long)i); // [package, preload, i] - lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] - lua_setfield(lstate, -2, def.name); // [package, preload] - - if (nlua_disable_preload && strequal(def.name, "vim")) { - break; - } - } - - lua_pop(lstate, 2); // [] -} - static int nlua_module_preloader(lua_State *lstate) { size_t i = (size_t)lua_tointeger(lstate, lua_upvalueindex(1)); @@ -561,24 +543,29 @@ static int nlua_module_preloader(lua_State *lstate) return 1; } -static bool nlua_common_package_init(lua_State *lstate) +static bool nlua_init_packages(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { - nlua_preload_modules(lstate); + // put builtin packages in preload + lua_getglobal(lstate, "package"); // [package] + lua_getfield(lstate, -1, "preload"); // [package, preload] + for (size_t i = 0; i < ARRAY_SIZE(builtin_modules); i++) { + ModuleDef def = builtin_modules[i]; + lua_pushinteger(lstate, (long)i); // [package, preload, i] + lua_pushcclosure(lstate, nlua_module_preloader, 1); // [package, preload, cclosure] + lua_setfield(lstate, -2, def.name); // [package, preload] - lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim._load_package"); - if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating _load_package module: %.*s\n")); - return false; + if (nlua_disable_preload && strequal(def.name, "vim.inspect")) { + break; + } } - // TODO(bfredl): ideally all initialization should be done as a single require - // call. + lua_pop(lstate, 2); // [] + lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim.shared"); + lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating shared module: %.*s\n")); + nlua_error(lstate, _("E5106: Error while loading packages: %.*s\n")); return false; } @@ -654,14 +641,7 @@ static bool nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_setglobal(lstate, "vim"); - if (!nlua_common_package_init(lstate)) { - return false; - } - - lua_getglobal(lstate, "require"); - lua_pushstring(lstate, "vim"); - if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while creating vim module: %.*s\n")); + if (!nlua_init_packages(lstate)) { return false; } @@ -732,7 +712,7 @@ static lua_State *nlua_thread_acquire_vm(void) lua_setglobal(lstate, "vim"); - nlua_common_package_init(lstate); + nlua_init_packages(lstate); lua_getglobal(lstate, "package"); lua_getfield(lstate, -1, "loaded"); diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua deleted file mode 100644 index f5f293939b..0000000000 --- a/src/nvim/lua/vim.lua +++ /dev/null @@ -1,666 +0,0 @@ --- Nvim-Lua stdlib: the `vim` module (:help lua-stdlib) --- --- Lua code lives in one of three places: --- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the --- `inspect` and `lpeg` modules. --- 2. runtime/lua/vim/shared.lua: Code shared between Nvim and tests. --- (This will go away if we migrate to nvim as the test-runner.) --- 3. src/nvim/lua/: Compiled-into Nvim itself. --- --- Guideline: "If in doubt, put it in the runtime". --- --- Most functions should live directly in `vim.`, not in submodules. --- The only "forbidden" names are those claimed by legacy `if_lua`: --- $ vim --- :lua for k,v in pairs(vim) do print(k) end --- buffer --- open --- window --- lastline --- firstline --- type --- line --- eval --- dict --- beep --- list --- command --- --- Reference (#6580): --- - https://github.com/luafun/luafun --- - https://github.com/rxi/lume --- - http://leafo.net/lapis/reference/utilities.html --- - https://github.com/torch/paths --- - https://github.com/bakpakin/Fennel (pretty print, repl) --- - https://github.com/howl-editor/howl/tree/master/lib/howl/util - -local vim = vim -assert(vim) -assert(vim.inspect) - --- These are for loading runtime modules lazily since they aren't available in --- the nvim binary as specified in executor.c -setmetatable(vim, { - __index = function(t, key) - if key == 'treesitter' then - t.treesitter = require('vim.treesitter') - return t.treesitter - elseif key == 'filetype' then - t.filetype = require('vim.filetype') - return t.filetype - elseif key == 'F' then - t.F = require('vim.F') - return t.F - elseif require('vim.uri')[key] ~= nil then - -- Expose all `vim.uri` functions on the `vim` module. - t[key] = require('vim.uri')[key] - return t[key] - elseif key == 'lsp' then - t.lsp = require('vim.lsp') - return t.lsp - elseif key == 'highlight' then - t.highlight = require('vim.highlight') - return t.highlight - elseif key == 'diagnostic' then - t.diagnostic = require('vim.diagnostic') - return t.diagnostic - elseif key == 'keymap' then - t.keymap = require('vim.keymap') - return t.keymap - elseif key == 'ui' then - t.ui = require('vim.ui') - return t.ui - end - end -}) - -vim.log = { - levels = { - TRACE = 0; - DEBUG = 1; - INFO = 2; - WARN = 3; - ERROR = 4; - } -} - --- Internal-only until comments in #8107 are addressed. --- Returns: --- {errcode}, {output} -function vim._system(cmd) - local out = vim.fn.system(cmd) - local err = vim.v.shell_error - return err, out -end - --- Gets process info from the `ps` command. --- Used by nvim_get_proc() as a fallback. -function vim._os_proc_info(pid) - if pid == nil or pid <= 0 or type(pid) ~= 'number' then - error('invalid pid') - end - local cmd = { 'ps', '-p', pid, '-o', 'comm=', } - local err, name = vim._system(cmd) - if 1 == err and vim.trim(name) == '' then - return {} -- Process not found. - elseif 0 ~= err then - error('command failed: '..vim.fn.string(cmd)) - end - local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=', }) - -- Remove trailing whitespace. - name = vim.trim(name):gsub('^.*/', '') - ppid = tonumber(ppid) or -1 - return { - name = name, - pid = pid, - ppid = ppid, - } -end - --- Gets process children from the `pgrep` command. --- Used by nvim_get_proc_children() as a fallback. -function vim._os_proc_children(ppid) - if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then - error('invalid ppid') - end - local cmd = { 'pgrep', '-P', ppid, } - local err, rv = vim._system(cmd) - if 1 == err and vim.trim(rv) == '' then - return {} -- Process not found. - elseif 0 ~= err then - error('command failed: '..vim.fn.string(cmd)) - end - local children = {} - for s in rv:gmatch('%S+') do - local i = tonumber(s) - if i ~= nil then - table.insert(children, i) - end - end - return children -end - --- TODO(ZyX-I): Create compatibility layer. - ---- Return a human-readable representation of the given object. ---- ----@see https://github.com/kikito/inspect.lua ----@see https://github.com/mpeterv/vinspect -local function inspect(object, options) -- luacheck: no unused - error(object, options) -- Stub for gen_vimdoc.py -end - -do - local tdots, tick, got_line1 = 0, 0, false - - --- Paste handler, invoked by |nvim_paste()| when a conforming UI - --- (such as the |TUI|) pastes text into the editor. - --- - --- Example: To remove ANSI color codes when pasting: - ---
-  --- vim.paste = (function(overridden)
-  ---   return function(lines, phase)
-  ---     for i,line in ipairs(lines) do
-  ---       -- Scrub ANSI color codes from paste input.
-  ---       lines[i] = line:gsub('\27%[[0-9;mK]+', '')
-  ---     end
-  ---     overridden(lines, phase)
-  ---   end
-  --- end)(vim.paste)
-  --- 
- --- - ---@see |paste| - --- - ---@param lines |readfile()|-style list of lines to paste. |channel-lines| - ---@param phase -1: "non-streaming" paste: the call contains all lines. - --- If paste is "streamed", `phase` indicates the stream state: - --- - 1: starts the paste (exactly once) - --- - 2: continues the paste (zero or more times) - --- - 3: ends the paste (exactly once) - ---@returns false if client should cancel the paste. - function vim.paste(lines, phase) - local call = vim.api.nvim_call_function - local now = vim.loop.now() - local mode = call('mode', {}):sub(1,1) - if phase < 2 then -- Reset flags. - tdots, tick, got_line1 = now, 0, false - elseif mode ~= 'c' then - vim.api.nvim_command('undojoin') - end - if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line. - got_line1 = (#lines > 1) - vim.api.nvim_set_option('paste', true) -- For nvim_input(). - local line1 = lines[1]:gsub('<', ''):gsub('[\r\n\012\027]', ' ') -- Scrub. - vim.api.nvim_input(line1) - vim.api.nvim_set_option('paste', false) - elseif mode ~= 'c' then - if phase < 2 and mode:find('^[vV\22sS\19]') then - vim.api.nvim_command([[exe "normal! \"]]) - vim.api.nvim_put(lines, 'c', false, true) - elseif phase < 2 and not mode:find('^[iRt]') then - vim.api.nvim_put(lines, 'c', true, true) - -- XXX: Normal-mode: workaround bad cursor-placement after first chunk. - vim.api.nvim_command('normal! a') - elseif phase < 2 and mode == 'R' then - local nchars = 0 - for _, line in ipairs(lines) do - nchars = nchars + line:len() - end - local row, col = unpack(vim.api.nvim_win_get_cursor(0)) - local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1] - local firstline = lines[1] - firstline = bufline:sub(1, col)..firstline - lines[1] = firstline - lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len()) - vim.api.nvim_buf_set_lines(0, row-1, row, false, lines) - else - vim.api.nvim_put(lines, 'c', false, true) - end - end - if phase ~= -1 and (now - tdots >= 100) then - local dots = ('.'):rep(tick % 4) - tdots = now - tick = tick + 1 - -- Use :echo because Lua print('') is a no-op, and we want to clear the - -- message when there are zero dots. - vim.api.nvim_command(('echo "%s"'):format(dots)) - end - if phase == -1 or phase == 3 then - vim.api.nvim_command('redraw'..(tick > 1 and '|echo ""' or '')) - end - return true -- Paste will not continue if not returning `true`. - end -end - ---- Defers callback `cb` until the Nvim API is safe to call. ---- ----@see |lua-loop-callbacks| ----@see |vim.schedule()| ----@see |vim.in_fast_event()| -function vim.schedule_wrap(cb) - return (function (...) - local args = vim.F.pack_len(...) - vim.schedule(function() cb(vim.F.unpack_len(args)) end) - end) -end - ---- ----@private -function vim.empty_dict() - return setmetatable({}, vim._empty_dict_mt) -end - --- vim.fn.{func}(...) -vim.fn = setmetatable({}, { - __index = function(t, key) - local _fn - if vim.api[key] ~= nil then - _fn = function() - error(string.format("Tried to call API function with vim.fn: use vim.api.%s instead", key)) - end - else - _fn = function(...) - return vim.call(key, ...) - end - end - t[key] = _fn - return _fn - end -}) - -vim.funcref = function(viml_func_name) - return vim.fn[viml_func_name] -end - --- An easier alias for commands. -vim.cmd = function(command) - return vim.api.nvim_exec(command, false) -end - --- These are the vim.env/v/g/o/bo/wo variable magic accessors. -do - local validate = vim.validate - - --@private - local function make_dict_accessor(scope, handle) - validate { - scope = {scope, 's'}; - } - local mt = {} - function mt:__newindex(k, v) - return vim._setvar(scope, handle or 0, k, v) - end - function mt:__index(k) - if handle == nil and type(k) == 'number' then - return make_dict_accessor(scope, k) - end - return vim._getvar(scope, handle or 0, k) - end - return setmetatable({}, mt) - end - - vim.g = make_dict_accessor('g', false) - vim.v = make_dict_accessor('v', false) - vim.b = make_dict_accessor('b') - vim.w = make_dict_accessor('w') - vim.t = make_dict_accessor('t') -end - ---- Get a table of lines with start, end columns for a region marked by two points ---- ----@param bufnr number of buffer ----@param pos1 (line, column) tuple marking beginning of region ----@param pos2 (line, column) tuple marking end of region ----@param regtype type of selection (:help setreg) ----@param inclusive boolean indicating whether the selection is end-inclusive ----@return region lua table of the form {linenr = {startcol,endcol}} -function vim.region(bufnr, pos1, pos2, regtype, inclusive) - if not vim.api.nvim_buf_is_loaded(bufnr) then - vim.fn.bufload(bufnr) - end - - -- check that region falls within current buffer - local buf_line_count = vim.api.nvim_buf_line_count(bufnr) - pos1[1] = math.min(pos1[1], buf_line_count - 1) - pos2[1] = math.min(pos2[1], buf_line_count - 1) - - -- in case of block selection, columns need to be adjusted for non-ASCII characters - -- TODO: handle double-width characters - local bufline - if regtype:byte() == 22 then - bufline = vim.api.nvim_buf_get_lines(bufnr, pos1[1], pos1[1] + 1, true)[1] - pos1[2] = vim.str_utfindex(bufline, pos1[2]) - end - - local region = {} - for l = pos1[1], pos2[1] do - local c1, c2 - if regtype:byte() == 22 then -- block selection: take width from regtype - c1 = pos1[2] - c2 = c1 + regtype:sub(2) - -- and adjust for non-ASCII characters - bufline = vim.api.nvim_buf_get_lines(bufnr, l, l + 1, true)[1] - if c1 < #bufline then - c1 = vim.str_byteindex(bufline, c1) - end - if c2 < #bufline then - c2 = vim.str_byteindex(bufline, c2) - end - else - c1 = (l == pos1[1]) and (pos1[2]) or 0 - c2 = (l == pos2[1]) and (pos2[2] + (inclusive and 1 or 0)) or -1 - end - table.insert(region, l, {c1, c2}) - end - return region -end - ---- Defers calling `fn` until `timeout` ms passes. ---- ---- Use to do a one-shot timer that calls `fn` ---- Note: The {fn} is |schedule_wrap|ped automatically, so API functions are ---- safe to call. ----@param fn Callback to call once `timeout` expires ----@param timeout Number of milliseconds to wait before calling `fn` ----@return timer luv timer object -function vim.defer_fn(fn, timeout) - vim.validate { fn = { fn, 'c', true}; } - local timer = vim.loop.new_timer() - timer:start(timeout, 0, vim.schedule_wrap(function() - timer:stop() - timer:close() - - fn() - end)) - - return timer -end - - ---- Display a notification to the user. ---- ---- This function can be overridden by plugins to display notifications using a ---- custom provider (such as the system notification provider). By default, ---- writes to |:messages|. ---- ----@param msg string Content of the notification to show to the user. ----@param level number|nil One of the values from |vim.log.levels|. ----@param opts table|nil Optional parameters. Unused by default. -function vim.notify(msg, level, opts) -- luacheck: no unused args - if level == vim.log.levels.ERROR then - vim.api.nvim_err_writeln(msg) - elseif level == vim.log.levels.WARN then - vim.api.nvim_echo({{msg, 'WarningMsg'}}, true, {}) - else - vim.api.nvim_echo({{msg}}, true, {}) - end -end - -do - local notified = {} - - --- Display a notification only one time. - --- - --- Like |vim.notify()|, but subsequent calls with the same message will not - --- display a notification. - --- - ---@param msg string Content of the notification to show to the user. - ---@param level number|nil One of the values from |vim.log.levels|. - ---@param opts table|nil Optional parameters. Unused by default. - function vim.notify_once(msg, level, opts) -- luacheck: no unused args - if not notified[msg] then - vim.notify(msg, level, opts) - notified[msg] = true - end - end -end - ----@private -function vim.register_keystroke_callback() - error('vim.register_keystroke_callback is deprecated, instead use: vim.on_key') -end - -local on_key_cbs = {} - ---- Adds Lua function {fn} with namespace id {ns_id} as a listener to every, ---- yes every, input key. ---- ---- The Nvim command-line option |-w| is related but does not support callbacks ---- and cannot be toggled dynamically. ---- ----@param fn function: Callback function. It should take one string argument. ---- On each key press, Nvim passes the key char to fn(). |i_CTRL-V| ---- If {fn} is nil, it removes the callback for the associated {ns_id} ----@param ns_id number? Namespace ID. If nil or 0, generates and returns a new ---- |nvim_create_namespace()| id. ---- ----@return number Namespace id associated with {fn}. Or count of all callbacks ----if on_key() is called without arguments. ---- ----@note {fn} will be removed if an error occurs while calling. ----@note {fn} will not be cleared by |nvim_buf_clear_namespace()| ----@note {fn} will receive the keys after mappings have been evaluated -function vim.on_key(fn, ns_id) - if fn == nil and ns_id == nil then - return #on_key_cbs - end - - vim.validate { - fn = { fn, 'c', true}, - ns_id = { ns_id, 'n', true } - } - - if ns_id == nil or ns_id == 0 then - ns_id = vim.api.nvim_create_namespace('') - end - - on_key_cbs[ns_id] = fn - return ns_id -end - ---- Executes the on_key callbacks. ----@private -function vim._on_key(char) - local failed_ns_ids = {} - local failed_messages = {} - for k, v in pairs(on_key_cbs) do - local ok, err_msg = pcall(v, char) - if not ok then - vim.on_key(nil, k) - table.insert(failed_ns_ids, k) - table.insert(failed_messages, err_msg) - end - end - - if failed_ns_ids[1] then - error(string.format( - "Error executing 'on_key' with ns_ids '%s'\n Messages: %s", - table.concat(failed_ns_ids, ", "), - table.concat(failed_messages, "\n"))) - end -end - ---- Generate a list of possible completions for the string. ---- String starts with ^ and then has the pattern. ---- ---- 1. Can we get it to just return things in the global namespace with that name prefix ---- 2. Can we get it to return things from global namespace even with `print(` in front. -function vim._expand_pat(pat, env) - env = env or _G - - pat = string.sub(pat, 2, #pat) - - if pat == '' then - local result = vim.tbl_keys(env) - table.sort(result) - return result, 0 - end - - -- TODO: We can handle spaces in [] ONLY. - -- We should probably do that at some point, just for cooler completion. - -- TODO: We can suggest the variable names to go in [] - -- This would be difficult as well. - -- Probably just need to do a smarter match than just `:match` - - -- Get the last part of the pattern - local last_part = pat:match("[%w.:_%[%]'\"]+$") - if not last_part then return {}, 0 end - - local parts, search_index = vim._expand_pat_get_parts(last_part) - - local match_part = string.sub(last_part, search_index, #last_part) - local prefix_match_pat = string.sub(pat, 1, #pat - #match_part) or '' - - local final_env = env - - for _, part in ipairs(parts) do - if type(final_env) ~= 'table' then - return {}, 0 - end - local key - - -- Normally, we just have a string - -- Just attempt to get the string directly from the environment - if type(part) == "string" then - key = part - else - -- However, sometimes you want to use a variable, and complete on it - -- With this, you have the power. - - -- MY_VAR = "api" - -- vim[MY_VAR] - -- -> _G[MY_VAR] -> "api" - local result_key = part[1] - if not result_key then - return {}, 0 - end - - local result = rawget(env, result_key) - - if result == nil then - return {}, 0 - end - - key = result - end - local field = rawget(final_env, key) - if field == nil then - local mt = getmetatable(final_env) - if mt and type(mt.__index) == "table" then - field = rawget(mt.__index, key) - end - end - final_env = field - - if not final_env then - return {}, 0 - end - end - - local keys = {} - ---@private - local function insert_keys(obj) - for k,_ in pairs(obj) do - if type(k) == "string" and string.sub(k,1,string.len(match_part)) == match_part then - table.insert(keys,k) - end - end - end - - if type(final_env) == "table" then - insert_keys(final_env) - end - local mt = getmetatable(final_env) - if mt and type(mt.__index) == "table" then - insert_keys(mt.__index) - end - - table.sort(keys) - - return keys, #prefix_match_pat -end - -vim._expand_pat_get_parts = function(lua_string) - local parts = {} - - local accumulator, search_index = '', 1 - local in_brackets, bracket_end = false, -1 - local string_char = nil - for idx = 1, #lua_string do - local s = lua_string:sub(idx, idx) - - if not in_brackets and (s == "." or s == ":") then - table.insert(parts, accumulator) - accumulator = '' - - search_index = idx + 1 - elseif s == "[" then - in_brackets = true - - table.insert(parts, accumulator) - accumulator = '' - - search_index = idx + 1 - elseif in_brackets then - if idx == bracket_end then - in_brackets = false - search_index = idx + 1 - - if string_char == "VAR" then - table.insert(parts, { accumulator }) - accumulator = '' - - string_char = nil - end - elseif not string_char then - bracket_end = string.find(lua_string, ']', idx, true) - - if s == '"' or s == "'" then - string_char = s - elseif s ~= ' ' then - string_char = "VAR" - accumulator = s - end - elseif string_char then - if string_char ~= s then - accumulator = accumulator .. s - else - table.insert(parts, accumulator) - accumulator = '' - - string_char = nil - end - end - else - accumulator = accumulator .. s - end - end - - parts = vim.tbl_filter(function(val) return #val > 0 end, parts) - - return parts, search_index -end - ----Prints given arguments in human-readable format. ----Example: ----
----  -- Print highlight group Normal and store it's contents in a variable.
----  local hl_normal = vim.pretty_print(vim.api.nvim_get_hl_by_name("Normal", true))
----
----@see |vim.inspect()| ----@return given arguments. -function vim.pretty_print(...) - local objects = {} - for i = 1, select('#', ...) do - local v = select(i, ...) - table.insert(objects, vim.inspect(v)) - end - - print(table.concat(objects, ' ')) - return ... -end - - -require('vim._meta') - -return vim -- cgit From 4d2744ffe30c785ff19624831e36d01e3f6a6089 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 27 Feb 2022 12:29:33 +0100 Subject: refactor: fix clang-tidy bugprone-signed-char-misuse warnings Prefer to declare variables with correct type instead of explicit casts wherever possible. --- src/cjson/lua_cjson.c | 2 +- src/nvim/charset.c | 2 +- src/nvim/debugger.c | 2 +- src/nvim/edit.c | 2 +- src/nvim/eval.c | 3 +-- src/nvim/eval/funcs.c | 11 +++++------ src/nvim/ex_docmd.c | 4 ++-- src/nvim/ex_eval.c | 2 +- src/nvim/getchar.c | 17 +++++++---------- src/nvim/main.c | 3 +-- src/nvim/search.c | 2 +- src/nvim/strings.c | 2 +- src/nvim/syntax.c | 2 +- 13 files changed, 24 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index b5f97bc485..c243f93c05 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -1110,7 +1110,7 @@ static int json_is_invalid_number(json_parse_t *json) /* Reject numbers starting with 0x, or leading zeros */ if (*p == '0') { - int ch2 = *(p + 1); + char ch2 = *(p + 1); if ((ch2 | 0x20) == 'x' || /* Hex */ ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index f4882e57e1..97aac67627 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1500,7 +1500,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, cons } else if ((what & (STR2NR_HEX | STR2NR_OCT | STR2NR_OOCT | STR2NR_BIN)) && !STRING_ENDED(ptr + 1) && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { - pre = ptr[1]; + pre = (char_u)ptr[1]; // Detect hexadecimal: 0x or 0X followed by hex digit. if ((what & STR2NR_HEX) && !STRING_ENDED(ptr + 2) diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index 75ebf0084e..b48a3155b6 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -201,7 +201,7 @@ void do_debug(char_u *cmd) if (last_cmd != 0) { // Check that the tail matches. p++; - while (*p != NUL && *p == *tail) { + while (*p != NUL && *p == (char_u)(*tail)) { p++; tail++; } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 00ffa7cba1..095e082f61 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -7776,7 +7776,7 @@ int hkmap(int c) case ';': c = 't'; break; default: { - static char str[] = "zqbcxlsjphmkwonu ydafe rig"; + static char_u str[] = "zqbcxlsjphmkwonu ydafe rig"; if (c < 'a' || c > 'z') { return c; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d95b9560c2..c5c03455b7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -4858,7 +4858,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval long numval; char_u *stringval; int opt_type; - int c; bool working = (**arg == '+'); // has("+option") int ret = OK; int opt_flags; @@ -4877,7 +4876,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return OK; } - c = *option_end; + char c = *option_end; *option_end = NUL; opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 49dde537c3..fb2465b9fd 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8296,7 +8296,7 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) // Repeat until {skip} returns false. for (;;) { subpatnum - = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); + = searchit(curwin, curbuf, &pos, NULL, dir, (char_u *)pat, 1, options, RE_SEARCH, &sia); // finding the first match again means there is no match where {skip} // evaluates to zero. if (firstpos.lnum != 0 && equalpos(pos, firstpos)) { @@ -9339,7 +9339,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) { static char *e_invact = N_("E927: Invalid action: '%s'"); const char *title = NULL; - int action = ' '; + char action = ' '; static int recursive = 0; rettv->vval.v_number = -1; dict_T *what = NULL; @@ -9569,7 +9569,6 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c */ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int regname; bool append = false; MotionType yank_type; long block_len; @@ -9583,13 +9582,13 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strregname == NULL) { return; // Type error; errmsg already given. } - regname = (uint8_t)(*strregname); + char regname = (uint8_t)(*strregname); if (regname == 0 || regname == '@') { regname = '"'; } const typval_T *regcontents = NULL; - int pointreg = 0; + char pointreg = 0; if (argvars[1].v_type == VAR_DICT) { dict_T *const d = argvars[1].vval.v_dict; @@ -9759,7 +9758,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) static char *e_invact2 = N_("E962: Invalid action: '%s'"); win_T *wp; dict_T *d; - int action = 'r'; + char action = 'r'; rettv->vval.v_number = -1; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d3203bcee8..6e915d98dc 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -80,7 +80,7 @@ static char *e_no_such_user_defined_command_str = N_("E184: No such user-defined command: %s"); static char *e_no_such_user_defined_command_in_current_buffer_str - = N_("E1237: No such user-defined command in current buffer: %s"); + = N_("E1237: No such user-defined command in current buffer: %s"); static int quitmore = 0; static bool ex_pressedreturn = false; @@ -2835,7 +2835,7 @@ int modifier_len(char_u *cmd) for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) { int j; for (j = 0; p[j] != NUL; j++) { - if (p[j] != cmdmods[i].name[j]) { + if (p[j] != (char_u)cmdmods[i].name[j]) { break; } } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 851828afcf..6395bbc70b 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1602,7 +1602,7 @@ void ex_endtry(exarg_T *eap) { int idx; bool rethrow = false; - int pending = CSTP_NONE; + char pending = CSTP_NONE; void *rettv = NULL; cstack_T *const cstack = eap->cstack; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 8426cdb98c..22d957d03d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1907,9 +1907,6 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // complete match if (keylen >= 0 && keylen <= typebuf.tb_len) { char_u *map_str = NULL; - int save_m_expr; - int save_m_noremap; - int save_m_silent; // Write chars to script file(s). // Note: :lmap mappings are written *after* being applied. #5658 @@ -1946,9 +1943,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // Copy the values from *mp that are used, because evaluating the // expression may invoke a function that redefines the mapping, thereby // making *mp invalid. - save_m_expr = mp->m_expr; - save_m_noremap = mp->m_noremap; - save_m_silent = mp->m_silent; + char save_m_expr = mp->m_expr; + int save_m_noremap = mp->m_noremap; + char save_m_silent = mp->m_silent; char_u *save_m_keys = NULL; // only saved when needed char_u *save_m_str = NULL; // only saved when needed LuaRef save_m_luaref = mp->m_luaref; @@ -2661,9 +2658,9 @@ int fix_input_buffer(char_u *buf, int len) /// @param[in] orig_rhs_len `strlen` of orig_rhs. /// @param[in] cpo_flags See param docs for @ref replace_termcodes. /// @param[out] mapargs MapArguments struct holding the replaced strings. -void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, - const char_u *orig_rhs, const size_t orig_rhs_len, - LuaRef rhs_lua, int cpo_flags, MapArguments *mapargs) +void set_maparg_lhs_rhs(const char_u *orig_lhs, const size_t orig_lhs_len, const char_u *orig_rhs, + const size_t orig_rhs_len, LuaRef rhs_lua, int cpo_flags, + MapArguments *mapargs) { char_u *lhs_buf = NULL; char_u *rhs_buf = NULL; @@ -3988,7 +3985,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol) /// special characters. /// /// @param c NUL or typed character for abbreviation -static char_u *eval_map_expr(mapblock_T *mp, int c) +static char_u *eval_map_expr(mapblock_T *mp, int c) { char_u *res; char_u *p = NULL; diff --git a/src/nvim/main.c b/src/nvim/main.c index ae25ff63dd..7281809c06 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -823,7 +823,6 @@ static void command_line_scan(mparm_T *parmp) bool had_stdin_file = false; // found explicit "-" argument bool had_minmin = false; // found "--" argument int want_argument; // option argument with argument - int c; long n; argc--; @@ -845,7 +844,7 @@ static void command_line_scan(mparm_T *parmp) // Optional argument. } else if (argv[0][0] == '-' && !had_minmin) { want_argument = false; - c = argv[0][argv_idx++]; + char c = argv[0][argv_idx++]; switch (c) { case NUL: // "nvim -" read from stdin if (exmode_active) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 682fa417a9..e6b47e75b2 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1071,7 +1071,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, * Find out the direction of the search. */ if (dirc == 0) { - dirc = spats[0].off.dir; + dirc = (char_u)spats[0].off.dir; } else { spats[0].off.dir = dirc; set_vv_searchforward(); diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 291138ef23..9d4c64e4b1 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -354,7 +354,7 @@ char *strcase_save(const char *const orig, bool upper) int l = utf_ptr2len((const char_u *)p); if (c == 0) { // overlong sequence, use only the first byte - c = *p; + c = (char_u)(*p); l = 1; } int uc = upper ? mb_toupper(c) : mb_tolower(c); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index db10b71d38..1e7801faee 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -4208,7 +4208,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha p = flagtab[fidx].name; int i; for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) { - if (arg[len] != p[i] && arg[len] != p[i + 1]) { + if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) { break; } } -- cgit From 7fd1182c62d6e969ac15b3891bfcc4ff480d6953 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 5 Mar 2022 19:16:14 +0300 Subject: fix: bounds check for underdot --- src/nvim/eval/funcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b688e087ed..8a1b6f081b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11410,14 +11410,14 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } break; case 'u': { - int len = STRLEN(what); + const size_t len = STRLEN(what); if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline p = highlight_has_attr(id, HL_UNDERLINELINE, modec); - } else if (len > 5 && TOLOWER_ASC(what[6]) == 'o') { // underdot + } else if (len > 6 && TOLOWER_ASC(what[6]) == 'o') { // underdot p = highlight_has_attr(id, HL_UNDERDOT, modec); } else { // underdash p = highlight_has_attr(id, HL_UNDERDASH, modec); -- cgit From 30e4cc3b3f2133e9a7170da9da8175832681f39a Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 3 Jan 2022 12:22:13 +0000 Subject: feat(decorations): support signs Add the following options to extmarks: - sign_text - sign_hl_group - number_hl_group - line_hl_group - cursorline_hl_group Note: ranges are unsupported and decorations are only applied to start_row --- src/nvim/api/extmark.c | 56 ++++++++++++- src/nvim/api/keysets.lua | 5 ++ src/nvim/api/private/helpers.c | 36 +++++++++ src/nvim/buffer.c | 19 +++++ src/nvim/buffer_defs.h | 1 + src/nvim/decoration.c | 174 +++++++++++++++++++++++++++++++++++++++-- src/nvim/decoration.h | 19 ++++- src/nvim/extmark.c | 7 +- src/nvim/marktree.h | 5 ++ src/nvim/screen.c | 8 +- src/nvim/sign.c | 2 + src/nvim/sign_defs.h | 1 + 12 files changed, 316 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 3a968f07ab..0ee8134ec4 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -445,6 +445,27 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - strict: boolean that indicates extmark should not be placed /// if the line or column value is past the end of the /// buffer or end of the line respectively. Defaults to true. +/// - sign_text: string of length 1-2 used to display in the +/// sign column. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - sign_hl_group: name of the highlight group used to +/// highlight the sign column text. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - number_hl_group: name of the highlight group used to +/// highlight the number column. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - line_hl_group: name of the highlight group used to +/// highlight the whole line. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row +/// - cursorline_hl_group: name of the highlight group used to +/// highlight the line when the cursor is on the same line +/// as the mark and 'cursorline' is enabled. +/// Note: ranges are unsupported and decorations are only +/// applied to start_row /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -519,10 +540,25 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - if (HAS_KEY(opts->hl_group)) { - decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err); - if (ERROR_SET(err)) { - goto error; + struct { + const char *name; + Object *opt; + int *dest; + } hls[] = { + { "hl_group" , &opts->hl_group , &decor.hl_id }, + { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id }, + { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id }, + { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id }, + { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id }, + { NULL, NULL, NULL }, + }; + + for (int j = 0; hls[j].name && hls[j].dest; j++) { + if (HAS_KEY(*hls[j].opt)) { + *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err); + if (ERROR_SET(err)) { + goto error; + } } } @@ -622,6 +658,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } + if (opts->sign_text.type == kObjectTypeString) { + if (!init_sign_text(&decor.sign_text, + (char_u *)opts->sign_text.data.string.data)) { + api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value"); + goto error; + } + } else if (HAS_KEY(opts->sign_text)) { + api_set_error(err, kErrorTypeValidation, "sign_text is not a String"); + goto error; + } + bool right_gravity = true; OPTION_TO_BOOL(right_gravity, right_gravity, true); @@ -709,6 +756,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer error: clear_virttext(&decor.virt_text); + xfree(decor.sign_text); return 0; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index b05816f8ac..856b336a98 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -22,6 +22,11 @@ return { "virt_lines_above"; "virt_lines_leftcol"; "strict"; + "sign_text"; + "sign_hl_group"; + "number_hl_group"; + "line_hl_group"; + "cursorline_hl_group"; }; keymap = { "noremap"; diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 35e8589255..8056950e26 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id) } return old_current_sctx; } + +// adapted from sign.c:sign_define_init_text. +// TODO(lewis6991): Consider merging +int init_sign_text(char_u **sign_text, char_u *text) +{ + char_u *s; + + char_u *endp = text + (int)STRLEN(text); + + // Count cells and check for non-printable chars + int cells = 0; + for (s = text; s < endp; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be empty, one or two display cells + if (s != endp || cells > 2) { + return FAIL; + } + if (cells < 1) { + return OK; + } + + // Allocate one byte more if we need to pad up + // with a space. + size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); + *sign_text = vim_strnsave(text, len); + + if (cells == 1) { + STRCPY(*sign_text + len - 1, " "); + } + + return OK; +} diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9e82b4e80b..084e18c6cb 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -36,6 +36,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/digraph.h" +#include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -5469,6 +5470,11 @@ static int buf_signcols_inner(buf_T *buf, int maximum) FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->se_lnum > curline) { + // Counted all signs, now add extmark signs + if (curline > 0) { + linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, + maximum-linesum); + } if (linesum > signcols) { signcols = linesum; if (signcols >= maximum) { @@ -5483,6 +5489,19 @@ static int buf_signcols_inner(buf_T *buf, int maximum) } } + if (curline > 0) { + linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum); + } + if (linesum > signcols) { + signcols = linesum; + if (signcols >= maximum) { + return maximum; + } + } + + // Check extmarks between signs + linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count-1, maximum); + if (linesum > signcols) { signcols = linesum; if (signcols >= maximum) { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 1e0c837056..c7afa5f9d0 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -875,6 +875,7 @@ struct file_buffer { MarkTree b_marktree[1]; Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces size_t b_virt_line_blocks; // number of virt_line blocks + size_t b_signs; // number of sign extmarks // array of channel_id:s which have asked to receive updates for this // buffer. diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 935b233752..6c006b7fe0 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -5,6 +5,7 @@ #include "nvim/extmark.h" #include "nvim/highlight.h" #include "nvim/lua/executor.h" +#include "nvim/move.h" #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/vim.h" @@ -65,8 +66,15 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { - if ((!decor || decor->hl_id) && row2 >= row1) { - redraw_buf_range_later(buf, row1+1, row2+1); + if (row2 >= row1) { + if (decor && decor->sign_text) { + buf->b_signcols_valid = false; + changed_line_abv_curs(); + } + + if (!decor || decor->hl_id || decor_has_sign(decor)) { + redraw_buf_range_later(buf, row1+1, row2+1); + } } if (decor && kv_size(decor->virt_text)) { @@ -82,9 +90,15 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) { decor_redraw(buf, row, row2, decor); - if (decor && kv_size(decor->virt_lines)) { - assert(buf->b_virt_line_blocks > 0); - buf->b_virt_line_blocks--; + if (decor) { + if (kv_size(decor->virt_lines)) { + assert(buf->b_virt_line_blocks > 0); + buf->b_virt_line_blocks--; + } + if (decor_has_sign(decor)) { + assert(buf->b_signs > 0); + buf->b_signs--; + } } decor_free(decor); } @@ -97,6 +111,7 @@ void decor_free(Decoration *decor) clear_virttext(&kv_A(decor->virt_lines, i).line); } kv_destroy(decor->virt_lines); + xfree(decor->sign_text); xfree(decor); } } @@ -136,6 +151,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) { state->row = -1; state->buf = buf; + state->has_sign_decor = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); if (item.virt_text_owned) { @@ -180,9 +196,23 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) Decoration decor = get_decor(mark); + // Exclude non-paired marks unless they contain virt_text or a sign + if (!mt_paired(mark) + && !kv_size(decor.virt_text) + && !decor_has_sign(&decor)) { + goto next_mark; + } + + // Don't add signs for end marks as the start mark has already been added. + if (mt_end(mark) && decor_has_sign(&decor)) { + goto next_mark; + } + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); - if ((!mt_end(mark) && altpos.row < top_row + // Exclude start marks if the end mark position is above the top row + // Exclude end marks if we have already added the start mark + if ((mt_start(mark) && altpos.row < top_row && !kv_size(decor.virt_text)) || (mt_end(mark) && altpos.row >= top_row)) { goto next_mark; @@ -241,6 +271,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r kv_A(state->active, index) = kv_A(state->active, index-1); } kv_A(state->active, index) = range; + + if (decor_has_sign(decor)) { + state->has_sign_decor = true; + } } int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state) @@ -294,7 +328,8 @@ next_mark: bool active = false, keep = true; if (item.end_row < state->row || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) { + if (!(item.start_row >= state->row && kv_size(item.decor.virt_text)) + && !decor_has_sign(&item.decor)) { keep = false; } } else { @@ -329,6 +364,131 @@ next_mark: return attr; } +void decor_redraw_signs(buf_T *buf, DecorState *state, int row, + int *num_signs, sign_attrs_T sattrs[]) +{ + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange item = kv_A(state->active, i); + Decoration *decor = &item.decor; + + if (!decor_has_sign(decor)) { + continue; + } + + if (state->row != item.start_row) { + continue; + } + + int j; + for (j = (*num_signs); j > 0; j--) { + if (sattrs[j].sat_prio <= decor->priority) { + break; + } + sattrs[j] = sattrs[j-1]; + } + if (j < SIGN_SHOW_MAX) { + memset(&sattrs[j], 0, sizeof(sign_attrs_T)); + sattrs[j].sat_text = decor->sign_text; + if (decor->sign_hl_id != 0) { + sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id); + } + if (decor->number_hl_id != 0) { + sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id); + } + if (decor->line_hl_id != 0) { + sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id); + } + if (decor->cursorline_hl_id != 0) { + sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id); + } + sattrs[j].sat_prio = decor->priority; + (*num_signs)++; + } + } +} + +// Get the maximum required amount of sign columns needed between row and +// end_row. +int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max) +{ + int count = 0; // count for the number of signs on a given row + int count_remove = 0; // how much to decrement count by when iterating marks for a new row + int signcols = 0; // highest value of count + int currow = -1; // current row + + if (max <= 1 && buf->b_signs >= (size_t)max) { + return max; + } + + if (buf->b_signs == 0) { + return 0; + } + + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, 0, -1, itr); + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > end_row) { + break; + } + + if ((mark.pos.row < row && mt_end(mark)) + || marktree_decor_level(mark) < kDecorLevelVisible + || !mark.decor_full) { + goto next_mark; + } + + Decoration decor = get_decor(mark); + + if (!decor.sign_text) { + goto next_mark; + } + + if (mark.pos.row > currow) { + count -= count_remove; + count_remove = 0; + currow = mark.pos.row; + } + + if (!mt_paired(mark)) { + if (mark.pos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + count_remove++; + } + goto next_mark; + } + + mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); + + if (mt_end(mark)) { + if (mark.pos.row >= row && altpos.row <= end_row) { + count_remove++; + } + } else { + if (altpos.row >= row) { + count++; + if (count > signcols) { + signcols = count; + if (signcols >= max) { + return max; + } + } + } + } + +next_mark: + marktree_itr_next(buf->b_marktree, itr); + } + + return signcols; +} + void decor_redraw_end(DecorState *state) { state->buf = NULL; diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 02472d09e4..7e6dbdf1a7 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -47,13 +47,18 @@ struct Decoration { bool virt_text_hide; bool hl_eol; bool virt_lines_above; - // TODO(bfredl): style, signs, etc + // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col int virt_text_width; // width of virt_text + char_u *sign_text; + int sign_hl_id; + int number_hl_id; + int line_hl_id; + int cursorline_hl_id; }; #define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, DECOR_PRIORITY_BASE, 0, 0 } + false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 } typedef struct { int start_row; @@ -75,6 +80,7 @@ typedef struct { int col_until; int current; int eol_col; + bool has_sign_decor; } DecorState; typedef struct { @@ -98,6 +104,15 @@ EXTERN bool provider_active INIT(= false); LUA_NOREF, LUA_NOREF, LUA_NOREF, \ LUA_NOREF, -1 } +static inline bool decor_has_sign(Decoration *decor) +{ + return decor->sign_text + || decor->sign_hl_id + || decor->number_hl_id + || decor->line_hl_id + || decor->cursorline_hl_id; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "decoration.h.generated.h" #endif diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 48e57e20e1..e1f1ed7d13 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -67,7 +67,9 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col uint8_t decor_level = kDecorLevelNone; // no decor if (decor) { - if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) { + if (kv_size(decor->virt_text) + || kv_size(decor->virt_lines) + || decor_has_sign(decor)) { decor_full = true; decor = xmemdup(decor, sizeof *decor); } @@ -142,6 +144,9 @@ revised: if (kv_size(decor->virt_lines)) { buf->b_virt_line_blocks++; } + if (decor_has_sign(decor)) { + buf->b_signs++; + } decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 30f5aacebc..440816930b 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -87,6 +87,11 @@ static inline bool mt_end(mtkey_t key) return key.flags & MT_FLAG_END; } +static inline bool mt_start(mtkey_t key) +{ + return mt_paired(key) && !mt_end(key); +} + static inline bool mt_right(mtkey_t key) { return key.flags & MT_FLAG_RIGHT_GRAVITY; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7fafe3dd6e..ee1acd8d03 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2208,6 +2208,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc buf_T *buf = wp->w_buffer; bool end_fill = (lnum == buf->b_ml.ml_line_count+1); + has_decor = decor_redraw_line(buf, lnum-1, &decor_state); + if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. @@ -2229,9 +2231,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - has_decor = decor_redraw_line(wp->w_buffer, lnum-1, - &decor_state); - for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_line != LUA_NOREF) { @@ -2460,6 +2459,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc memset(sattrs, 0, sizeof(sattrs)); num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs); + if (decor_state.has_sign_decor) { + decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs); + } // If this line has a sign with line highlighting set line_attr. // TODO(bfredl, vigoux): this should not take priority over decoration! diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 8b41781c98..ad48003f3b 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -506,6 +506,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) if (sp->sn_num_hl != 0) { sattr.sat_numhl = syn_id2attr(sp->sn_num_hl); } + // Store the priority so we can mesh in extmark signs later + sattr.sat_prio = sign->se_priority; } sattrs[nr_matches] = sattr; diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index c734502878..6d28c1849a 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -40,6 +40,7 @@ typedef struct sign_attrs_S { int sat_linehl; int sat_culhl; int sat_numhl; + int sat_prio; // Used for inserting extmark signs } sign_attrs_T; #define SIGN_SHOW_MAX 9 -- cgit From 92349b1db0039aac3a43089d0aade2437164505c Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sun, 6 Mar 2022 12:35:14 -0700 Subject: feat(api): add 'buffer' argument to nvim_get_autocmds (#17594) This enables retrieving autocommands defined in the given buffers. Under the hood this simply translates the buffer numbers into '' patterns. --- src/nvim/api/autocmd.c | 66 ++++++++++++++++++++++++++++++++++++++++++++---- src/nvim/api/keysets.lua | 1 + 2 files changed, 62 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 0ad7b320d0..685667ac64 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -41,7 +41,9 @@ static int64_t next_autocmd_id = 1; /// @param opts Optional Parameters: /// - event : Name or list of name of events to match against /// - group (string): Name of group to match against -/// - pattern: Pattern or list of patterns to match against +/// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} +/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands +/// |autocmd-buflocal|. Cannot be used with {pattern} /// /// @return A list of autocmds that match Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) @@ -53,6 +55,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) char_u *pattern_filters[AUCMD_MAX_PATTERNS]; char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + Array buffers = ARRAY_DICT_INIT; + bool event_set[NUM_EVENTS] = { false }; bool check_event = false; @@ -100,6 +104,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } } + if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Cannot use both 'pattern' and 'buffer'"); + goto cleanup; + } + int pattern_filter_count = 0; if (opts->pattern.type != kObjectTypeNil) { Object v = opts->pattern; @@ -107,25 +117,70 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) pattern_filters[pattern_filter_count] = (char_u *)v.data.string.data; pattern_filter_count += 1; } else if (v.type == kObjectTypeArray) { + if (v.data.array.size > AUCMD_MAX_PATTERNS) { + api_set_error(err, kErrorTypeValidation, + "Too many patterns. Please limit yourself to %d or fewer", + AUCMD_MAX_PATTERNS); + goto cleanup; + } + FOREACH_ITEM(v.data.array, item, { + if (item.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'pattern': must be a string"); + goto cleanup; + } + pattern_filters[pattern_filter_count] = (char_u *)item.data.string.data; pattern_filter_count += 1; }); } else { - api_set_error(err, - kErrorTypeValidation, + api_set_error(err, kErrorTypeValidation, "Not a valid 'pattern' value. Must be a string or an array"); goto cleanup; } + } + + if (opts->buffer.type == kObjectTypeInteger || opts->buffer.type == kObjectTypeBuffer) { + buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err); + if (ERROR_SET(err)) { + goto cleanup; + } - if (pattern_filter_count >= AUCMD_MAX_PATTERNS) { + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + } else if (opts->buffer.type == kObjectTypeArray) { + if (opts->buffer.data.array.size > AUCMD_MAX_PATTERNS) { api_set_error(err, kErrorTypeValidation, - "Too many patterns. Please limit yourself to less"); + "Too many buffers. Please limit yourself to %d or fewer", AUCMD_MAX_PATTERNS); goto cleanup; } + + FOREACH_ITEM(opts->buffer.data.array, bufnr, { + if (bufnr.type != kObjectTypeInteger && bufnr.type != kObjectTypeBuffer) { + api_set_error(err, kErrorTypeValidation, "Invalid value for 'buffer': must be an integer"); + goto cleanup; + } + + buf_T *buf = find_buffer_by_handle((Buffer)bufnr.data.integer, err); + if (ERROR_SET(err)) { + goto cleanup; + } + + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); + ADD(buffers, CSTR_TO_OBJ((char *)pattern_buflocal)); + }); + } else if (opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Invalid value for 'buffer': must be an integer or array of integers"); + goto cleanup; } + FOREACH_ITEM(buffers, bufnr, { + pattern_filters[pattern_filter_count] = (char_u *)bufnr.data.string.data; + pattern_filter_count += 1; + }); + FOR_ALL_AUEVENTS(event) { if (check_event && !event_set[event]) { continue; @@ -234,6 +289,7 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) } cleanup: + api_free_array(buffers); return autocmd_list; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 32d4e98822..435e8195dd 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -142,6 +142,7 @@ return { "event"; "group"; "pattern"; + "buffer"; }; create_augroup = { "clear"; -- cgit From 96bb1784a61daec4535edf291303225aa1fa01e0 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sun, 6 Mar 2022 22:56:41 +0300 Subject: fix(api): highlight attribute for underline This commit fixes regression introduced in c365de1 when checking for highlight attribute for underline was returning '0' when it was present Fixes #17624. --- src/nvim/eval/funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index df1889b12d..7da4fe62e8 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11411,7 +11411,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) case 'u': { const size_t len = STRLEN(what); if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline - p = highlight_has_attr(id, HL_UNDERCURL, modec); + p = highlight_has_attr(id, HL_UNDERLINE, modec); } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline -- cgit From 8e7446b3cbc5c82706f41d701239fa18ab5b2808 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sun, 6 Mar 2022 21:45:26 +0000 Subject: refactor(signcol): smarter invalidation (#17533) Previously b_signcols was invalidated whenever a sign was added/removed or when a buffer line was added/removed. This change introduces a sentinel linenr_T into the buffer state which is a line number used to determine the signcolumn. With this information, we can invalidate the signcolumn less often. Now the signcolumn is only invalidated when a sign or line at the sentinel line number is removed. --- src/nvim/buffer.c | 92 ++++++++++++++++++++++++++++++++++++++++++++------ src/nvim/buffer_defs.h | 9 +++-- src/nvim/decoration.c | 9 +++-- src/nvim/extmark.c | 4 +++ src/nvim/sign.c | 16 ++++++--- 5 files changed, 107 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 084e18c6cb..2bd14e2103 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1737,7 +1737,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl buf = xcalloc(1, sizeof(buf_T)); // init b: variables buf->b_vars = tv_dict_alloc(); - buf->b_signcols_valid = false; + buf->b_signcols.valid = false; init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -5468,6 +5468,8 @@ static int buf_signcols_inner(buf_T *buf, int maximum) int linesum = 0; linenr_T curline = 0; + buf->b_signcols.sentinel = 0; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->se_lnum > curline) { // Counted all signs, now add extmark signs @@ -5475,13 +5477,14 @@ static int buf_signcols_inner(buf_T *buf, int maximum) linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum); } + curline = sign->se_lnum; if (linesum > signcols) { signcols = linesum; + buf->b_signcols.sentinel = curline; if (signcols >= maximum) { return maximum; } } - curline = sign->se_lnum; linesum = 0; } if (sign->se_has_text_or_icon) { @@ -5504,6 +5507,7 @@ static int buf_signcols_inner(buf_T *buf, int maximum) if (linesum > signcols) { signcols = linesum; + buf->b_signcols.sentinel = curline; if (signcols >= maximum) { return maximum; } @@ -5512,28 +5516,96 @@ static int buf_signcols_inner(buf_T *buf, int maximum) return signcols; } +/// Invalidate the signcolumn if needed after deleting +/// signs between line1 and line2 (inclusive). +/// +/// @param buf buffer to check +/// @param line1 start of region being deleted +/// @param line2 end of region being deleted +void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2) +{ + if (!buf->b_signcols.valid) { + return; + } + + if (!buf->b_signcols.sentinel) { + buf->b_signcols.valid = false; + return; + } + + linenr_T sent = buf->b_signcols.sentinel; + + if (sent >= line1 && sent <= line2) { + // Only invalidate when removing signs at the sentinel line. + buf->b_signcols.valid = false; + } +} + +/// Re-calculate the signcolumn after adding a sign. +/// +/// @param buf buffer to check +/// @param added sign being added +void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) +{ + if (!buf->b_signcols.valid) { + return; + } + + if (!added || !buf->b_signcols.sentinel) { + buf->b_signcols.valid = false; + return; + } + + if (added->se_lnum == buf->b_signcols.sentinel) { + if (buf->b_signcols.size == buf->b_signcols.max) { + buf->b_signcols.max++; + } + buf->b_signcols.size++; + return; + } + + sign_entry_T *s; + + // Get first sign for added lnum + for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {} + + // Count signs for lnum + int linesum = 1; + for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) { + linesum++; + } + linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum-1, (int)s->se_lnum-1, + SIGN_SHOW_MAX-linesum); + + if (linesum > buf->b_signcols.size) { + buf->b_signcols.size = linesum; + buf->b_signcols.max = linesum; + buf->b_signcols.sentinel = added->se_lnum; + } +} + int buf_signcols(buf_T *buf, int maximum) { // The maximum can be determined from 'signcolumn' which is window scoped so // need to invalidate signcols if the maximum is greater than the previous // maximum. - if (maximum > buf->b_signcols_max) { - buf->b_signcols_valid = false; + if (maximum > buf->b_signcols.max) { + buf->b_signcols.valid = false; } - if (!buf->b_signcols_valid) { + if (!buf->b_signcols.valid) { int signcols = buf_signcols_inner(buf, maximum); // Check if we need to redraw - if (signcols != buf->b_signcols) { - buf->b_signcols = signcols; - buf->b_signcols_max = maximum; + if (signcols != buf->b_signcols.size) { + buf->b_signcols.size = signcols; + buf->b_signcols.max = maximum; redraw_buf_later(buf, NOT_VALID); } - buf->b_signcols_valid = true; + buf->b_signcols.valid = true; } - return buf->b_signcols; + return buf->b_signcols.size; } // Get "buf->b_fname", use "[No Name]" if it is NULL. diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index c7afa5f9d0..7acb646980 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -862,9 +862,12 @@ struct file_buffer { // may use a different synblock_T. sign_entry_T *b_signlist; // list of placed signs - int b_signcols; // last calculated number of sign columns - bool b_signcols_valid; // calculated sign columns is valid - int b_signcols_max; // Maximum value b_signcols is valid for. + struct { + int size; // last calculated number of sign columns + bool valid; // calculated sign columns is valid + linenr_T sentinel; // a line number which is holding up the signcolumn + int max; // Maximum value size is valid for. + } b_signcols; Terminal *terminal; // Terminal instance associated with the buffer diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 6c006b7fe0..619e8fdadc 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -1,6 +1,7 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include "nvim/buffer.h" #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" @@ -67,11 +68,6 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { if (row2 >= row1) { - if (decor && decor->sign_text) { - buf->b_signcols_valid = false; - changed_line_abv_curs(); - } - if (!decor || decor->hl_id || decor_has_sign(decor)) { redraw_buf_range_later(buf, row1+1, row2+1); } @@ -99,6 +95,9 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor) assert(buf->b_signs > 0); buf->b_signs--; } + if (row2 >= row && decor->sign_text) { + buf_signcols_del_check(buf, row+1, row2+1); + } } decor_free(decor); } diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index e1f1ed7d13..2916e3e978 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -147,6 +147,10 @@ revised: if (decor_has_sign(decor)) { buf->b_signs++; } + if (decor->sign_text) { + // TODO(lewis6991): smarter invalidation + buf_signcols_add_check(buf, NULL); + } decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index ad48003f3b..6d0ac30003 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -196,7 +196,8 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int if (next != NULL) { next->se_prev = newsign; } - buf->b_signcols_valid = false; + + buf_signcols_add_check(buf, newsign); if (prev == NULL) { // When adding first sign need to redraw the windows to create the @@ -541,7 +542,6 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) sign_entry_T *next; // the next sign in a b_signlist linenr_T lnum; // line number whose sign was deleted - buf->b_signcols_valid = false; lastp = &buf->b_signlist; lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { @@ -554,6 +554,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) next->se_prev = sign->se_prev; } lnum = sign->se_lnum; + buf_signcols_del_check(buf, lnum, lnum); if (sign->se_group != NULL) { sign_group_unref(sign->se_group->sg_name); } @@ -675,7 +676,7 @@ void buf_delete_signs(buf_T *buf, char_u *group) lastp = &sign->se_next; } } - buf->b_signcols_valid = false; + buf_signcols_del_check(buf, 1, MAXLNUM); } /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. @@ -737,14 +738,19 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a int is_fixed = 0; int signcol = win_signcol_configured(curwin, &is_fixed); - curbuf->b_signcols_valid = false; + bool delete = amount == MAXLNUM; + + if (delete) { + buf_signcols_del_check(curbuf, line1, line2); + } + lastp = &curbuf->b_signlist; for (sign = curbuf->b_signlist; sign != NULL; sign = next) { next = sign->se_next; new_lnum = sign->se_lnum; if (sign->se_lnum >= line1 && sign->se_lnum <= line2) { - if (amount != MAXLNUM) { + if (!delete) { new_lnum += amount; } else if (!is_fixed || signcol >= 2) { *lastp = next; -- cgit From 918ab6bf0064d5d147d3b10a56fb4f95d355c171 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 6 Mar 2022 13:19:34 +0100 Subject: ci(clint): remove "Unknown NOLINT error category" warning Clang-tidy also uses "NOLINT" to suppress warnings which causes a conflict. --- src/clint.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/clint.py b/src/clint.py index 4b7bf002e6..37b44920af 100755 --- a/src/clint.py +++ b/src/clint.py @@ -191,7 +191,6 @@ _ERROR_CATEGORIES = [ 'readability/fn_size', 'readability/multiline_comment', 'readability/multiline_string', - 'readability/nolint', 'readability/nul', 'readability/todo', 'readability/utf8', @@ -298,9 +297,6 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error): if category in _ERROR_CATEGORIES: _error_suppressions.setdefault( category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) def ParseKnownErrorSuppressions(filename, raw_lines, linenum): -- cgit From ff032f2710974dcf5930187f1925534da93db199 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 6 Mar 2022 12:40:31 +0100 Subject: refactor: remove redundant casts --- src/nvim/eval.c | 2 +- src/nvim/eval/funcs.c | 8 ++++---- src/nvim/eval/userfunc.c | 8 ++++---- src/nvim/ex_cmds2.c | 3 +-- src/nvim/ex_eval.c | 2 +- src/nvim/fileio.c | 6 +++--- src/nvim/getchar.c | 2 +- src/nvim/highlight.c | 2 +- src/nvim/marktree.c | 4 ++-- src/nvim/memline.c | 4 ++-- src/nvim/option.c | 8 ++++---- src/nvim/regexp.c | 3 +-- src/nvim/spell.c | 2 +- src/nvim/ui.c | 4 ++-- 14 files changed, 28 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c5c03455b7..aa7b9e80a4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9441,7 +9441,7 @@ void new_script_vars(scid_T id) hashtab_T *ht; scriptvar_T *sv; - ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)); + ga_grow(&ga_scripts, id - ga_scripts.ga_len); { /* Re-allocating ga_data means that an ht_array pointing to * ht_smallarray becomes invalid. We can recognize this: ht_mask is diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index df1889b12d..d207dcb527 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11413,13 +11413,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (len <= 5 || (TOLOWER_ASC(what[5]) == 'l' && len <= 9)) { // underline p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (TOLOWER_ASC(what[5]) == 'c') { // undercurl - p = highlight_has_attr(id, HL_UNDERCURL, modec); + p = highlight_has_attr(id, HL_UNDERCURL, modec); } else if (len > 9 && TOLOWER_ASC(what[9]) == 'l') { // underlineline - p = highlight_has_attr(id, HL_UNDERLINELINE, modec); + p = highlight_has_attr(id, HL_UNDERLINELINE, modec); } else if (len > 6 && TOLOWER_ASC(what[6]) == 'o') { // underdot - p = highlight_has_attr(id, HL_UNDERDOT, modec); + p = highlight_has_attr(id, HL_UNDERDOT, modec); } else { // underdash - p = highlight_has_attr(id, HL_UNDERDASH, modec); + p = highlight_has_attr(id, HL_UNDERDASH, modec); } break; } diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 5764f9fbd4..471c4092fe 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -516,7 +516,7 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf if (llen > 0) { fname_buf[0] = K_SPECIAL; fname_buf[1] = KS_EXTRA; - fname_buf[2] = (int)KE_SNR; + fname_buf[2] = KE_SNR; int i = 3; if (eval_fname_sid((const char *)name)) { // "" or "s:" if (current_sctx.sc_sid <= 0) { @@ -1713,7 +1713,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, // Check for hard coded : already translated function ID (from a user // command). if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA - && (*pp)[2] == (int)KE_SNR) { + && (*pp)[2] == KE_SNR) { *pp += 3; len = get_id_len((const char **)pp) + 3; return (char_u *)xmemdupz(start, len); @@ -1821,7 +1821,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, // Change "" to the byte sequence. name[0] = K_SPECIAL; name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; + name[2] = KE_SNR; memmove(name + 3, name + 5, strlen((char *)name + 5) + 1); } goto theend; @@ -1888,7 +1888,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, if (!skip && lead > 0) { name[0] = K_SPECIAL; name[1] = KS_EXTRA; - name[2] = (int)KE_SNR; + name[2] = KE_SNR; if (sid_buf_len > 0) { // If it's "" memcpy(name + 3, sid_buf, sid_buf_len); } diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 8666c7e33a..2a40e50014 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1796,7 +1796,7 @@ scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out) if (sid_out != NULL) { *sid_out = sid; } - ga_grow(&script_items, (int)(sid - script_items.ga_len)); + ga_grow(&script_items, sid - script_items.ga_len); while (script_items.ga_len < sid) { script_items.ga_len++; SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; @@ -2188,7 +2188,6 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) } - /// ":scriptnames" void ex_scriptnames(exarg_T *eap) { diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 6395bbc70b..25b6aa7d8a 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -430,7 +430,7 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int STRCAT(val, p); p[-2] = NUL; - sprintf((char *)(val + STRLEN(p)), " (%s)", &mesg[1]); + snprintf(val + STRLEN(p), strlen(" (%s)"), " (%s)", &mesg[1]); p[-2] = '"'; } break; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 965aa8749d..c5a89d3371 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3911,13 +3911,13 @@ static int check_mtime(buf_T *buf, FileInfo *file_info) static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST { - return (long)file_info->stat.st_mtim.tv_nsec != mtime_ns + return file_info->stat.st_mtim.tv_nsec != mtime_ns #if defined(__linux__) || defined(MSWIN) // On a FAT filesystem, esp. under Linux, there are only 5 bits to store // the seconds. Since the roundoff is done when flushing the inode, the // time may change unexpectedly by one second!!! - || (long)file_info->stat.st_mtim.tv_sec - mtime > 1 - || mtime - (long)file_info->stat.st_mtim.tv_sec > 1; + || file_info->stat.st_mtim.tv_sec - mtime > 1 + || mtime - file_info->stat.st_mtim.tv_sec > 1; #else || (long)file_info->stat.st_mtim.tv_sec != mtime; #endif diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 22d957d03d..299456f688 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -4140,7 +4140,7 @@ int makemap(FILE *fd, buf_T *buf) } for (p = mp->m_str; *p != NUL; p++) { if (p[0] == K_SPECIAL && p[1] == KS_EXTRA - && p[2] == (int)KE_SNR) { + && p[2] == KE_SNR) { break; } } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index a01d8369ba..a1fd0d0d66 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -327,7 +327,7 @@ void update_window_hl(win_T *wp, bool invalid) wp->w_hl_attr_normal); } - for (int hlf = 0; hlf < (int)HLF_COUNT; hlf++) { + for (int hlf = 0; hlf < HLF_COUNT; hlf++) { int attr; if (wp->w_hl_ids[hlf] != 0) { attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false); diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 918db8b76c..d2354dbf6b 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -558,8 +558,8 @@ void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_ { // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms // once we upgrade to a non-broken version of gcc in functionaltest-lua CI - rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); - rawkey(itr).flags = (uint16_t)((uint16_t)rawkey(itr).flags + rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); + rawkey(itr).flags = (uint16_t)(rawkey(itr).flags | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET) | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)); rawkey(itr).decor_full = key.decor_full; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 004ef36b36..59f57aa667 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -303,7 +303,7 @@ int ml_open(buf_T *buf) b0p->b0_id[0] = BLOCK0_ID0; b0p->b0_id[1] = BLOCK0_ID1; - b0p->b0_magic_long = (long)B0_MAGIC_LONG; + b0p->b0_magic_long = B0_MAGIC_LONG; b0p->b0_magic_int = (int)B0_MAGIC_INT; b0p->b0_magic_short = (short)B0_MAGIC_SHORT; b0p->b0_magic_char = B0_MAGIC_CHAR; @@ -3709,7 +3709,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_ static int b0_magic_wrong(ZERO_BL *b0p) { - return b0p->b0_magic_long != (long)B0_MAGIC_LONG + return b0p->b0_magic_long != B0_MAGIC_LONG || b0p->b0_magic_int != (int)B0_MAGIC_INT || b0p->b0_magic_short != (short)B0_MAGIC_SHORT || b0p->b0_magic_char != B0_MAGIC_CHAR; diff --git a/src/nvim/option.c b/src/nvim/option.c index 9068c90dd1..a0706dfa14 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3851,7 +3851,7 @@ static bool parse_winhl_opt(win_T *wp) if (strncmp("Normal", p, nlen) == 0) { w_hl_id_normal = hl_id; } else { - for (hlf = 0; hlf < (int)HLF_COUNT; hlf++) { + for (hlf = 0; hlf < HLF_COUNT; hlf++) { if (strlen(hlf_names[hlf]) == nlen && strncmp(hlf_names[hlf], p, nlen) == 0) { w_hl_ids[hlf] = hl_id; @@ -5267,7 +5267,7 @@ static void showoptions(int all, int opt_flags) && Columns + GAP >= INT_MIN + 3 && (Columns + GAP - 3) / INC >= INT_MIN && (Columns + GAP - 3) / INC <= INT_MAX); - cols = (int)((Columns + GAP - 3) / INC); + cols = (Columns + GAP - 3) / INC; if (cols == 0) { cols = 1; } @@ -5666,11 +5666,11 @@ void comp_col(void) assert(sc_col >= 0 && INT_MIN + sc_col <= Columns && Columns - sc_col <= INT_MAX); - sc_col = (int)(Columns - sc_col); + sc_col = Columns - sc_col; assert(ru_col >= 0 && INT_MIN + ru_col <= Columns && Columns - ru_col <= INT_MAX); - ru_col = (int)(Columns - ru_col); + ru_col = Columns - ru_col; if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 6a6c915094..cac95e1def 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -5043,8 +5043,7 @@ static bool regmatch( } else { MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr); if (limit > 0 - && (long)(behind_pos.rs_u.ptr - - rp->rs_un.regsave.rs_u.ptr) > limit) { + && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) { no = FAIL; } } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 1296d410f6..fb644daa39 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3824,7 +3824,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // At end of a prefix or at start of prefixtree: check for // following word. - if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) { + if (byts[arridx] == 0 || n == STATE_NOPREFIX) { // Set su->su_badflags to the caps type at this position. // Use the caps type until here for the prefix itself. n = nofold_len(fword, sp->ts_fidx, su->su_badptr); diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 31b9614c34..7c67c058b0 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -639,8 +639,8 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) } } else { // non-positive indicates no request - wp->w_height_request = (int)MAX(height, 0); - wp->w_width_request = (int)MAX(width, 0); + wp->w_height_request = MAX(height, 0); + wp->w_width_request = MAX(width, 0); win_set_inner_size(wp); } } -- cgit From 8262de0b489d5b9b5f6c99c1016b62d4e72a899b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Mar 2022 07:57:31 +0800 Subject: vim-patch:8.2.3739: in wrong directory when using win_execute() with 'acd' set Problem: In wrong directory when using win_execute() with 'acd' set. Solution: Restore the directory when returning to the window. (closes vim/vim#9276) https://github.com/vim/vim/commit/dea4a616376c6500894c16e26057ce16d7ef9f0e --- src/nvim/testdir/test_autochdir.vim | 30 ++++++++++++++++++++++++++++++ src/nvim/window.c | 4 ++++ 2 files changed, 34 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 53ed4617f7..7f1ed34b36 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -26,6 +26,34 @@ func Test_set_filename() call delete('samples/Xtest') endfunc +func Test_set_filename_other_window() + CheckFunction test_autochdir + call ch_logfile('logfile', 'w') + let cwd = getcwd() + call test_autochdir() + call mkdir('Xa') + call mkdir('Xb') + call mkdir('Xc') + try + args Xa/aaa.txt Xb/bbb.txt + set acd + let winid = win_getid() + snext + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + call win_execute(winid, 'file ' .. cwd .. '/Xc/ccc.txt') + call assert_equal('Xb', substitute(getcwd(), '.*/\([^/]*\)$', '\1', '')) + finally + set noacd + call chdir(cwd) + call delete('Xa', 'rf') + call delete('Xb', 'rf') + call delete('Xc', 'rf') + bwipe! aaa.txt + bwipe! bbb.txt + bwipe! ccc.txt + endtry +endfunc + func Test_verbose_pwd() CheckFunction test_autochdir let cwd = getcwd() @@ -55,6 +83,8 @@ func Test_verbose_pwd() set noacd call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) wincmd w + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + execute 'cd' cwd call assert_match('\[global\].*testdir', execute('verbose pwd')) wincmd w call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) diff --git a/src/nvim/window.c b/src/nvim/window.c index 83048d911f..c4002e93e0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4680,6 +4680,10 @@ static void win_enter_ext(win_T *const wp, const int flags) /// Used after making another window the current one: change directory if needed. void fix_current_dir(void) { + if (p_acd) { + do_autochdir(); + return; + } // New directory is either the local directory of the window, tab or NULL. char *new_dir = (char *)(curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir); -- cgit From 1a5409db0d21c2cf4fe59c825ed73d8a64f62693 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Mar 2022 08:10:31 +0800 Subject: vim-patch:8.2.3745: autochdir test fails without the +channel feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Autochdir test fails without the +channel feature. Solution: Remove the ch_logfile() call. (Dominique Pellé, closes vim/vim#9281) https://github.com/vim/vim/commit/f661cee847d2c17652b0ad0d703d2e3ac8610265 --- src/nvim/testdir/test_autochdir.vim | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 7f1ed34b36..8f61224a13 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -28,7 +28,6 @@ endfunc func Test_set_filename_other_window() CheckFunction test_autochdir - call ch_logfile('logfile', 'w') let cwd = getcwd() call test_autochdir() call mkdir('Xa') -- cgit From da9bc96152efedfa80fabf0d1aabe52b7269181e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Mar 2022 08:11:52 +0800 Subject: vim-patch:8.2.3920: restoring directory after using another window is inefficient Problem: Restoring directory after using another window is inefficient. Solution: Only restore the directory for win_execute(). Apply 'autochdir' only when needed. https://github.com/vim/vim/commit/90c317f2246a7fb4bd4e3feb0778b53627bc9fad --- src/nvim/testdir/test_autochdir.vim | 21 +++++++++++++++++++++ src/nvim/window.c | 3 --- src/nvim/window.h | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index 8f61224a13..de5c124908 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -53,6 +53,27 @@ func Test_set_filename_other_window() endtry endfunc +func Test_acd_win_execute() + CheckFunction test_autochdir + let cwd = getcwd() + set acd + call test_autochdir() + + call mkdir('Xfile') + let winid = win_getid() + new Xfile/file + call assert_match('testdir.Xfile$', getcwd()) + cd .. + call assert_match('testdir$', getcwd()) + call win_execute(winid, 'echo') + call assert_match('testdir$', getcwd()) + + bwipe! + set noacd + call chdir(cwd) + call delete('Xfile', 'rf') +endfunc + func Test_verbose_pwd() CheckFunction test_autochdir let cwd = getcwd() diff --git a/src/nvim/window.c b/src/nvim/window.c index c4002e93e0..f04204fafe 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6767,9 +6767,6 @@ void restore_win_noblock(switchwin_T *switchwin, bool no_display) curwin = switchwin->sw_curwin; curbuf = curwin->w_buffer; } - // If called by win_execute() and executing the command changed the - // directory, it now has to be restored. - fix_current_dir(); } /// Make "buf" the current buffer. diff --git a/src/nvim/window.h b/src/nvim/window.h index e2fd2c515d..c9af88b9ad 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -5,6 +5,7 @@ #include "nvim/buffer_defs.h" #include "nvim/mark.h" +#include "nvim/os/os.h" // Values for file_name_in_line() #define FNAME_MESS 1 // give error message @@ -47,12 +48,27 @@ typedef struct { do { \ win_T *const wp_ = (wp); \ const pos_T curpos_ = wp_->w_cursor; \ + char_u cwd_[MAXPATHL]; \ + char_u autocwd_[MAXPATHL]; \ + bool apply_acd_ = false; \ + const int cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + /* If 'acd' is set, check we are using that directory. If yes, then */ \ + /* apply 'acd' afterwards, otherwise restore the current directory. */ \ + if (cwd_status_ == OK && p_acd) { \ + do_autochdir(); \ + apply_acd_ = os_dirname(autocwd_, MAXPATHL) == OK && STRCMP(cwd_, autocwd_) == 0; \ + } \ switchwin_T switchwin_; \ if (switch_win_noblock(&switchwin_, wp_, (tp), true) == OK) { \ check_cursor(); \ block; \ } \ restore_win_noblock(&switchwin_, true); \ + if (apply_acd_) { \ + do_autochdir(); \ + } else if (cwd_status_ == OK) { \ + os_chdir((char *)cwd_); \ + } \ /* Update the status line if the cursor moved. */ \ if (win_valid(wp_) && !equalpos(curpos_, wp_->w_cursor)) { \ wp_->w_redr_status = true; \ -- cgit From 8e06377bc6b3cdbabd0fd50e5ee351067fc5ec42 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Mar 2022 09:03:26 +0800 Subject: vim-patch:8.2.4060: win_execute() slow on systems where getcwd()/chdir() is slow Problem: win_execute() is slow on systems where getcwd() or chdir() is slow. (Rick Howe) Solution: Avoid using getcwd() and chdir() if no local directory is used and 'acd' is not set. (closes vim/vim#9504) https://github.com/vim/vim/commit/d6f27c66cca32b93fcf8024b1bad1618946bbbea --- src/nvim/window.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/window.h b/src/nvim/window.h index c9af88b9ad..42701f72b4 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -51,7 +51,16 @@ typedef struct { char_u cwd_[MAXPATHL]; \ char_u autocwd_[MAXPATHL]; \ bool apply_acd_ = false; \ - const int cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + int cwd_status_ = FAIL; \ + /* Getting and setting directory can be slow on some systems, only do */ \ + /* this when the current or target window/tab have a local directory or */ \ + /* 'acd' is set. */ \ + if (curwin != wp \ + && (curwin->w_localdir != NULL || wp->w_localdir != NULL \ + || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL)) \ + || p_acd)) { \ + cwd_status_ = os_dirname(cwd_, MAXPATHL); \ + } \ /* If 'acd' is set, check we are using that directory. If yes, then */ \ /* apply 'acd' afterwards, otherwise restore the current directory. */ \ if (cwd_status_ == OK && p_acd) { \ -- cgit From eb70540ff0385f3929b39f26faceaa2765f6949a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 6 Mar 2022 09:11:23 +0800 Subject: vim-patch:8.2.4513: window-local directory is not applied if 'acd' fails Problem: Window-local directory is not applied if 'acd' fails. Solution: Don't call do_autochdir(). (closes vim/vim#9891) https://github.com/vim/vim/commit/b29ae159777028bb3266835b55716749ab0515be --- src/nvim/testdir/test_autochdir.vim | 13 +++++++++---- src/nvim/window.c | 4 ---- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim index de5c124908..4229095f9f 100644 --- a/src/nvim/testdir/test_autochdir.vim +++ b/src/nvim/testdir/test_autochdir.vim @@ -90,22 +90,27 @@ func Test_verbose_pwd() set acd wincmd w call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) - execute 'lcd' cwd - call assert_match('\[window\].*testdir$', execute('verbose pwd')) execute 'tcd' cwd call assert_match('\[tabpage\].*testdir$', execute('verbose pwd')) execute 'cd' cwd call assert_match('\[global\].*testdir$', execute('verbose pwd')) + execute 'lcd' cwd + call assert_match('\[window\].*testdir$', execute('verbose pwd')) edit call assert_match('\[autochdir\].*testdir$', execute('verbose pwd')) + enew + wincmd w + call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + wincmd w + call assert_match('\[window\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) set noacd call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) wincmd w - call assert_match('\[autochdir\].*testdir[/\\]Xautodir', execute('verbose pwd')) + call assert_match('\[window\].*testdir$', execute('verbose pwd')) execute 'cd' cwd - call assert_match('\[global\].*testdir', execute('verbose pwd')) + call assert_match('\[global\].*testdir$', execute('verbose pwd')) wincmd w call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd')) diff --git a/src/nvim/window.c b/src/nvim/window.c index f04204fafe..d5299202b0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4680,10 +4680,6 @@ static void win_enter_ext(win_T *const wp, const int flags) /// Used after making another window the current one: change directory if needed. void fix_current_dir(void) { - if (p_acd) { - do_autochdir(); - return; - } // New directory is either the local directory of the window, tab or NULL. char *new_dir = (char *)(curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir); -- cgit From f39a12d629500bdbacb389ed593adac34d058fcb Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 6 Mar 2022 13:13:10 +0100 Subject: refactor(lua): make vim submodule lazy loading declarative This will allow us to also use the same logic for lua threads and processes, later. --- src/nvim/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9abefbe02a..9f29cae29d 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -342,6 +342,7 @@ add_custom_command( ${LUA_KEYMAP_MODULE_SOURCE} "vim.keymap" DEPENDS ${CHAR_BLOB_GENERATOR} + ${LUA_INIT_PACKAGES_MODULE_SOURCE} ${LUA_EDITOR_MODULE_SOURCE} ${LUA_SHARED_MODULE_SOURCE} ${LUA_INSPECT_MODULE_SOURCE} -- cgit From 147908336ec2e2051dd752e38964d0c36aea1b54 Mon Sep 17 00:00:00 2001 From: bfredl Date: Mon, 7 Mar 2022 11:01:13 +0100 Subject: fix(lua): don't use nlua_error when exiting early Screen state is not initialized yet. Print directly to stderr instead. --- src/nvim/lua/executor.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 29a3c515c2..6aaff100ca 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -565,7 +565,8 @@ static bool nlua_init_packages(lua_State *lstate) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - nlua_error(lstate, _("E5106: Error while loading packages: %.*s\n")); + mch_errmsg(lua_tostring(lstate, -1)); + mch_errmsg("\n"); return false; } -- cgit From ec3f93ff886604f97da084681c9f0477d5b1b192 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Mar 2022 06:45:54 +0800 Subject: vim-patch:8.2.4520: using wrong highlight for cursor line number Problem: Using wrong highlight for cursor line number. Solution: Take filler lines into account when using CursorLineNr. (closes vim/vim#9897) https://github.com/vim/vim/commit/127969cf98000a760826ca3a0f3781a8b79522f1 --- src/nvim/screen.c | 6 +++--- src/nvim/testdir/test_diffmode.vim | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index ee1acd8d03..1cdee7c972 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2890,9 +2890,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } else if (wp->w_p_cul && lnum == wp->w_cursor.lnum && (wp->w_p_culopt_flags & CULOPT_NBR) - && (row == startrow - || wp->w_p_culopt_flags & CULOPT_LINE) - && filler_todo == 0) { + && (row == startrow + filler_lines + || (row > startrow + filler_lines + && wp->w_p_culopt_flags & CULOPT_LINE))) { // When 'cursorline' is set highlight the line number of // the current line differently. // When 'cursorlineopt' has "screenline" only highlight diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 482d39056f..10eb979b45 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -1017,6 +1017,32 @@ func Test_diff_with_cursorline() call delete('Xtest_diff_cursorline') endfunc +func Test_diff_with_cursorline_number() + CheckScreendump + + let lines =<< trim END + hi CursorLine ctermbg=red ctermfg=white + hi CursorLineNr ctermbg=white ctermfg=black cterm=underline + set cursorline number + call setline(1, ["baz", "foo", "foo", "bar"]) + 2 + vnew + call setline(1, ["foo", "foo", "bar"]) + windo diffthis + 1wincmd w + END + call writefile(lines, 'Xtest_diff_cursorline_number') + let buf = RunVimInTerminal('-S Xtest_diff_cursorline_number', {}) + + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_01', {}) + call term_sendkeys(buf, ":set cursorlineopt=number\r") + call VerifyScreenDump(buf, 'Test_diff_with_cursorline_number_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_cursorline_number') +endfunc + func Test_diff_with_cursorline_breakindent() CheckScreendump -- cgit From 2783f4cc4a410cd3b73e8cdfbdf8c859c426c6c6 Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Tue, 8 Mar 2022 09:45:43 +0530 Subject: feat(api): autocmd `group` can be either name or id (#17559) * feat(api): `group` can be either string or int This affects the following API functions: - `vim.api.nvim_create_autocmd` - `vim.api.nvim_get_autocmds` - `vim.api.nvim_do_autocmd` closes #17552 * refactor: add two maps for fast lookups * fix: delete augroup info from id->name map When in "stupid_legacy_mode", the value in name->id map would be updated to `AUGROUP_DELETED`, but the entry would still remain in id->name. This would create a problem in `augroup_name` function which would return the name of the augroup instead of `--DELETED--`. The id->name map is only used for fast loopup in `augroup_name` function so there's no point in keeping the entry of deleted augroup in it. Co-authored-by: TJ DeVries --- src/nvim/api/autocmd.c | 112 +++++++++++++++++++++++++++++-------------------- src/nvim/autocmd.c | 59 ++++++++++++++++---------- src/nvim/map.c | 1 + src/nvim/map.h | 1 + 4 files changed, 106 insertions(+), 67 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 685667ac64..5ede0e5265 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -40,7 +40,7 @@ static int64_t next_autocmd_id = 1; /// /// @param opts Optional Parameters: /// - event : Name or list of name of events to match against -/// - group (string): Name of group to match against +/// - group (string|int): Name or id of group to match against /// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} /// - buffer: Buffer number or list of buffer numbers for buffer local autocommands /// |autocmd-buflocal|. Cannot be used with {pattern} @@ -62,19 +62,27 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) int group = 0; - if (opts->group.type != kObjectTypeNil) { - Object v = opts->group; - if (v.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "group must be a string."); - goto cleanup; - } - - group = augroup_find(v.data.string.data); - - if (group < 0) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + group = augroup_find(opts->group.data.string.data); + if (group < 0) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + case kObjectTypeInteger: + group = (int)opts->group.data.integer; + char *name = augroup_name(group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); goto cleanup; - } } if (opts->event.type != kObjectTypeNil) { @@ -321,7 +329,7 @@ cleanup: /// - buffer: (bufnr) /// - create a |autocmd-buflocal| autocmd. /// - NOTE: Cannot be used with {pattern} -/// - group: (string) The augroup name +/// - group: (string|int) The augroup name or id /// - once: (boolean) - See |autocmd-once| /// - nested: (boolean) - See |autocmd-nested| /// - desc: (string) - Description of the autocmd @@ -406,23 +414,29 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - // TODO(tjdevries): accept number for namespace instead - if (opts->group.type != kObjectTypeNil) { - Object *v = &opts->group; - if (v->type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(v->data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", v->data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { @@ -624,7 +638,7 @@ void nvim_del_augroup_by_name(String name) /// - NOTE: Cannot be used with {pattern} /// - pattern (string|table) - optional, defaults to "*". /// - NOTE: Cannot be used with {buffer} -/// - group (string) - autocmd group name +/// - group (string|int) - autocmd group name or id /// - modeline (boolean) - Default true, see || void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) @@ -644,21 +658,29 @@ void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) goto cleanup; } - if (opts->group.type != kObjectTypeNil) { - if (opts->group.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, "'group' must be a string"); - goto cleanup; - } - - au_group = augroup_find(opts->group.data.string.data); - - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeException, - "invalid augroup: %s", opts->group.data.string.data); - + switch (opts->group.type) { + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); + goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); goto cleanup; - } } if (opts->buffer.type != kObjectTypeNil) { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 7cb493f57d..a850e5c1a0 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -105,15 +105,24 @@ static char_u *old_termresponse = NULL; #define FOR_ALL_AUPATS_IN_EVENT(event, ap) \ for (AutoPat *ap = first_autopat[event]; ap != NULL; ap = ap->next) // NOLINT -// Map of autocmd group names. +// Map of autocmd group names and ids. // name -> ID -static Map(String, int) augroup_map = MAP_INIT; +// ID -> name +static Map(String, int) map_augroup_name_to_id = MAP_INIT; +static Map(int, String) map_augroup_id_to_name = MAP_INIT; -static void augroup_map_del(char *name) +static void augroup_map_del(int id, char *name) { - String key = map_key(String, int)(&augroup_map, cstr_as_string(name)); - map_del(String, int)(&augroup_map, key); - api_free_string(key); + if (name != NULL) { + String key = map_key(String, int)(&map_augroup_name_to_id, cstr_as_string(name)); + map_del(String, int)(&map_augroup_name_to_id, key); + api_free_string(key); + } + if (id > 0) { + String mapped = map_get(int, String)(&map_augroup_id_to_name, id); + api_free_string(mapped); + map_del(int, String)(&map_augroup_id_to_name, id); + } } @@ -382,12 +391,14 @@ int augroup_add(char *name) } if (existing_id == AUGROUP_DELETED) { - augroup_map_del(name); + augroup_map_del(existing_id, name); } int next_id = next_augroup_id++; - String name_copy = cstr_to_string(name); - map_put(String, int)(&augroup_map, name_copy, next_id); + String name_key = cstr_to_string(name); + String name_val = cstr_to_string(name); + map_put(String, int)(&map_augroup_name_to_id, name_key, next_id); + map_put(int, String)(&map_augroup_id_to_name, next_id, name_val); return next_id; } @@ -416,7 +427,8 @@ void augroup_del(char *name, bool stupid_legacy_mode) FOR_ALL_AUPATS_IN_EVENT(event, ap) { if (ap->group == i && ap->pat != NULL) { give_warning((char_u *)_("W19: Deleting augroup that is still in use"), true); - map_put(String, int)(&augroup_map, cstr_as_string(name), AUGROUP_DELETED); + map_put(String, int)(&map_augroup_name_to_id, cstr_as_string(name), AUGROUP_DELETED); + augroup_map_del(ap->group, NULL); return; } } @@ -432,7 +444,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) } // Remove the group because it's not currently in use. - augroup_map_del(name); + augroup_map_del(i, name); au_cleanup(); } } @@ -445,7 +457,7 @@ void augroup_del(char *name, bool stupid_legacy_mode) int augroup_find(const char *name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int existing_id = map_get(String, int)(&augroup_map, cstr_as_string((char *)name)); + int existing_id = map_get(String, int)(&map_augroup_name_to_id, cstr_as_string((char *)name)); if (existing_id == AUGROUP_DELETED) { return existing_id; } @@ -487,13 +499,10 @@ char *augroup_name(int group) return NULL; } - String key; - int value; - map_foreach(&augroup_map, key, value, { - if (value == group) { - return key.data; - } - }); + String key = map_get(int, String)(&map_augroup_id_to_name, group); + if (key.data != NULL) { + return key.data; + } // If it's not in the map anymore, then it must have been deleted. return (char *)get_deleted_augroup(); @@ -526,7 +535,7 @@ void do_augroup(char_u *arg, int del_group) String name; int value; - map_foreach(&augroup_map, name, value, { + map_foreach(&map_augroup_name_to_id, name, value, { if (value > 0) { msg_puts(name.data); } else { @@ -556,11 +565,17 @@ void free_all_autocmds(void) // Delete the augroup_map, including free the data String name; int id; - map_foreach(&augroup_map, name, id, { + map_foreach(&map_augroup_name_to_id, name, id, { + (void)id; + api_free_string(name); + }) + map_destroy(String, int)(&map_augroup_name_to_id); + + map_foreach(&map_augroup_id_to_name, id, name, { (void)id; api_free_string(name); }) - map_destroy(String, int)(&augroup_map); + map_destroy(int, String)(&map_augroup_id_to_name); } #endif diff --git a/src/nvim/map.c b/src/nvim/map.c index 091d653046..4e39eb8c07 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -178,6 +178,7 @@ MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) +MAP_IMPL(int, String, DEFAULT_INITIALIZER) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index c9c89bf2fd..00f72386a7 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -47,6 +47,7 @@ MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) +MAP_DECLS(int, String) MAP_DECLS(ColorKey, ColorItem) -- cgit From bce1fd221814c705e8ad8172f8839305e0633127 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Tue, 8 Mar 2022 12:56:45 +0100 Subject: chore(aucmd): remove left-over debug log (#17649) --- src/nvim/eval.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c5c03455b7..81432fefd3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7789,7 +7789,6 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - ILOG(" We tryin to call dat dang lua ref "); nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL); return false; -- cgit From 4f007a7f651ccd052f299000b49b9a389f713725 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Tue, 8 Mar 2022 11:27:11 -0500 Subject: fix: do not pass aucmd to the callback (#17650) --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 81432fefd3..2ec748a217 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7789,7 +7789,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - nlua_call_ref(callback->data.luaref, "aucmd", args, false, NULL); + nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); return false; break; -- cgit From f24121ad96d7f560a36f0d977766d4e8232fbda6 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 8 Mar 2022 21:38:10 +0000 Subject: fix(line continuation): set growsize to correct value (#17655) Using MAX always sets growsize to 8000, unless ga_len is larger... --- src/nvim/ex_cmds2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 8666c7e33a..4493fb430b 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1698,7 +1698,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, return false; } if (ga->ga_len > init_growsize) { - ga_set_growsize(ga, MAX(ga->ga_len, 8000)); + ga_set_growsize(ga, MIN(ga->ga_len, 8000)); } ga_concat_len(ga, (const char *)line + 1, len - 1); return true; @@ -1852,7 +1852,7 @@ static void cmd_source_buffer(const exarg_T *const eap) for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) { // Adjust growsize to current length to speed up concatenating many lines. if (ga.ga_len > 400) { - ga_set_growsize(&ga, MAX(ga.ga_len, 8000)); + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); } ga_concat(&ga, (char *)ml_get(curr_lnum)); ga_append(&ga, NL); -- cgit From 2ab2af598eff1bea9a4aa5da51d6c202173d5ee7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 7 Mar 2022 12:02:20 +0000 Subject: fix(extmarks): fix signs Don't add sign extmarks to state->active. Instead when drawing signs, perform a full line scan for sign marks. This allows decor_redraw_line to be moved back inside the `!number_only` block in screen.c, which prevents decor scans when redrawing the number column when 'relativenumber' is set. Fixes: #17638 --- src/nvim/decoration.c | 44 +++++++++++++++++++++++--------------------- src/nvim/decoration.h | 1 - src/nvim/screen.c | 8 +++----- 3 files changed, 26 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 619e8fdadc..b533f4c46f 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -150,7 +150,6 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state) { state->row = -1; state->buf = buf; - state->has_sign_decor = false; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); if (item.virt_text_owned) { @@ -202,11 +201,6 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state) goto next_mark; } - // Don't add signs for end marks as the start mark has already been added. - if (mt_end(mark) && decor_has_sign(&decor)) { - goto next_mark; - } - mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL); // Exclude start marks if the end mark position is above the top row @@ -270,10 +264,6 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r kv_A(state->active, index) = kv_A(state->active, index-1); } kv_A(state->active, index) = range; - - if (decor_has_sign(decor)) { - state->has_sign_decor = true; - } } int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state) @@ -327,8 +317,7 @@ next_mark: bool active = false, keep = true; if (item.end_row < state->row || (item.end_row == state->row && item.end_col <= col)) { - if (!(item.start_row >= state->row && kv_size(item.decor.virt_text)) - && !decor_has_sign(&item.decor)) { + if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) { keep = false; } } else { @@ -363,19 +352,29 @@ next_mark: return attr; } -void decor_redraw_signs(buf_T *buf, DecorState *state, int row, - int *num_signs, sign_attrs_T sattrs[]) +void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs[]) { - for (size_t i = 0; i < kv_size(state->active); i++) { - DecorRange item = kv_A(state->active, i); - Decoration *decor = &item.decor; + if (!buf->b_signs) { + return; + } + + MarkTreeIter itr[1] = { 0 }; + marktree_itr_get(buf->b_marktree, row, 0, itr); + + while (true) { + mtkey_t mark = marktree_itr_current(itr); + if (mark.pos.row < 0 || mark.pos.row > row) { + break; + } - if (!decor_has_sign(decor)) { - continue; + if (mt_end(mark) || marktree_decor_level(mark) < kDecorLevelVisible) { + goto next_mark; } - if (state->row != item.start_row) { - continue; + Decoration *decor = mark.decor_full; + + if (!decor || !decor_has_sign(decor)) { + goto next_mark; } int j; @@ -403,6 +402,9 @@ void decor_redraw_signs(buf_T *buf, DecorState *state, int row, sattrs[j].sat_prio = decor->priority; (*num_signs)++; } + +next_mark: + marktree_itr_next(buf->b_marktree, itr); } } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 7e6dbdf1a7..2277a0ef1c 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -80,7 +80,6 @@ typedef struct { int col_until; int current; int eol_col; - bool has_sign_decor; } DecorState; typedef struct { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index ee1acd8d03..1498d1c9b3 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2208,8 +2208,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc buf_T *buf = wp->w_buffer; bool end_fill = (lnum == buf->b_ml.ml_line_count+1); - has_decor = decor_redraw_line(buf, lnum-1, &decor_state); - if (!number_only) { // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. @@ -2231,6 +2229,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } + has_decor = decor_redraw_line(buf, lnum-1, &decor_state); + for (size_t k = 0; k < kv_size(*providers); k++) { DecorProvider *p = kv_A(*providers, k); if (p && p->redraw_line != LUA_NOREF) { @@ -2459,9 +2459,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc memset(sattrs, 0, sizeof(sattrs)); num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs); - if (decor_state.has_sign_decor) { - decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs); - } + decor_redraw_signs(buf, lnum-1, &num_signs, sattrs); // If this line has a sign with line highlighting set line_attr. // TODO(bfredl, vigoux): this should not take priority over decoration! -- cgit From 165cf1b48e4505b2b8aa9ff0522c9176a073b7ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Feb 2022 15:56:37 +0800 Subject: vim-patch:8.2.0997: cannot execute a register containing line continuation Problem: Cannot execute a register containing line continuation. Solution: Concatenate lines where needed. (Yegappan Lakshmanan, closes vim/vim#6272) https://github.com/vim/vim/commit/856c1110c1cf0d6e44e387b70732ca4b4c8ef0f2 According to #2542 the "Future:" part was removed intentionally. Use size_t in more places to reduce type casts. --- src/nvim/ops.c | 70 +++++++++++++++++++++++++++++++++- src/nvim/testdir/test_registers.vim | 76 +++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index b5c7020dee..3f51210781 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1023,6 +1023,60 @@ static int stuff_yank(int regname, char_u *p) static int execreg_lastc = NUL; +/// When executing a register as a series of ex-commands, if the +/// line-continuation character is used for a line, then join it with one or +/// more previous lines. Note that lines are processed backwards starting from +/// the last line in the register. +/// +/// @param lines list of lines in the register +/// @param idx index of the line starting with \ or "\. Join this line with all the immediate +/// predecessor lines that start with a \ and the first line that doesn't start +/// with a \. Lines that start with a comment "\ character are ignored. +/// @returns the concatenated line. The index of the line that should be +/// processed next is returned in idx. +static char_u *execreg_line_continuation(char_u **lines, size_t *idx) +{ + size_t i = *idx; + assert(i > 0); + const size_t cmd_end = i; + + garray_T ga; + ga_init(&ga, (int)sizeof(char_u), 400); + + char_u *p; + + // search backwards to find the first line of this command. + // Any line not starting with \ or "\ is the start of the + // command. + while (--i > 0) { + p = skipwhite(lines[i]); + if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) { + break; + } + } + const size_t cmd_start = i; + + // join all the lines + ga_concat(&ga, (char *)lines[cmd_start]); + for (size_t j = cmd_start + 1; j <= cmd_end; j++) { + p = skipwhite(lines[j]); + if (*p == '\\') { + // Adjust the growsize to the current length to + // speed up concatenating many lines. + if (ga.ga_len > 400) { + ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); + } + ga_concat(&ga, (char *)(p + 1)); + } + } + ga_append(&ga, NUL); + char_u *str = vim_strsave(ga.ga_data); + ga_clear(&ga); + + *idx = i; + return str; +} + /// Execute a yank register: copy it into the stuff buffer /// /// @param colon insert ':' before each line @@ -1111,7 +1165,21 @@ int do_execreg(int regname, int colon, int addcr, int silent) return FAIL; } } - escaped = vim_strsave_escape_ks(reg->y_array[i]); + + // Handle line-continuation for :@ + char_u *str = reg->y_array[i]; + bool free_str = false; + if (colon && i > 0) { + p = skipwhite(str); + if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) { + str = execreg_line_continuation(reg->y_array, &i); + free_str = true; + } + } + escaped = vim_strsave_escape_ks(str); + if (free_str) { + xfree(str); + } retval = ins_typebuf(escaped, remap, 0, true, silent); xfree(escaped); if (retval == FAIL) { diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index 23e39eba35..ecc65b240b 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -482,6 +482,82 @@ func Test_v_register() bwipe! endfunc +" Test for executing the contents of a register as an Ex command with line +" continuation. +func Test_execute_reg_as_ex_cmd() + " Line continuation with just two lines + let code =<< trim END + let l = [ + \ 1] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1], l) + + " Line continuation with more than two lines + let code =<< trim END + let l = [ + \ 1, + \ 2, + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use comments interspersed with code + let code =<< trim END + let l = [ + "\ one + \ 1, + "\ two + \ 2, + "\ three + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use line continuation in the middle + let code =<< trim END + let a = "one" + let l = [ + \ 1, + \ 2] + let b = "two" + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2], l) + call assert_equal("one", a) + call assert_equal("two", b) + + " only one line with a \ + let @r = "\\let l = 1" + call assert_fails('@r', 'E10:') + + " only one line with a "\ + let @r = ' "\ let i = 1' + @r + call assert_false(exists('i')) + + " first line also begins with a \ + let @r = "\\let l = [\n\\ 1]" + call assert_fails('@r', 'E10:') + + " Test with a large number of lines + let @r = "let str = \n" + let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312) + let @r ..= ' \ ""' + @r + call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str) +endfunc + func Test_ve_blockpaste() new set ve=all -- cgit From ee903e86fdd3c9294653d9068cb499b972b2ded0 Mon Sep 17 00:00:00 2001 From: VVKot Date: Sun, 19 Dec 2021 20:06:12 +0000 Subject: vim-patch:8.1.0748: using sprintf() instead of semsg() Problem: Using sprintf() instead of semsg(). Solution: Use semsg(). Fix bug with E888. (Ozaki Kiichi, closes vim/vim#3801) https://github.com/vim/vim/commit/1be45b2ea76ae2e39817a98a584d4d6cbb983a7b vim-patch:8.1.0136: Lua tests don't cover new features Problem: Lua tests don't cover new features. Solution: Add more tests. (Dominique Pelle, closes vim/vim#3130) https://github.com/vim/vim/commit/2f362bf7f9acc9ec87799d1e41bf0ae7712d1f7a vim-patch:8.1.0139: Lua tests fail on some platforms Problem: Lua tests fail on some platforms. Solution: Accept a hex number with and without "0x". (Ken Takata, closes vim/vim#3137) https://github.com/vim/vim/commit/a8a60d0c6b292216e55f005cf9637789a771d34b vim-patch:8.1.0164: luaeval('vim.buffer().name') returns an error Problem: luaeval('vim.buffer().name') returns an error. Solution: Return an empty string. (Dominique Pelle, closes vim/vim#3167) https://github.com/vim/vim/commit/fe08df452af10db8a24dbeb1bd9ef09492a4bc66 vim-patch:8.1.0300: the old window title might be freed twice Problem: The old window title might be freed twice. (Dominique Pelle) Solution: Do not free "oldtitle" in a signal handler but set a flag to have it freed later. https://github.com/vim/vim/commit/d8f0cef2bdbdc15d7906f991725e09e67c97cf7e vim-patch:8.1.0672: the Lua interface doesn't know about v:null Problem: The Lua interface doesn't know about v:null. Solution: Add Lua support for v:null. (Uji, closes vim/vim#3744) https://github.com/vim/vim/commit/9067cd6cdfdc0bb869aa7f5d2a6c607ea8255239 --- src/nvim/regexp.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index cac95e1def..b7f11b2de0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -468,6 +468,8 @@ static int toggle_Magic(int x) #define EMSG_RET_FAIL(m) return (emsg(m), rc_did_emsg = true, FAIL) #define EMSG2_RET_NULL(m, c) \ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL) +#define EMSG3_RET_NULL(m, c, a) \ + return (semsg((const char *)(m), (c) ? "" : "\\", (a)), rc_did_emsg = true, (void *)NULL) #define EMSG2_RET_FAIL(m, c) \ return (semsg((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL) #define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \ @@ -1762,13 +1764,9 @@ static char_u *regpiece(int *flagp) if (re_multi_type(peekchr()) != NOT_MULTI) { // Can't have a multi follow a multi. if (peekchr() == Magic('*')) { - snprintf((char *)IObuff, IOSIZE, _("E61: Nested %s*"), - reg_magic >= MAGIC_ON ? "" : "\\"); - } else { - snprintf((char *)IObuff, IOSIZE, _("E62: Nested %s%c"), - reg_magic == MAGIC_ALL ? "" : "\\", no_Magic(peekchr())); + EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON); } - EMSG_RET_NULL((char *)IObuff); + EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr())); } return ret; @@ -1927,10 +1925,8 @@ static char_u *regatom(int *flagp) case Magic('{'): case Magic('*'): c = no_Magic(c); - snprintf((char *)IObuff, IOSIZE, _("E64: %s%c follows nothing"), - (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL) - ? "" : "\\", c); - EMSG_RET_NULL((char *)IObuff); + EMSG3_RET_NULL(_("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c); // NOTREACHED case Magic('~'): /* previous substitute pattern */ @@ -2537,7 +2533,9 @@ do_multibyte: static bool re_mult_next(char *what) { if (re_multi_type(peekchr()) == MULTI_MULT) { - EMSG2_RET_FAIL(_("E888: (NFA regexp) cannot repeat %s"), what); + semsg(_("E888: (NFA regexp) cannot repeat %s"), what); + rc_did_emsg = true; + return false; } return true; } @@ -3149,9 +3147,7 @@ static int read_limits(long *minval, long *maxval) regparse++; // Allow either \{...} or \{...\} } if (*regparse != '}') { - snprintf((char *)IObuff, IOSIZE, _("E554: Syntax error in %s{...}"), - reg_magic == MAGIC_ALL ? "" : "\\"); - EMSG_RET_FAIL((char *)IObuff); + EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL); } /* -- cgit From f6d507f5ba2a11f8f0a2bd976f38d2c0d170e91f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Feb 2022 11:11:23 +0800 Subject: refactor(ops): use op_yank_reg() instead of op_yank() when deleting Needed for Vim patch 8.1.0999. --- src/nvim/ex_docmd.c | 2 +- src/nvim/ops.c | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6e915d98dc..a140d76858 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8160,7 +8160,7 @@ static void ex_operators(exarg_T *eap) case CMD_yank: oa.op_type = OP_YANK; - (void)op_yank(&oa, true, false); + (void)op_yank(&oa, true); break; default: // CMD_rshift or CMD_lshift diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3f51210781..a18c6e126a 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1572,12 +1572,14 @@ int op_delete(oparg_T *oap) yankreg_T *reg = NULL; int did_yank = false; if (oap->regname != 0) { - // yank without message - did_yank = op_yank(oap, false, true); - if (!did_yank) { - // op_yank failed, don't do anything + // check for read-only register + if (!valid_yank_reg(oap->regname, true)) { + beep_flush(); return OK; } + reg = get_yank_register(oap->regname, YREG_YANK); // yank into specif'd reg + op_yank_reg(oap, false, reg, is_append_register(oap->regname)); // yank without message + did_yank = true; } /* @@ -2647,12 +2649,12 @@ void free_register(yankreg_T *reg) /// Yanks the text between "oap->start" and "oap->end" into a yank register. /// If we are to append (uppercase register), we first yank into a new yank /// register and then concatenate the old and the new one. +/// Do not call this from a delete operation. Use op_yank_reg() instead. /// /// @param oap operator arguments /// @param message show message when more than `&report` lines are yanked. -/// @param deleting whether the function was called from a delete operation. /// @returns whether the operation register was writable. -bool op_yank(oparg_T *oap, bool message, int deleting) +bool op_yank(oparg_T *oap, bool message) FUNC_ATTR_NONNULL_ALL { // check for read-only register @@ -2666,11 +2668,8 @@ bool op_yank(oparg_T *oap, bool message, int deleting) yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK); op_yank_reg(oap, message, reg, is_append_register(oap->regname)); - // op_delete will set_clipboard and do_autocmd - if (!deleting) { - set_clipboard(oap->regname, reg); - do_autocmd_textyankpost(oap, reg); - } + set_clipboard(oap->regname, reg); + do_autocmd_textyankpost(oap, reg); return true; } @@ -6698,7 +6697,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } else { curwin->w_p_lbr = lbr_saved; oap->excl_tr_ws = cap->cmdchar == 'z'; - (void)op_yank(oap, !gui_yank, false); + (void)op_yank(oap, !gui_yank); } check_cursor_col(); break; -- cgit From 5ac30eacf41848ffebcea692a913bc77a0a7424f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Feb 2022 11:12:30 +0800 Subject: vim-patch:8.1.0999: use register one too often and not properly tested Problem: Use register one too often and not properly tested. Solution: Do not always use register one when specifying a register. (closes vim/vim#4085) Add more tests. https://github.com/vim/vim/commit/9d7fdd403a3a9ee0d008b6dcbcd2ecc9ec0f57b7 --- src/nvim/ops.c | 10 ++--- src/nvim/testdir/test_registers.vim | 85 ++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index a18c6e126a..23db7fe5a3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1582,12 +1582,10 @@ int op_delete(oparg_T *oap) did_yank = true; } - /* - * Put deleted text into register 1 and shift number registers if the - * delete contains a line break, or when a regname has been specified. - */ - if (oap->regname != 0 || oap->motion_type == kMTLineWise - || oap->line_count > 1 || oap->use_reg_one) { + // Put deleted text into register 1 and shift number registers if the + // delete contains a line break, or when using a specific operator (Vi + // compatible) + if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) { shift_delete_registers(is_append_register(oap->regname)); reg = &y_regs[1]; op_yank_reg(oap, false, reg, false); diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index ecc65b240b..f78b748d71 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -62,7 +62,6 @@ func Test_display_registers() call assert_match('^\nType Name Content\n' \ . ' c "" a\n' \ . ' c "0 ba\n' - \ . ' c "1 b\n' \ . ' c "a b\n' \ . '.*' \ . ' c "- a\n' @@ -85,6 +84,90 @@ func Test_display_registers() let g:clipboard = save_clipboard endfunc +func Test_register_one() + " delete a line goes into register one + new + call setline(1, "one") + normal dd + call assert_equal("one\n", @1) + + " delete a word does not change register one, does change "- + call setline(1, "two") + normal de + call assert_equal("one\n", @1) + call assert_equal("two", @-) + + " delete a word with a register does not change register one + call setline(1, "three") + normal "ade + call assert_equal("three", @a) + call assert_equal("one\n", @1) + + " delete a word with register DOES change register one with one of a list of + " operators + " % + call setline(1, ["(12)3"]) + normal "ad% + call assert_equal("(12)", @a) + call assert_equal("(12)", @1) + + " ( + call setline(1, ["first second"]) + normal $"ad( + call assert_equal("first secon", @a) + call assert_equal("first secon", @1) + + " ) + call setline(1, ["First Second."]) + normal gg0"ad) + call assert_equal("First Second.", @a) + call assert_equal("First Second.", @1) + + " ` + call setline(1, ["start here."]) + normal gg0fhmx0"ad`x + call assert_equal("start ", @a) + call assert_equal("start ", @1) + + " / + call setline(1, ["searchX"]) + exe "normal gg0\"ad/X\" + call assert_equal("search", @a) + call assert_equal("search", @1) + + " ? + call setline(1, ["Ysearch"]) + exe "normal gg$\"ad?Y\" + call assert_equal("Ysearc", @a) + call assert_equal("Ysearc", @1) + + " n + call setline(1, ["Ynext"]) + normal gg$"adn + call assert_equal("Ynex", @a) + call assert_equal("Ynex", @1) + + " N + call setline(1, ["prevY"]) + normal gg0"adN + call assert_equal("prev", @a) + call assert_equal("prev", @1) + + " } + call setline(1, ["one", ""]) + normal gg0"ad} + call assert_equal("one\n", @a) + call assert_equal("one\n", @1) + + " { + call setline(1, ["", "two"]) + normal 2G$"ad{ + call assert_equal("\ntw", @a) + call assert_equal("\ntw", @1) + + bwipe! +endfunc + func Test_recording_status_in_ex_line() norm qx redraw! -- cgit From f27068caad517c52e700477d51343cbebd8d4df0 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Tue, 8 Mar 2022 19:51:06 +0100 Subject: chore(lgtm): ignore "__eq__ not overridden" warning --- src/clint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/clint.py b/src/clint.py index 37b44920af..10f33d22e2 100755 --- a/src/clint.py +++ b/src/clint.py @@ -369,7 +369,7 @@ def Search(pattern, s): return _regexp_compile_cache[pattern].search(s) -class _IncludeState(dict): +class _IncludeState(dict): # lgtm [py/missing-equals] """Tracks line numbers for includes, and the order in which includes appear. -- cgit From 05f643f9d235b0db881acf94ce05ad3359ffb058 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Tue, 8 Mar 2022 19:58:45 +0100 Subject: chore(lgtm): fix "empty block without comment" warnings --- src/nvim/eval/funcs.c | 3 +-- src/nvim/fileio.c | 3 +-- src/nvim/lua/xdiff.c | 1 + src/nvim/marktree.c | 2 -- src/nvim/normal.c | 7 +++---- src/nvim/ops.c | 1 - 6 files changed, 6 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f47c9c482b..d81a408663 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9706,8 +9706,7 @@ free_lstval: if (set_unnamed) { // Discard the result. We already handle the error case. - if (op_reg_set_previous(regname)) { - } + op_reg_set_previous(regname); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index c5a89d3371..971ae9abae 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -4961,9 +4961,8 @@ int buf_check_timestamp(buf_T *buf) buf_store_file_info(buf, &file_info); } - // Don't do anything for a directory. Might contain the file - // explorer. if (os_isdir(buf->b_fname)) { + // Don't do anything for a directory. Might contain the file explorer. } else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar) && !bufIsChanged(buf) && file_info_ok) { // If 'autoread' is set, the buffer has no changes and the file still diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index ea7a700e1e..37855630d1 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -171,6 +171,7 @@ static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, goto exit_1; } if (strequal("unified", v->data.string.data)) { + // the default } else if (strequal("indices", v->data.string.data)) { had_result_type_indices = true; } else { diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index d2354dbf6b..4456b28293 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -1159,8 +1159,6 @@ static size_t check_node(MarkTree *b, mtnode_t *x, mtpos_t *last, bool *last_rig if (i > 0) { unrelative(x->key[i-1].pos, last); } - if (x->level) { - } assert(pos_leq(*last, x->key[i].pos)); if (last->row == x->key[i].pos.row && last->col == x->key[i].pos.col) { assert(!*last_right || mt_right(x->key[i])); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7fe6469527..5c39e9c8bf 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2105,10 +2105,9 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) } else { // MOUSE_RIGHT stuffcharReadbuff('#'); } - } - // Handle double clicks, unless on status line - else if (in_status_line) { - } else if (in_sep_line) { + } else if (in_status_line || in_sep_line) { + // Do nothing if on status line or vertical separator + // Handle double clicks otherwise } else if ((mod_mask & MOD_MASK_MULTI_CLICK) && (State & (NORMAL | INSERT))) { if (is_click || !VIsual_active) { if (VIsual_active) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3f51210781..09b00a1d91 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -7382,7 +7382,6 @@ const yankreg_T *op_reg_get(const char name) /// /// @return true on success, false on failure. bool op_reg_set_previous(const char name) - FUNC_ATTR_WARN_UNUSED_RESULT { int i = op_reg_index(name); if (i == -1) { -- cgit From 3011794c8600f529bc049983a64ca99ae03908df Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Mar 2022 07:18:49 +0800 Subject: feat(api): relax statusline fillchar width check Treat fillchar as single-width even if it isn't. --- src/nvim/api/vim.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index c37df45c14..1a324bfaa5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2241,7 +2241,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err) /// - winid: (number) |window-ID| of the window to use as context for statusline. /// - maxwidth: (number) Maximum width of statusline. /// - fillchar: (string) Character to fill blank spaces in the statusline (see -/// 'fillchars'). +/// 'fillchars'). Treated as single-width even if it isn't. /// - highlights: (boolean) Return highlight information. /// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid} /// is ignored. @@ -2277,11 +2277,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * if (HAS_KEY(opts->fillchar)) { if (opts->fillchar.type != kObjectTypeString || opts->fillchar.data.string.size == 0 - || char2cells(fillchar = utf_ptr2char((char_u *)opts->fillchar.data.string.data)) != 1 - || (size_t)utf_char2len(fillchar) != opts->fillchar.data.string.size) { - api_set_error(err, kErrorTypeValidation, "fillchar must be a single-width character"); + || ((size_t)utf_ptr2len((char_u *)opts->fillchar.data.string.data) + != opts->fillchar.data.string.size)) { + api_set_error(err, kErrorTypeValidation, "fillchar must be a single character"); return result; } + fillchar = utf_ptr2char((char_u *)opts->fillchar.data.string.data); } if (HAS_KEY(opts->highlights)) { -- cgit From 4fed1bf7e537a3a7dc9e8385c65f68ac5c0cf616 Mon Sep 17 00:00:00 2001 From: kylo252 <59826753+kylo252@users.noreply.github.com> Date: Tue, 16 Nov 2021 13:16:33 +0100 Subject: vim-patch:8.1.2005: the regexp.c file is too big Problem: The regexp.c file is too big. Solution: Move the backtracking engine to a separate file. (Yegappan Lakshmanan, closes vim/vim#4905) https://github.com/vim/vim/commit/6d7d7cf750bca5d641e464f6a3af5ee5b99a5ac8 vim-patch:8.1.2010: new file uses old style comments Problem: New file uses old style comments. Solution: Change to new style comments. (Yegappan Lakshmanan, closes vim/vim#4910) https://github.com/vim/vim/commit/9490b9a61cf1f1f3fa9758663a33124ea9f71c87 --- src/nvim/CMakeLists.txt | 4 + src/nvim/regexp.c | 6451 ++++++----------------------------------------- src/nvim/regexp.h | 1 + src/nvim/regexp_bt.c | 4951 ++++++++++++++++++++++++++++++++++++ 4 files changed, 5707 insertions(+), 5700 deletions(-) create mode 100644 src/nvim/regexp_bt.c (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 9f29cae29d..96e5d1e77c 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -135,6 +135,9 @@ foreach(sfile ${NVIM_SOURCES}) if(${f} MATCHES "^(regexp_nfa.c)$") list(APPEND to_remove ${sfile}) endif() + if(${f} MATCHES "^(regexp_bt.c)$") + list(APPEND to_remove ${sfile}) + endif() if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$") list(APPEND to_remove ${sfile}) endif() @@ -261,6 +264,7 @@ endif() # NVIM_GENERATED_SOURCES: generated source files # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} + "${CMAKE_CURRENT_LIST_DIR}/regexp_bt.c" "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" ${GENERATED_API_DISPATCH} "${GENERATED_UI_EVENTS_CALL}" diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index b7f11b2de0..0fc49cd1bf 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -5,41 +5,6 @@ /* * Handling of regular expressions: vim_regcomp(), vim_regexec(), vim_regsub() - * - * NOTICE: - * - * This is NOT the original regular expression code as written by Henry - * Spencer. This code has been modified specifically for use with the VIM - * editor, and should not be used separately from Vim. If you want a good - * regular expression library, get the original code. The copyright notice - * that follows is from the original. - * - * END NOTICE - * - * Copyright (c) 1986 by University of Toronto. - * Written by Henry Spencer. Not derived from licensed software. - * - * Permission is granted to anyone to use this software for any - * purpose on any computer system, and to redistribute it freely, - * subject to the following restrictions: - * - * 1. The author is not responsible for the consequences of use of - * this software, no matter how awful, even if they arise - * from defects in it. - * - * 2. The origin of this software must not be misrepresented, either - * by explicit claim or by omission. - * - * 3. Altered versions must be plainly marked as such, and must not - * be misrepresented as being the original software. - * - * Beware that some of this code is subtly aware of the way operator - * precedence is structured in regular expressions. Serious changes in - * regular-expression syntax might require a total rethink. - * - * Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert - * Webb, Ciaran McCreesh and Bram Moolenaar. - * Named character class support added by Walter Briscoe (1998 Jul 01) */ // By default: do not create debugging logs or files related to regular @@ -70,204 +35,14 @@ #include "nvim/strings.h" #ifdef REGEXP_DEBUG -/* show/save debugging data when BT engine is used */ +// show/save debugging data when BT engine is used # define BT_REGEXP_DUMP -/* save the debugging data to a file instead of displaying it */ +// save the debugging data to a file instead of displaying it # define BT_REGEXP_LOG # define BT_REGEXP_DEBUG_LOG # define BT_REGEXP_DEBUG_LOG_NAME "bt_regexp_debug.log" #endif -/* - * The "internal use only" fields in regexp_defs.h are present to pass info from - * compile to execute that permits the execute phase to run lots faster on - * simple cases. They are: - * - * regstart char that must begin a match; NUL if none obvious; Can be a - * multi-byte character. - * reganch is the match anchored (at beginning-of-line only)? - * regmust string (pointer into program) that match must include, or NULL - * regmlen length of regmust string - * regflags RF_ values or'ed together - * - * Regstart and reganch permit very fast decisions on suitable starting points - * for a match, cutting down the work a lot. Regmust permits fast rejection - * of lines that cannot possibly match. The regmust tests are costly enough - * that vim_regcomp() supplies a regmust only if the r.e. contains something - * potentially expensive (at present, the only such thing detected is * or + - * at the start of the r.e., which can involve a lot of backup). Regmlen is - * supplied because the test in vim_regexec() needs it and vim_regcomp() is - * computing it anyway. - */ - -/* - * Structure for regexp "program". This is essentially a linear encoding - * of a nondeterministic finite-state machine (aka syntax charts or - * "railroad normal form" in parsing technology). Each node is an opcode - * plus a "next" pointer, possibly plus an operand. "Next" pointers of - * all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next" - * pointer with a BRANCH on both ends of it is connecting two alternatives. - * (Here we have one of the subtle syntax dependencies: an individual BRANCH - * (as opposed to a collection of them) is never concatenated with anything - * because of operator precedence). The "next" pointer of a BRACES_COMPLEX - * node points to the node after the stuff to be repeated. - * The operand of some types of node is a literal string; for others, it is a - * node leading into a sub-FSM. In particular, the operand of a BRANCH node - * is the first node of the branch. - * (NB this is *not* a tree structure: the tail of the branch connects to the - * thing following the set of BRANCHes.) - * - * pattern is coded like: - * - * +-----------------+ - * | V - * \| BRANCH BRANCH --> END - * | ^ | ^ - * +------+ +----------+ - * - * - * +------------------+ - * V | - * * BRANCH BRANCH --> BACK BRANCH --> NOTHING --> END - * | | ^ ^ - * | +---------------+ | - * +---------------------------------------------+ - * - * - * +----------------------+ - * V | - * \+ BRANCH --> BRANCH --> BACK BRANCH --> NOTHING --> END - * | | ^ ^ - * | +-----------+ | - * +--------------------------------------------------+ - * - * - * +-------------------------+ - * V | - * \{} BRANCH BRACE_LIMITS --> BRACE_COMPLEX --> BACK END - * | | ^ - * | +----------------+ - * +-----------------------------------------------+ - * - * - * \@! BRANCH NOMATCH --> END --> END - * | | ^ ^ - * | +----------------+ | - * +--------------------------------+ - * - * +---------+ - * | V - * \z[abc] BRANCH BRANCH a BRANCH b BRANCH c BRANCH NOTHING --> END - * | | | | ^ ^ - * | | | +-----+ | - * | | +----------------+ | - * | +---------------------------+ | - * +------------------------------------------------------+ - * - * They all start with a BRANCH for "\|" alternatives, even when there is only - * one alternative. - */ - -/* - * The opcodes are: - */ - -/* definition number opnd? meaning */ -#define END 0 /* End of program or NOMATCH operand. */ -#define BOL 1 /* Match "" at beginning of line. */ -#define EOL 2 /* Match "" at end of line. */ -#define BRANCH 3 /* node Match this alternative, or the - * next... */ -#define BACK 4 /* Match "", "next" ptr points backward. */ -#define EXACTLY 5 /* str Match this string. */ -#define NOTHING 6 /* Match empty string. */ -#define STAR 7 /* node Match this (simple) thing 0 or more - * times. */ -#define PLUS 8 /* node Match this (simple) thing 1 or more - * times. */ -#define MATCH 9 /* node match the operand zero-width */ -#define NOMATCH 10 /* node check for no match with operand */ -#define BEHIND 11 /* node look behind for a match with operand */ -#define NOBEHIND 12 /* node look behind for no match with operand */ -#define SUBPAT 13 /* node match the operand here */ -#define BRACE_SIMPLE 14 /* node Match this (simple) thing between m and - * n times (\{m,n\}). */ -#define BOW 15 /* Match "" after [^a-zA-Z0-9_] */ -#define EOW 16 /* Match "" at [^a-zA-Z0-9_] */ -#define BRACE_LIMITS 17 /* nr nr define the min & max for BRACE_SIMPLE - * and BRACE_COMPLEX. */ -#define NEWL 18 /* Match line-break */ -#define BHPOS 19 /* End position for BEHIND or NOBEHIND */ - - -/* character classes: 20-48 normal, 50-78 include a line-break */ -#define ADD_NL 30 -#define FIRST_NL ANY + ADD_NL -#define ANY 20 /* Match any one character. */ -#define ANYOF 21 /* str Match any character in this string. */ -#define ANYBUT 22 /* str Match any character not in this - * string. */ -#define IDENT 23 /* Match identifier char */ -#define SIDENT 24 /* Match identifier char but no digit */ -#define KWORD 25 /* Match keyword char */ -#define SKWORD 26 /* Match word char but no digit */ -#define FNAME 27 /* Match file name char */ -#define SFNAME 28 /* Match file name char but no digit */ -#define PRINT 29 /* Match printable char */ -#define SPRINT 30 /* Match printable char but no digit */ -#define WHITE 31 /* Match whitespace char */ -#define NWHITE 32 /* Match non-whitespace char */ -#define DIGIT 33 /* Match digit char */ -#define NDIGIT 34 /* Match non-digit char */ -#define HEX 35 /* Match hex char */ -#define NHEX 36 /* Match non-hex char */ -#define OCTAL 37 /* Match octal char */ -#define NOCTAL 38 /* Match non-octal char */ -#define WORD 39 /* Match word char */ -#define NWORD 40 /* Match non-word char */ -#define HEAD 41 /* Match head char */ -#define NHEAD 42 /* Match non-head char */ -#define ALPHA 43 /* Match alpha char */ -#define NALPHA 44 /* Match non-alpha char */ -#define LOWER 45 /* Match lowercase char */ -#define NLOWER 46 /* Match non-lowercase char */ -#define UPPER 47 /* Match uppercase char */ -#define NUPPER 48 /* Match non-uppercase char */ -#define LAST_NL NUPPER + ADD_NL -// -V:WITH_NL:560 -#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL) - -#define MOPEN 80 // -89 Mark this point in input as start of - // \( … \) subexpr. MOPEN + 0 marks start of - // match. -#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks - // end of match. -#define BACKREF 100 // -109 node Match same string again \1-\9. - -# define ZOPEN 110 // -119 Mark this point in input as start of - // \z( … \) subexpr. -# define ZCLOSE 120 // -129 Analogous to ZOPEN. -# define ZREF 130 // -139 node Match external submatch \z1-\z9 - -#define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */ - -#define NOPEN 150 // Mark this point in input as start of - // \%( subexpr. -#define NCLOSE 151 // Analogous to NOPEN. - -#define MULTIBYTECODE 200 /* mbc Match one multi-byte character */ -#define RE_BOF 201 /* Match "" at beginning of file. */ -#define RE_EOF 202 /* Match "" at end of file. */ -#define CURSOR 203 /* Match location of cursor. */ - -#define RE_LNUM 204 /* nr cmp Match line number */ -#define RE_COL 205 /* nr cmp Match column number */ -#define RE_VCOL 206 /* nr cmp Match virtual column number */ - -#define RE_MARK 207 /* mark cmp Match mark position */ -#define RE_VISUAL 208 /* Match Visual area */ -#define RE_COMPOSING 209 // any composing characters - /* * Magic characters have a special meaning, they don't match literally. * Magic characters are negative. This separates them from literal characters @@ -285,107 +60,6 @@ */ typedef void (*(*fptr_T)(int *, int))(void); -typedef struct { - char_u *regparse; - int prevchr_len; - int curchr; - int prevchr; - int prevprevchr; - int nextchr; - int at_start; - int prev_at_start; - int regnpar; -} parse_state_T; - -/* - * Structure used to save the current input state, when it needs to be - * restored after trying a match. Used by reg_save() and reg_restore(). - * Also stores the length of "backpos". - */ -typedef struct { - union { - char_u *ptr; ///< rex.input pointer, for single-line regexp - lpos_T pos; ///< rex.input pos, for multi-line regexp - } rs_u; - int rs_len; -} regsave_T; - -/* struct to save start/end pointer/position in for \(\) */ -typedef struct { - union { - char_u *ptr; - lpos_T pos; - } se_u; -} save_se_T; - -/* used for BEHIND and NOBEHIND matching */ -typedef struct regbehind_S { - regsave_T save_after; - regsave_T save_behind; - int save_need_clear_subexpr; - save_se_T save_start[NSUBEXP]; - save_se_T save_end[NSUBEXP]; -} regbehind_T; - -/* Values for rs_state in regitem_T. */ -typedef enum regstate_E { - RS_NOPEN = 0 /* NOPEN and NCLOSE */ - , RS_MOPEN /* MOPEN + [0-9] */ - , RS_MCLOSE /* MCLOSE + [0-9] */ - , RS_ZOPEN /* ZOPEN + [0-9] */ - , RS_ZCLOSE /* ZCLOSE + [0-9] */ - , RS_BRANCH /* BRANCH */ - , RS_BRCPLX_MORE /* BRACE_COMPLEX and trying one more match */ - , RS_BRCPLX_LONG /* BRACE_COMPLEX and trying longest match */ - , RS_BRCPLX_SHORT /* BRACE_COMPLEX and trying shortest match */ - , RS_NOMATCH /* NOMATCH */ - , RS_BEHIND1 /* BEHIND / NOBEHIND matching rest */ - , RS_BEHIND2 /* BEHIND / NOBEHIND matching behind part */ - , RS_STAR_LONG /* STAR/PLUS/BRACE_SIMPLE longest match */ - , RS_STAR_SHORT /* STAR/PLUS/BRACE_SIMPLE shortest match */ -} regstate_T; - -/* - * When there are alternatives a regstate_T is put on the regstack to remember - * what we are doing. - * Before it may be another type of item, depending on rs_state, to remember - * more things. - */ -typedef struct regitem_S { - regstate_T rs_state; // what we are doing, one of RS_ above - uint16_t rs_no; // submatch nr or BEHIND/NOBEHIND - char_u *rs_scan; // current node in program - union { - save_se_T sesave; - regsave_T regsave; - } rs_un; ///< room for saving rex.input -} regitem_T; - - -/* used for STAR, PLUS and BRACE_SIMPLE matching */ -typedef struct regstar_S { - int nextb; /* next byte */ - int nextb_ic; /* next byte reverse case */ - long count; - long minval; - long maxval; -} regstar_T; - -/* used to store input position when a BACK was encountered, so that we now if - * we made any progress since the last time. */ -typedef struct backpos_S { - char_u *bp_scan; /* "scan" where BACK was encountered */ - regsave_T bp_pos; /* last input position */ -} backpos_T; - -typedef struct { - int a, b, c; -} decomp_T; - - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "regexp.c.generated.h" -#endif static int no_Magic(int x) { if (is_Magic(x)) @@ -400,66 +74,13 @@ static int toggle_Magic(int x) return Magic(x); } -/* - * The first byte of the regexp internal "program" is actually this magic - * number; the start node begins in the second byte. It's used to catch the - * most severe mutilation of the program by the caller. - */ - +// The first byte of the BT regexp internal "program" is actually this magic +// number; the start node begins in the second byte. It's used to catch the +// most severe mutilation of the program by the caller. #define REGMAGIC 0234 -/* - * Opcode notes: - * - * BRANCH The set of branches constituting a single choice are hooked - * together with their "next" pointers, since precedence prevents - * anything being concatenated to any individual branch. The - * "next" pointer of the last BRANCH in a choice points to the - * thing following the whole choice. This is also where the - * final "next" pointer of each individual branch points; each - * branch starts with the operand node of a BRANCH node. - * - * BACK Normal "next" pointers all implicitly point forward; BACK - * exists to make loop structures possible. - * - * STAR,PLUS '=', and complex '*' and '+', are implemented as circular - * BRANCH structures using BACK. Simple cases (one character - * per match) are implemented with STAR and PLUS for speed - * and to minimize recursive plunges. - * - * BRACE_LIMITS This is always followed by a BRACE_SIMPLE or BRACE_COMPLEX - * node, and defines the min and max limits to be used for that - * node. - * - * MOPEN,MCLOSE ...are numbered at compile time. - * ZOPEN,ZCLOSE ...ditto - */ - -/* - * A node is one char of opcode followed by two chars of "next" pointer. - * "Next" pointers are stored as two 8-bit bytes, high order first. The - * value is a positive offset from the opcode of the node containing it. - * An operand, if any, simply follows the node. (Note that much of the - * code generation knows about this implicit relationship.) - * - * Using two bytes for the "next" pointer is vast overkill for most things, - * but allows patterns to get big without disasters. - */ -#define OP(p) ((int)*(p)) -#define NEXT(p) (((*((p) + 1) & 0377) << 8) + (*((p) + 2) & 0377)) -#define OPERAND(p) ((p) + 3) -/* Obtain an operand that was stored as four bytes, MSB first. */ -#define OPERAND_MIN(p) (((long)(p)[3] << 24) + ((long)(p)[4] << 16) \ - + ((long)(p)[5] << 8) + (long)(p)[6]) -/* Obtain a second operand stored as four bytes. */ -#define OPERAND_MAX(p) OPERAND_MIN((p) + 4) -/* Obtain a second single-byte operand stored after a four bytes operand. */ -#define OPERAND_CMP(p) (p)[7] - -/* - * Utility definitions. - */ -#define UCHARAT(p) ((int)*(char_u *)(p)) +// Utility definitions. +#define UCHARAT(p) ((int)(*(char_u *)(p))) // Used for an error (down from) vim_regcomp(): give the error message, set // rc_did_emsg and return NULL @@ -477,14 +98,6 @@ static int toggle_Magic(int x) #define MAX_LIMIT (32767L << 16L) - -#ifdef BT_REGEXP_DUMP -static void regdump(char_u *, bt_regprog_T *); -#endif -#ifdef REGEXP_DEBUG -static char_u *regprop(char_u *); -#endif - static char_u e_missingbracket[] = N_("E769: Missing ] after %s["); static char_u e_reverse_range[] = N_("E944: Reverse range in character class"); static char_u e_large_class[] = N_("E945: Range too large in character class"); @@ -500,11 +113,17 @@ static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); #define NOT_MULTI 0 #define MULTI_ONE 1 #define MULTI_MULT 2 -/* - * Return NOT_MULTI if c is not a "multi" operator. - * Return MULTI_ONE if c is a single "multi" operator. - * Return MULTI_MULT if c is a multi "multi" operator. - */ + +// return values for regmatch() +#define RA_FAIL 1 // something failed, abort +#define RA_CONT 2 // continue in inner loop +#define RA_BREAK 3 // break inner loop +#define RA_MATCH 4 // successful match +#define RA_NOMATCH 5 // didn't match + +/// Return NOT_MULTI if c is not a "multi" operator. +/// Return MULTI_ONE if c is a single "multi" operator. +/// Return MULTI_MULT if c is a multi "multi" operator. static int re_multi_type(int c) { if (c == Magic('@') || c == Magic('=') || c == Magic('?')) @@ -514,22 +133,6 @@ static int re_multi_type(int c) return NOT_MULTI; } -/* - * Flags to be passed up and down. - */ -#define HASWIDTH 0x1 /* Known never to match null string. */ -#define SIMPLE 0x2 /* Simple enough to be STAR/PLUS operand. */ -#define SPSTART 0x4 /* Starts with * or +. */ -#define HASNL 0x8 /* Contains some \n. */ -#define HASLOOKBH 0x10 /* Contains "\@<=" or "\@ 1) { p += l; @@ -1204,4409 +505,662 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp) break; } else if (p[0] == '\\' && p[1] != NUL) { if (dirc == '?' && newp != NULL && p[1] == '?') { - /* change "\?" to "?", make a copy first. */ + // change "\?" to "?", make a copy first. if (*newp == NULL) { *newp = vim_strsave(startp); p = *newp + (p - startp); } STRMOVE(p, p + 1); - } else - ++p; /* skip next character */ - if (*p == 'v') + } else { + p++; // skip next character + } + if (*p == 'v') { mymagic = MAGIC_ALL; - else if (*p == 'V') + } else if (*p == 'V') { mymagic = MAGIC_NONE; - } - } - return p; -} - -/// Return true if the back reference is legal. We must have seen the close -/// brace. -/// TODO(vim): Should also check that we don't refer to something repeated -/// (+*=): what instance of the repetition should we match? -static int seen_endbrace(int refnum) -{ - if (!had_endbrace[refnum]) { - char_u *p; - - // Trick: check if "@<=" or "@re_in_use = false; - - /* - * Second pass: emit code. - */ - regcomp_start(expr, re_flags); - regcode = r->program; - regc(REGMAGIC); - if (reg(REG_NOPAREN, &flags) == NULL || reg_toolong) { - xfree(r); - if (reg_toolong) - EMSG_RET_NULL(_("E339: Pattern too long")); - return NULL; - } - - /* Dig out information for optimizations. */ - r->regstart = NUL; /* Worst-case defaults. */ - r->reganch = 0; - r->regmust = NULL; - r->regmlen = 0; - r->regflags = regflags; - if (flags & HASNL) - r->regflags |= RF_HASNL; - if (flags & HASLOOKBH) - r->regflags |= RF_LOOKBH; - /* Remember whether this pattern has any \z specials in it. */ - r->reghasz = re_has_z; - scan = r->program + 1; /* First BRANCH. */ - if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ - scan = OPERAND(scan); - - /* Starting-point info. */ - if (OP(scan) == BOL || OP(scan) == RE_BOF) { - r->reganch++; - scan = regnext(scan); - } - - if (OP(scan) == EXACTLY) { - r->regstart = utf_ptr2char(OPERAND(scan)); - } else if (OP(scan) == BOW - || OP(scan) == EOW - || OP(scan) == NOTHING - || OP(scan) == MOPEN + 0 || OP(scan) == NOPEN - || OP(scan) == MCLOSE + 0 || OP(scan) == NCLOSE) { - char_u *regnext_scan = regnext(scan); - if (OP(regnext_scan) == EXACTLY) { - r->regstart = utf_ptr2char(OPERAND(regnext_scan)); - } - } - /* - * If there's something expensive in the r.e., find the longest - * literal string that must appear and make it the regmust. Resolve - * ties in favor of later strings, since the regstart check works - * with the beginning of the r.e. and avoiding duplication - * strengthens checking. Not a strong reason, but sufficient in the - * absence of others. - */ - /* - * When the r.e. starts with BOW, it is faster to look for a regmust - * first. Used a lot for "#" and "*" commands. (Added by mool). - */ - if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW) - && !(flags & HASNL)) { - longest = NULL; - len = 0; - for (; scan != NULL; scan = regnext(scan)) - if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) { - longest = OPERAND(scan); - len = (int)STRLEN(OPERAND(scan)); - } - r->regmust = longest; - r->regmlen = len; - } - } -#ifdef BT_REGEXP_DUMP - regdump(expr, r); -#endif - r->engine = &bt_regengine; - return (regprog_T *)r; -} +// variables used for parsing +static int prevchr_len; // byte length of previous char +static int at_start; // True when on the first character +static int prev_at_start; // True when on the second character /* - * Free a compiled regexp program, returned by bt_regcomp(). + * Start parsing at "str". */ -static void bt_regfree(regprog_T *prog) +static void initchr(char_u *str) { - xfree(prog); + regparse = str; + prevchr_len = 0; + curchr = prevprevchr = prevchr = nextchr = -1; + at_start = true; + prev_at_start = false; } /* - * Setup to parse the regexp. Used once to get the length and once to do it. + * Save the current parse state, so that it can be restored and parsing + * starts in the same state again. */ -static void -regcomp_start ( - char_u *expr, - int re_flags /* see vim_regcomp() */ -) +static void save_parse_state(parse_state_T *ps) { - initchr(expr); - if (re_flags & RE_MAGIC) - reg_magic = MAGIC_ON; - else - reg_magic = MAGIC_OFF; - reg_string = (re_flags & RE_STRING); - reg_strict = (re_flags & RE_STRICT); - get_cpo_flags(); - - num_complex_braces = 0; - regnpar = 1; - memset(had_endbrace, 0, sizeof(had_endbrace)); - regnzpar = 1; - re_has_z = 0; - regsize = 0L; - reg_toolong = false; - regflags = 0; - had_eol = false; + ps->regparse = regparse; + ps->prevchr_len = prevchr_len; + ps->curchr = curchr; + ps->prevchr = prevchr; + ps->prevprevchr = prevprevchr; + ps->nextchr = nextchr; + ps->at_start = at_start; + ps->prev_at_start = prev_at_start; + ps->regnpar = regnpar; } /* - * Check if during the previous call to vim_regcomp the EOL item "$" has been - * found. This is messy, but it works fine. + * Restore a previously saved parse state. */ -int vim_regcomp_had_eol(void) +static void restore_parse_state(parse_state_T *ps) { - return had_eol; + regparse = ps->regparse; + prevchr_len = ps->prevchr_len; + curchr = ps->curchr; + prevchr = ps->prevchr; + prevprevchr = ps->prevprevchr; + nextchr = ps->nextchr; + at_start = ps->at_start; + prev_at_start = ps->prev_at_start; + regnpar = ps->regnpar; } -// variables used for parsing -static int at_start; // True when on the first character -static int prev_at_start; // True when on the second character - -/* - * Parse regular expression, i.e. main body or parenthesized thing. - * - * Caller must absorb opening parenthesis. - * - * Combining parenthesis handling with the base level of regular expression - * is a trifle forced, but the need to tie the tails of the branches to what - * follows makes it hard to avoid. - */ -static char_u * -reg ( - int paren, /* REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN */ - int *flagp -) -{ - char_u *ret; - char_u *br; - char_u *ender; - int parno = 0; - int flags; - - *flagp = HASWIDTH; /* Tentatively. */ - - if (paren == REG_ZPAREN) { - /* Make a ZOPEN node. */ - if (regnzpar >= NSUBEXP) - EMSG_RET_NULL(_("E50: Too many \\z(")); - parno = regnzpar; - regnzpar++; - ret = regnode(ZOPEN + parno); - } else if (paren == REG_PAREN) { - /* Make a MOPEN node. */ - if (regnpar >= NSUBEXP) - EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL); - parno = regnpar; - ++regnpar; - ret = regnode(MOPEN + parno); - } else if (paren == REG_NPAREN) { - /* Make a NOPEN node. */ - ret = regnode(NOPEN); - } else - ret = NULL; - - /* Pick up the branches, linking them together. */ - br = regbranch(&flags); - if (br == NULL) - return NULL; - if (ret != NULL) - regtail(ret, br); /* [MZ]OPEN -> first. */ - else - ret = br; - /* If one of the branches can be zero-width, the whole thing can. - * If one of the branches has * at start or matches a line-break, the - * whole thing can. */ - if (!(flags & HASWIDTH)) - *flagp &= ~HASWIDTH; - *flagp |= flags & (SPSTART | HASNL | HASLOOKBH); - while (peekchr() == Magic('|')) { - skipchr(); - br = regbranch(&flags); - if (br == NULL || reg_toolong) - return NULL; - regtail(ret, br); /* BRANCH -> BRANCH. */ - if (!(flags & HASWIDTH)) - *flagp &= ~HASWIDTH; - *flagp |= flags & (SPSTART | HASNL | HASLOOKBH); - } - - /* Make a closing node, and hook it on the end. */ - ender = regnode( - paren == REG_ZPAREN ? ZCLOSE + parno : - paren == REG_PAREN ? MCLOSE + parno : - paren == REG_NPAREN ? NCLOSE : END); - regtail(ret, ender); - - /* Hook the tails of the branches to the closing node. */ - for (br = ret; br != NULL; br = regnext(br)) - regoptail(br, ender); - - /* Check for proper termination. */ - if (paren != REG_NOPAREN && getchr() != Magic(')')) { - if (paren == REG_ZPAREN) - EMSG_RET_NULL(_("E52: Unmatched \\z(")); - else if (paren == REG_NPAREN) - EMSG2_RET_NULL(_(e_unmatchedpp), reg_magic == MAGIC_ALL); - else - EMSG2_RET_NULL(_(e_unmatchedp), reg_magic == MAGIC_ALL); - } else if (paren == REG_NOPAREN && peekchr() != NUL) { - if (curchr == Magic(')')) - EMSG2_RET_NULL(_(e_unmatchedpar), reg_magic == MAGIC_ALL); - else - EMSG_RET_NULL(_(e_trailing)); /* "Can't happen". */ - /* NOTREACHED */ - } - // Here we set the flag allowing back references to this set of - // parentheses. - if (paren == REG_PAREN) { - had_endbrace[parno] = true; // have seen the close paren - } - return ret; -} /* - * Parse one alternative of an | operator. - * Implements the & operator. + * Get the next character without advancing. */ -static char_u *regbranch(int *flagp) +static int peekchr(void) { - char_u *ret; - char_u *chain = NULL; - char_u *latest; - int flags; - - *flagp = WORST | HASNL; /* Tentatively. */ + static int after_slash = false; - ret = regnode(BRANCH); - for (;; ) { - latest = regconcat(&flags); - if (latest == NULL) - return NULL; - /* If one of the branches has width, the whole thing has. If one of - * the branches anchors at start-of-line, the whole thing does. - * If one of the branches uses look-behind, the whole thing does. */ - *flagp |= flags & (HASWIDTH | SPSTART | HASLOOKBH); - /* If one of the branches doesn't match a line-break, the whole thing - * doesn't. */ - *flagp &= ~HASNL | (flags & HASNL); - if (chain != NULL) - regtail(chain, latest); - if (peekchr() != Magic('&')) - break; - skipchr(); - regtail(latest, regnode(END)); /* operand ends */ - if (reg_toolong) - break; - reginsert(MATCH, latest); - chain = latest; + if (curchr != -1) { + return curchr; } - return ret; -} - -/* - * Parse one alternative of an | or & operator. - * Implements the concatenation operator. - */ -static char_u *regconcat(int *flagp) -{ - char_u *first = NULL; - char_u *chain = NULL; - char_u *latest; - int flags; - int cont = true; - - *flagp = WORST; /* Tentatively. */ - - while (cont) { - switch (peekchr()) { - case NUL: - case Magic('|'): - case Magic('&'): - case Magic(')'): - cont = false; - break; - case Magic('Z'): - regflags |= RF_ICOMBINE; - skipchr_keepstart(); - break; - case Magic('c'): - regflags |= RF_ICASE; - skipchr_keepstart(); - break; - case Magic('C'): - regflags |= RF_NOICASE; - skipchr_keepstart(); - break; - case Magic('v'): - reg_magic = MAGIC_ALL; - skipchr_keepstart(); - curchr = -1; - break; - case Magic('m'): - reg_magic = MAGIC_ON; - skipchr_keepstart(); - curchr = -1; - break; - case Magic('M'): - reg_magic = MAGIC_OFF; - skipchr_keepstart(); - curchr = -1; - break; - case Magic('V'): - reg_magic = MAGIC_NONE; - skipchr_keepstart(); - curchr = -1; - break; - default: - latest = regpiece(&flags); - if (latest == NULL || reg_toolong) - return NULL; - *flagp |= flags & (HASWIDTH | HASNL | HASLOOKBH); - if (chain == NULL) /* First piece. */ - *flagp |= flags & SPSTART; - else - regtail(chain, latest); - chain = latest; - if (first == NULL) - first = latest; - break; - } - } - if (first == NULL) /* Loop ran zero times. */ - first = regnode(NOTHING); - return first; -} - -/* - * Parse something followed by possible [*+=]. - * - * Note that the branching code sequences used for = and the general cases - * of * and + are somewhat optimized: they use the same NOTHING node as - * both the endmarker for their branch list and the body of the last branch. - * It might seem that this node could be dispensed with entirely, but the - * endmarker role is not redundant. - */ -static char_u *regpiece(int *flagp) -{ - char_u *ret; - int op; - char_u *next; - int flags; - long minval; - long maxval; - - ret = regatom(&flags); - if (ret == NULL) - return NULL; - - op = peekchr(); - if (re_multi_type(op) == NOT_MULTI) { - *flagp = flags; - return ret; - } - /* default flags */ - *flagp = (WORST | SPSTART | (flags & (HASNL | HASLOOKBH))); - - skipchr(); - switch (op) { - case Magic('*'): - if (flags & SIMPLE) - reginsert(STAR, ret); - else { - /* Emit x* as (x&|), where & means "self". */ - reginsert(BRANCH, ret); /* Either x */ - regoptail(ret, regnode(BACK)); /* and loop */ - regoptail(ret, ret); /* back */ - regtail(ret, regnode(BRANCH)); /* or */ - regtail(ret, regnode(NOTHING)); /* null. */ + switch (curchr = regparse[0]) { + case '.': + case '[': + case '~': + // magic when 'magic' is on + if (reg_magic >= MAGIC_ON) { + curchr = Magic(curchr); } break; - - case Magic('+'): - if (flags & SIMPLE) - reginsert(PLUS, ret); - else { - /* Emit x+ as x(&|), where & means "self". */ - next = regnode(BRANCH); /* Either */ - regtail(ret, next); - regtail(regnode(BACK), ret); /* loop back */ - regtail(next, regnode(BRANCH)); /* or */ - regtail(ret, regnode(NOTHING)); /* null. */ + case '(': + case ')': + case '{': + case '%': + case '+': + case '=': + case '?': + case '@': + case '!': + case '&': + case '|': + case '<': + case '>': + case '#': // future ext. + case '"': // future ext. + case '\'': // future ext. + case ',': // future ext. + case '-': // future ext. + case ':': // future ext. + case ';': // future ext. + case '`': // future ext. + case '/': // Can't be used in / command + // magic only after "\v" + if (reg_magic == MAGIC_ALL) { + curchr = Magic(curchr); } - *flagp = (WORST | HASWIDTH | (flags & (HASNL | HASLOOKBH))); break; - - case Magic('@'): - { - int lop = END; - int64_t nr = getdecchrs(); - - switch (no_Magic(getchr())) { - case '=': lop = MATCH; break; /* \@= */ - case '!': lop = NOMATCH; break; /* \@! */ - case '>': lop = SUBPAT; break; /* \@> */ - case '<': switch (no_Magic(getchr())) { - case '=': lop = BEHIND; break; /* \@<= */ - case '!': lop = NOBEHIND; break; /* \@= MAGIC_ON + && !at_start + && !(prev_at_start && prevchr == Magic('^')) + && (after_slash + || (prevchr != Magic('(') + && prevchr != Magic('&') + && prevchr != Magic('|')))) { + curchr = Magic('*'); } - if (lop == END) - EMSG2_RET_NULL(_("E59: invalid character after %s@"), - reg_magic == MAGIC_ALL); - /* Look behind must match with behind_pos. */ - if (lop == BEHIND || lop == NOBEHIND) { - regtail(ret, regnode(BHPOS)); - *flagp |= HASLOOKBH; + break; + case '^': + // '^' is only magic as the very first character and if it's after + // "\(", "\|", "\&' or "\n" + if (reg_magic >= MAGIC_OFF + && (at_start + || reg_magic == MAGIC_ALL + || prevchr == Magic('(') + || prevchr == Magic('|') + || prevchr == Magic('&') + || prevchr == Magic('n') + || (no_Magic(prevchr) == '(' + && prevprevchr == Magic('%')))) { + curchr = Magic('^'); + at_start = true; + prev_at_start = false; } - regtail(ret, regnode(END)); /* operand ends */ - if (lop == BEHIND || lop == NOBEHIND) { - if (nr < 0) - nr = 0; /* no limit is same as zero limit */ - reginsert_nr(lop, (uint32_t)nr, ret); - } else - reginsert(lop, ret); break; - } + case '$': + // '$' is only magic as the very last char and if it's in front of + // either "\|", "\)", "\&", or "\n" + if (reg_magic >= MAGIC_OFF) { + char_u *p = regparse + 1; + bool is_magic_all = (reg_magic == MAGIC_ALL); - case Magic('?'): - case Magic('='): - /* Emit x= as (x|) */ - reginsert(BRANCH, ret); /* Either x */ - regtail(ret, regnode(BRANCH)); /* or */ - next = regnode(NOTHING); /* null. */ - regtail(ret, next); - regoptail(ret, next); + // ignore \c \C \m \M \v \V and \Z after '$' + while (p[0] == '\\' && (p[1] == 'c' || p[1] == 'C' + || p[1] == 'm' || p[1] == 'M' + || p[1] == 'v' || p[1] == 'V' + || p[1] == 'Z')) { + if (p[1] == 'v') { + is_magic_all = true; + } else if (p[1] == 'm' || p[1] == 'M' || p[1] == 'V') { + is_magic_all = false; + } + p += 2; + } + if (p[0] == NUL + || (p[0] == '\\' + && (p[1] == '|' || p[1] == '&' || p[1] == ')' + || p[1] == 'n')) + || (is_magic_all + && (p[0] == '|' || p[0] == '&' || p[0] == ')')) + || reg_magic == MAGIC_ALL) { + curchr = Magic('$'); + } + } break; + case '\\': + { + int c = regparse[1]; - case Magic('{'): - if (!read_limits(&minval, &maxval)) - return NULL; - if (flags & SIMPLE) { - reginsert(BRACE_SIMPLE, ret); - reginsert_limits(BRACE_LIMITS, minval, maxval, ret); - } else { - if (num_complex_braces >= 10) - EMSG2_RET_NULL(_("E60: Too many complex %s{...}s"), - reg_magic == MAGIC_ALL); - reginsert(BRACE_COMPLEX + num_complex_braces, ret); - regoptail(ret, regnode(BACK)); - regoptail(ret, ret); - reginsert_limits(BRACE_LIMITS, minval, maxval, ret); - ++num_complex_braces; + if (c == NUL) { + curchr = '\\'; // trailing '\' + } else if (c <= '~' && META_flags[c]) { + // META contains everything that may be magic sometimes, + // except ^ and $ ("\^" and "\$" are only magic after + // "\V"). We now fetch the next character and toggle its + // magicness. Therefore, \ is so meta-magic that it is + // not in META. + curchr = -1; + prev_at_start = at_start; + at_start = false; // be able to say "/\*ptr" + regparse++; + after_slash++; + (void)peekchr(); + regparse--; + after_slash--; + curchr = toggle_Magic(curchr); + } else if (vim_strchr(REGEXP_ABBR, c)) { + /* + * Handle abbreviations, like "\t" for TAB -- webb + */ + curchr = backslash_trans(c); + } else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^')) + curchr = toggle_Magic(c); + else { + /* + * Next character can never be (made) magic? + * Then backslashing it won't do anything. + */ + curchr = utf_ptr2char(regparse + 1); } - if (minval > 0 && maxval > 0) - *flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH))); break; } - if (re_multi_type(peekchr()) != NOT_MULTI) { - // Can't have a multi follow a multi. - if (peekchr() == Magic('*')) { - EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON); - } - EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr())); + + default: + curchr = utf_ptr2char(regparse); } - return ret; + return curchr; } -/* When making changes to classchars also change nfa_classcodes. */ -static char_u *classchars = (char_u *)".iIkKfFpPsSdDxXoOwWhHaAlLuU"; -static int classcodes[] = { - ANY, IDENT, SIDENT, KWORD, SKWORD, - FNAME, SFNAME, PRINT, SPRINT, - WHITE, NWHITE, DIGIT, NDIGIT, - HEX, NHEX, OCTAL, NOCTAL, - WORD, NWORD, HEAD, NHEAD, - ALPHA, NALPHA, LOWER, NLOWER, - UPPER, NUPPER -}; +/* + * Eat one lexed character. Do this in a way that we can undo it. + */ +static void skipchr(void) +{ + // peekchr() eats a backslash, do the same here + if (*regparse == '\\') { + prevchr_len = 1; + } else { + prevchr_len = 0; + } + if (regparse[prevchr_len] != NUL) { + // Exclude composing chars that utfc_ptr2len does include. + prevchr_len += utf_ptr2len(regparse + prevchr_len); + } + regparse += prevchr_len; + prev_at_start = at_start; + at_start = false; + prevprevchr = prevchr; + prevchr = curchr; + curchr = nextchr; // use previously unget char, or -1 + nextchr = -1; +} /* - * Parse the lowest level. - * - * Optimization: gobbles an entire sequence of ordinary characters so that - * it can turn them into a single node, which is smaller to store and - * faster to run. Don't do this when one_exactly is set. + * Skip a character while keeping the value of prev_at_start for at_start. + * prevchr and prevprevchr are also kept. */ -static char_u *regatom(int *flagp) +static void skipchr_keepstart(void) { - char_u *ret; - int flags; - int c; - char_u *p; - int extra = 0; - int save_prev_at_start = prev_at_start; + int as = prev_at_start; + int pr = prevchr; + int prpr = prevprevchr; - *flagp = WORST; /* Tentatively. */ + skipchr(); + at_start = as; + prevchr = pr; + prevprevchr = prpr; +} - c = getchr(); - switch (c) { - case Magic('^'): - ret = regnode(BOL); - break; +/* + * Get the next character from the pattern. We know about magic and such, so + * therefore we need a lexical analyzer. + */ +static int getchr(void) +{ + int chr = peekchr(); - case Magic('$'): - ret = regnode(EOL); - had_eol = true; - break; + skipchr(); + return chr; +} - case Magic('<'): - ret = regnode(BOW); - break; +/* + * put character back. Works only once! + */ +static void ungetchr(void) +{ + nextchr = curchr; + curchr = prevchr; + prevchr = prevprevchr; + at_start = prev_at_start; + prev_at_start = false; - case Magic('>'): - ret = regnode(EOW); - break; + // Backup regparse, so that it's at the same position as before the + // getchr(). + regparse -= prevchr_len; +} - case Magic('_'): - c = no_Magic(getchr()); - if (c == '^') { /* "\_^" is start-of-line */ - ret = regnode(BOL); - break; - } - if (c == '$') { /* "\_$" is end-of-line */ - ret = regnode(EOL); - had_eol = true; - break; - } +/* + * Get and return the value of the hex string at the current position. + * Return -1 if there is no valid hex number. + * The position is updated: + * blahblah\%x20asdf + * before-^ ^-after + * The parameter controls the maximum number of input characters. This will be + * 2 when reading a \%x20 sequence and 4 when reading a \%u20AC sequence. + */ +static int64_t gethexchrs(int maxinputlen) +{ + int64_t nr = 0; + int c; + int i; - extra = ADD_NL; - *flagp |= HASNL; + for (i = 0; i < maxinputlen; ++i) { + c = regparse[0]; + if (!ascii_isxdigit(c)) + break; + nr <<= 4; + nr |= hex2nr(c); + ++regparse; + } - /* "\_[" is character range plus newline */ - if (c == '[') - goto collection; + if (i == 0) + return -1; + return nr; +} - // "\_x" is character class plus newline - FALLTHROUGH; +/* + * Get and return the value of the decimal string immediately after the + * current position. Return -1 for invalid. Consumes all digits. + */ +static int64_t getdecchrs(void) +{ + int64_t nr = 0; + int c; + int i; - /* - * Character classes. - */ - case Magic('.'): - case Magic('i'): - case Magic('I'): - case Magic('k'): - case Magic('K'): - case Magic('f'): - case Magic('F'): - case Magic('p'): - case Magic('P'): - case Magic('s'): - case Magic('S'): - case Magic('d'): - case Magic('D'): - case Magic('x'): - case Magic('X'): - case Magic('o'): - case Magic('O'): - case Magic('w'): - case Magic('W'): - case Magic('h'): - case Magic('H'): - case Magic('a'): - case Magic('A'): - case Magic('l'): - case Magic('L'): - case Magic('u'): - case Magic('U'): - p = vim_strchr(classchars, no_Magic(c)); - if (p == NULL) - EMSG_RET_NULL(_("E63: invalid use of \\_")); - /* When '.' is followed by a composing char ignore the dot, so that - * the composing char is matched here. */ - if (c == Magic('.') && utf_iscomposing(peekchr())) { - c = getchr(); - goto do_multibyte; - } - ret = regnode(classcodes[p - classchars] + extra); - *flagp |= HASWIDTH | SIMPLE; - break; + for (i = 0;; ++i) { + c = regparse[0]; + if (c < '0' || c > '9') + break; + nr *= 10; + nr += c - '0'; + regparse++; + curchr = -1; // no longer valid + } - case Magic('n'): - if (reg_string) { - /* In a string "\n" matches a newline character. */ - ret = regnode(EXACTLY); - regc(NL); - regc(NUL); - *flagp |= HASWIDTH | SIMPLE; - } else { - /* In buffer text "\n" matches the end of a line. */ - ret = regnode(NEWL); - *flagp |= HASWIDTH | HASNL; - } - break; - - case Magic('('): - if (one_exactly) - EMSG_ONE_RET_NULL; - ret = reg(REG_PAREN, &flags); - if (ret == NULL) - return NULL; - *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH); - break; - - case NUL: - case Magic('|'): - case Magic('&'): - case Magic(')'): - if (one_exactly) - EMSG_ONE_RET_NULL; - IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier. - // NOTREACHED - - case Magic('='): - case Magic('?'): - case Magic('+'): - case Magic('@'): - case Magic('{'): - case Magic('*'): - c = no_Magic(c); - EMSG3_RET_NULL(_("E64: %s%c follows nothing"), - (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c); - // NOTREACHED - - case Magic('~'): /* previous substitute pattern */ - if (reg_prev_sub != NULL) { - char_u *lp; - - ret = regnode(EXACTLY); - lp = reg_prev_sub; - while (*lp != NUL) - regc(*lp++); - regc(NUL); - if (*reg_prev_sub != NUL) { - *flagp |= HASWIDTH; - if ((lp - reg_prev_sub) == 1) - *flagp |= SIMPLE; - } - } else - EMSG_RET_NULL(_(e_nopresub)); - break; - - case Magic('1'): - case Magic('2'): - case Magic('3'): - case Magic('4'): - case Magic('5'): - case Magic('6'): - case Magic('7'): - case Magic('8'): - case Magic('9'): - { - int refnum; - - refnum = c - Magic('0'); - if (!seen_endbrace(refnum)) { - return NULL; - } - ret = regnode(BACKREF + refnum); - } - break; - - case Magic('z'): - { - c = no_Magic(getchr()); - switch (c) { - case '(': if ((reg_do_extmatch & REX_SET) == 0) - EMSG_RET_NULL(_(e_z_not_allowed)); - if (one_exactly) - EMSG_ONE_RET_NULL; - ret = reg(REG_ZPAREN, &flags); - if (ret == NULL) - return NULL; - *flagp |= flags & (HASWIDTH|SPSTART|HASNL|HASLOOKBH); - re_has_z = REX_SET; - break; - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': if ((reg_do_extmatch & REX_USE) == 0) - EMSG_RET_NULL(_(e_z1_not_allowed)); - ret = regnode(ZREF + c - '0'); - re_has_z = REX_USE; - break; + if (i == 0) + return -1; + return nr; +} - case 's': ret = regnode(MOPEN + 0); - if (!re_mult_next("\\zs")) { - return NULL; - } - break; +/* + * get and return the value of the octal string immediately after the current + * position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle + * numbers > 377 correctly (for example, 400 is treated as 40) and doesn't + * treat 8 or 9 as recognised characters. Position is updated: + * blahblah\%o210asdf + * before-^ ^-after + */ +static int64_t getoctchrs(void) +{ + int64_t nr = 0; + int c; + int i; - case 'e': ret = regnode(MCLOSE + 0); - if (!re_mult_next("\\ze")) { - return NULL; - } + for (i = 0; i < 3 && nr < 040; i++) { // -V536 + c = regparse[0]; + if (c < '0' || c > '7') break; - - default: EMSG_RET_NULL(_("E68: Invalid character after \\z")); - } + nr <<= 3; + nr |= hex2nr(c); + ++regparse; } - break; - - case Magic('%'): - { - c = no_Magic(getchr()); - switch (c) { - /* () without a back reference */ - case '(': - if (one_exactly) - EMSG_ONE_RET_NULL; - ret = reg(REG_NPAREN, &flags); - if (ret == NULL) - return NULL; - *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH); - break; - - /* Catch \%^ and \%$ regardless of where they appear in the - * pattern -- regardless of whether or not it makes sense. */ - case '^': - ret = regnode(RE_BOF); - break; - - case '$': - ret = regnode(RE_EOF); - break; - - case '#': - ret = regnode(CURSOR); - break; - - case 'V': - ret = regnode(RE_VISUAL); - break; - - case 'C': - ret = regnode(RE_COMPOSING); - break; - - /* \%[abc]: Emit as a list of branches, all ending at the last - * branch which matches nothing. */ - case '[': - if (one_exactly) /* doesn't nest */ - EMSG_ONE_RET_NULL; - { - char_u *lastbranch; - char_u *lastnode = NULL; - char_u *br; - - ret = NULL; - while ((c = getchr()) != ']') { - if (c == NUL) - EMSG2_RET_NULL(_(e_missing_sb), - reg_magic == MAGIC_ALL); - br = regnode(BRANCH); - if (ret == NULL) { - ret = br; - } else { - regtail(lastnode, br); - if (reg_toolong) { - return NULL; - } - } - - ungetchr(); - one_exactly = true; - lastnode = regatom(flagp); - one_exactly = false; - if (lastnode == NULL) { - return NULL; - } - } - if (ret == NULL) - EMSG2_RET_NULL(_(e_empty_sb), - reg_magic == MAGIC_ALL); - lastbranch = regnode(BRANCH); - br = regnode(NOTHING); - if (ret != JUST_CALC_SIZE) { - regtail(lastnode, br); - regtail(lastbranch, br); - /* connect all branches to the NOTHING - * branch at the end */ - for (br = ret; br != lastnode; ) { - if (OP(br) == BRANCH) { - regtail(br, lastbranch); - if (reg_toolong) { - return NULL; - } - br = OPERAND(br); - } else - br = regnext(br); - } - } - *flagp &= ~(HASWIDTH | SIMPLE); - break; - } - case 'd': /* %d123 decimal */ - case 'o': /* %o123 octal */ - case 'x': /* %xab hex 2 */ - case 'u': /* %uabcd hex 4 */ - case 'U': /* %U1234abcd hex 8 */ - { - int64_t i; - - switch (c) { - case 'd': i = getdecchrs(); break; - case 'o': i = getoctchrs(); break; - case 'x': i = gethexchrs(2); break; - case 'u': i = gethexchrs(4); break; - case 'U': i = gethexchrs(8); break; - default: i = -1; break; - } + if (i == 0) + return -1; + return nr; +} - if (i < 0 || i > INT_MAX) { - EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"), - reg_magic == MAGIC_ALL); - } - if (use_multibytecode(i)) { - ret = regnode(MULTIBYTECODE); - } else { - ret = regnode(EXACTLY); - } - if (i == 0) { - regc(0x0a); - } else { - regmbc(i); - } - regc(NUL); - *flagp |= HASWIDTH; - break; - } - default: - if (ascii_isdigit(c) || c == '<' || c == '>' - || c == '\'') { - uint32_t n = 0; - int cmp; - - cmp = c; - if (cmp == '<' || cmp == '>') - c = getchr(); - while (ascii_isdigit(c)) { - n = n * 10 + (uint32_t)(c - '0'); - c = getchr(); - } - if (c == '\'' && n == 0) { - /* "\%'m", "\%<'m" and "\%>'m": Mark */ - c = getchr(); - ret = regnode(RE_MARK); - if (ret == JUST_CALC_SIZE) - regsize += 2; - else { - *regcode++ = c; - *regcode++ = cmp; - } - break; - } else if (c == 'l' || c == 'c' || c == 'v') { - if (c == 'l') { - ret = regnode(RE_LNUM); - if (save_prev_at_start) { - at_start = true; - } - } else if (c == 'c') { - ret = regnode(RE_COL); - } else { - ret = regnode(RE_VCOL); - } - if (ret == JUST_CALC_SIZE) { - regsize += 5; - } else { - // put the number and the optional - // comparator after the opcode - regcode = re_put_uint32(regcode, n); - *regcode++ = cmp; - } - break; - } - } +/* + * read_limits - Read two integers to be taken as a minimum and maximum. + * If the first character is '-', then the range is reversed. + * Should end with 'end'. If minval is missing, zero is default, if maxval is + * missing, a very big number is the default. + */ +static int read_limits(long *minval, long *maxval) +{ + int reverse = false; + char_u *first_char; + long tmp; - EMSG2_RET_NULL(_("E71: Invalid character after %s%%"), - reg_magic == MAGIC_ALL); - } + if (*regparse == '-') { + // Starts with '-', so reverse the range later. + regparse++; + reverse = true; } - break; - - case Magic('['): -collection: - { - char_u *lp; - - /* - * If there is no matching ']', we assume the '[' is a normal - * character. This makes 'incsearch' and ":help [" work. - */ - lp = skip_anyof(regparse); - if (*lp == ']') { /* there is a matching ']' */ - int startc = -1; /* > 0 when next '-' is a range */ - int endc; - - /* - * In a character class, different parsing rules apply. - * Not even \ is special anymore, nothing is. - */ - if (*regparse == '^') { /* Complement of range. */ - ret = regnode(ANYBUT + extra); - regparse++; - } else - ret = regnode(ANYOF + extra); - - /* At the start ']' and '-' mean the literal character. */ - if (*regparse == ']' || *regparse == '-') { - startc = *regparse; - regc(*regparse++); - } - - while (*regparse != NUL && *regparse != ']') { - if (*regparse == '-') { - ++regparse; - /* The '-' is not used for a range at the end and - * after or before a '\n'. */ - if (*regparse == ']' || *regparse == NUL - || startc == -1 - || (regparse[0] == '\\' && regparse[1] == 'n')) { - regc('-'); - startc = '-'; /* [--x] is a range */ - } else { - /* Also accept "a-[.z.]" */ - endc = 0; - if (*regparse == '[') - endc = get_coll_element(®parse); - if (endc == 0) { - endc = mb_ptr2char_adv((const char_u **)®parse); - } - - /* Handle \o40, \x20 and \u20AC style sequences */ - if (endc == '\\' && !reg_cpo_lit) - endc = coll_get_char(); - - if (startc > endc) { - EMSG_RET_NULL(_(e_reverse_range)); - } - if (utf_char2len(startc) > 1 - || utf_char2len(endc) > 1) { - // Limit to a range of 256 chars - if (endc > startc + 256) { - EMSG_RET_NULL(_(e_large_class)); - } - while (++startc <= endc) { - regmbc(startc); - } - } else { - while (++startc <= endc) - regc(startc); - } - startc = -1; - } - } - /* - * Only "\]", "\^", "\]" and "\\" are special in Vi. Vim - * accepts "\t", "\e", etc., but only when the 'l' flag in - * 'cpoptions' is not included. - */ - else if (*regparse == '\\' - && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL - || (!reg_cpo_lit - && vim_strchr(REGEXP_ABBR, - regparse[1]) != NULL))) { - regparse++; - if (*regparse == 'n') { - /* '\n' in range: also match NL */ - if (ret != JUST_CALC_SIZE) { - /* Using \n inside [^] does not change what - * matches. "[^\n]" is the same as ".". */ - if (*ret == ANYOF) { - *ret = ANYOF + ADD_NL; - *flagp |= HASNL; - } - /* else: must have had a \n already */ - } - regparse++; - startc = -1; - } else if (*regparse == 'd' - || *regparse == 'o' - || *regparse == 'x' - || *regparse == 'u' - || *regparse == 'U') { - startc = coll_get_char(); - if (startc == 0) - regc(0x0a); - else - regmbc(startc); - } else { - startc = backslash_trans(*regparse++); - regc(startc); - } - } else if (*regparse == '[') { - int c_class; - int cu; - - c_class = get_char_class(®parse); - startc = -1; - /* Characters assumed to be 8 bits! */ - switch (c_class) { - case CLASS_NONE: - c_class = get_equi_class(®parse); - if (c_class != 0) { - /* produce equivalence class */ - reg_equi_class(c_class); - } else if ((c_class = - get_coll_element(®parse)) != 0) { - /* produce a collating element */ - regmbc(c_class); - } else { - /* literal '[', allow [[-x] as a range */ - startc = *regparse++; - regc(startc); - } - break; - case CLASS_ALNUM: - for (cu = 1; cu < 128; cu++) { - if (isalnum(cu)) { - regmbc(cu); - } - } - break; - case CLASS_ALPHA: - for (cu = 1; cu < 128; cu++) { - if (isalpha(cu)) { - regmbc(cu); - } - } - break; - case CLASS_BLANK: - regc(' '); - regc('\t'); - break; - case CLASS_CNTRL: - for (cu = 1; cu <= 127; cu++) { - if (iscntrl(cu)) { - regmbc(cu); - } - } - break; - case CLASS_DIGIT: - for (cu = 1; cu <= 127; cu++) { - if (ascii_isdigit(cu)) { - regmbc(cu); - } - } - break; - case CLASS_GRAPH: - for (cu = 1; cu <= 127; cu++) { - if (isgraph(cu)) { - regmbc(cu); - } - } - break; - case CLASS_LOWER: - for (cu = 1; cu <= 255; cu++) { - if (mb_islower(cu) && cu != 170 && cu != 186) { - regmbc(cu); - } - } - break; - case CLASS_PRINT: - for (cu = 1; cu <= 255; cu++) { - if (vim_isprintc(cu)) { - regmbc(cu); - } - } - break; - case CLASS_PUNCT: - for (cu = 1; cu < 128; cu++) { - if (ispunct(cu)) { - regmbc(cu); - } - } - break; - case CLASS_SPACE: - for (cu = 9; cu <= 13; cu++) - regc(cu); - regc(' '); - break; - case CLASS_UPPER: - for (cu = 1; cu <= 255; cu++) { - if (mb_isupper(cu)) { - regmbc(cu); - } - } - break; - case CLASS_XDIGIT: - for (cu = 1; cu <= 255; cu++) { - if (ascii_isxdigit(cu)) { - regmbc(cu); - } - } - break; - case CLASS_TAB: - regc('\t'); - break; - case CLASS_RETURN: - regc('\r'); - break; - case CLASS_BACKSPACE: - regc('\b'); - break; - case CLASS_ESCAPE: - regc(ESC); - break; - case CLASS_IDENT: - for (cu = 1; cu <= 255; cu++) { - if (vim_isIDc(cu)) { - regmbc(cu); - } - } - break; - case CLASS_KEYWORD: - for (cu = 1; cu <= 255; cu++) { - if (reg_iswordc(cu)) { - regmbc(cu); - } - } - break; - case CLASS_FNAME: - for (cu = 1; cu <= 255; cu++) { - if (vim_isfilec(cu)) { - regmbc(cu); - } - } - break; - } - } else { - // produce a multibyte character, including any - // following composing characters. - startc = utf_ptr2char(regparse); - int len = utfc_ptr2len(regparse); - if (utf_char2len(startc) != len) { - // composing chars - startc = -1; - } - while (--len >= 0) { - regc(*regparse++); - } - } - } - regc(NUL); - prevchr_len = 1; /* last char was the ']' */ - if (*regparse != ']') - EMSG_RET_NULL(_(e_toomsbra)); /* Cannot happen? */ - skipchr(); /* let's be friends with the lexer again */ - *flagp |= HASWIDTH | SIMPLE; - break; - } else if (reg_strict) - EMSG2_RET_NULL(_(e_missingbracket), reg_magic > MAGIC_OFF); - } - FALLTHROUGH; - - default: - { - int len; - - /* A multi-byte character is handled as a separate atom if it's - * before a multi and when it's a composing char. */ - if (use_multibytecode(c)) { -do_multibyte: - ret = regnode(MULTIBYTECODE); - regmbc(c); - *flagp |= HASWIDTH | SIMPLE; - break; - } - - ret = regnode(EXACTLY); - - /* - * Append characters as long as: - * - there is no following multi, we then need the character in - * front of it as a single character operand - * - not running into a Magic character - * - "one_exactly" is not set - * But always emit at least one character. Might be a Multi, - * e.g., a "[" without matching "]". - */ - for (len = 0; c != NUL && (len == 0 - || (re_multi_type(peekchr()) == NOT_MULTI - && !one_exactly - && !is_Magic(c))); ++len) { - c = no_Magic(c); - { - regmbc(c); - { - int l; - - /* Need to get composing character too. */ - for (;; ) { - l = utf_ptr2len(regparse); - if (!utf_composinglike(regparse, regparse + l)) { - break; - } - regmbc(utf_ptr2char(regparse)); - skipchr(); - } - } - } - c = getchr(); + first_char = regparse; + *minval = getdigits_long(®parse, false, 0); + if (*regparse == ',') { // There is a comma. + if (ascii_isdigit(*++regparse)) { + *maxval = getdigits_long(®parse, false, MAX_LIMIT); + } else { + *maxval = MAX_LIMIT; } - ungetchr(); - - regc(NUL); - *flagp |= HASWIDTH; - if (len == 1) - *flagp |= SIMPLE; + } else if (ascii_isdigit(*first_char)) { + *maxval = *minval; // It was \{n} or \{-n} + } else { + *maxval = MAX_LIMIT; // It was \{} or \{-} } - break; + if (*regparse == '\\') { + regparse++; // Allow either \{...} or \{...\} } - - return ret; -} - -/// Used in a place where no * or \+ can follow. -static bool re_mult_next(char *what) -{ - if (re_multi_type(peekchr()) == MULTI_MULT) { - semsg(_("E888: (NFA regexp) cannot repeat %s"), what); - rc_did_emsg = true; - return false; + if (*regparse != '}') { + EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL); } - return true; -} - -// Return true if MULTIBYTECODE should be used instead of EXACTLY for -// character "c". -static bool use_multibytecode(int c) -{ - return utf_char2len(c) > 1 - && (re_multi_type(peekchr()) != NOT_MULTI - || utf_iscomposing(c)); -} -/* - * Emit a node. - * Return pointer to generated code. - */ -static char_u *regnode(int op) -{ - char_u *ret; - - ret = regcode; - if (ret == JUST_CALC_SIZE) - regsize += 3; - else { - *regcode++ = op; - *regcode++ = NUL; /* Null "next" pointer. */ - *regcode++ = NUL; + /* + * Reverse the range if there was a '-', or make sure it is in the right + * order otherwise. + */ + if ((!reverse && *minval > *maxval) || (reverse && *minval < *maxval)) { + tmp = *minval; + *minval = *maxval; + *maxval = tmp; } - return ret; -} - -/* - * Emit (if appropriate) a byte of code - */ -static void regc(int b) -{ - if (regcode == JUST_CALC_SIZE) - regsize++; - else - *regcode++ = b; + skipchr(); // let's be friends with the lexer again + return OK; } /* - * Emit (if appropriate) a multi-byte character of code + * vim_regexec and friends */ -static void regmbc(int c) -{ - if (regcode == JUST_CALC_SIZE) { - regsize += utf_char2len(c); - } else { - regcode += utf_char2bytes(c, regcode); - } -} /* - * Insert an operator in front of already-emitted operand - * - * Means relocating the operand. + * Global work variables for vim_regexec(). */ -static void reginsert(int op, char_u *opnd) -{ - char_u *src; - char_u *dst; - char_u *place; - if (regcode == JUST_CALC_SIZE) { - regsize += 3; - return; - } - src = regcode; - regcode += 3; - dst = regcode; - while (src > opnd) - *--dst = *--src; - - place = opnd; /* Op node, where operand used to be. */ - *place++ = op; - *place++ = NUL; - *place = NUL; -} +// Sometimes need to save a copy of a line. Since alloc()/free() is very +// slow, we keep one allocated piece of memory and only re-allocate it when +// it's too small. It's freed in bt_regexec_both() when finished. +static char_u *reg_tofree = NULL; +static unsigned reg_tofreelen; -/* - * Insert an operator in front of already-emitted operand. - * Add a number to the operator. - */ -static void reginsert_nr(int op, long val, char_u *opnd) -{ - char_u *src; - char_u *dst; - char_u *place; - - if (regcode == JUST_CALC_SIZE) { - regsize += 7; - return; - } - src = regcode; - regcode += 7; - dst = regcode; - while (src > opnd) - *--dst = *--src; - - place = opnd; /* Op node, where operand used to be. */ - *place++ = op; - *place++ = NUL; - *place++ = NUL; - assert(val >= 0 && (uintmax_t)val <= UINT32_MAX); - re_put_uint32(place, (uint32_t)val); -} - -/* - * Insert an operator in front of already-emitted operand. - * The operator has the given limit values as operands. Also set next pointer. - * - * Means relocating the operand. - */ -static void reginsert_limits(int op, long minval, long maxval, char_u *opnd) -{ - char_u *src; - char_u *dst; - char_u *place; - - if (regcode == JUST_CALC_SIZE) { - regsize += 11; - return; - } - src = regcode; - regcode += 11; - dst = regcode; - while (src > opnd) - *--dst = *--src; - - place = opnd; /* Op node, where operand used to be. */ - *place++ = op; - *place++ = NUL; - *place++ = NUL; - assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX); - place = re_put_uint32(place, (uint32_t)minval); - assert(maxval >= 0 && (uintmax_t)maxval <= UINT32_MAX); - place = re_put_uint32(place, (uint32_t)maxval); - regtail(opnd, place); -} - -/* - * Write a four bytes number at "p" and return pointer to the next char. - */ -static char_u *re_put_uint32(char_u *p, uint32_t val) -{ - *p++ = (char_u) ((val >> 24) & 0377); - *p++ = (char_u) ((val >> 16) & 0377); - *p++ = (char_u) ((val >> 8) & 0377); - *p++ = (char_u) (val & 0377); - return p; -} - -// Set the next-pointer at the end of a node chain. -static void regtail(char_u *p, char_u *val) -{ - int offset; - - if (p == JUST_CALC_SIZE) { - return; - } - - // Find last node. - char_u *scan = p; - for (;; ) { - char_u *temp = regnext(scan); - if (temp == NULL) { - break; - } - scan = temp; - } - - if (OP(scan) == BACK) { - offset = (int)(scan - val); - } else { - offset = (int)(val - scan); - } - // When the offset uses more than 16 bits it can no longer fit in the two - // bytes available. Use a global flag to avoid having to check return - // values in too many places. - if (offset > 0xffff) { - reg_toolong = true; - } else { - *(scan + 1) = (char_u)(((unsigned)offset >> 8) & 0377); - *(scan + 2) = (char_u)(offset & 0377); - } -} - -/* - * Like regtail, on item after a BRANCH; nop if none. - */ -static void regoptail(char_u *p, char_u *val) -{ - /* When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless" */ - if (p == NULL || p == JUST_CALC_SIZE - || (OP(p) != BRANCH - && (OP(p) < BRACE_COMPLEX || OP(p) > BRACE_COMPLEX + 9))) - return; - regtail(OPERAND(p), val); -} - -/* - * Functions for getting characters from the regexp input. - */ - -/* - * Start parsing at "str". - */ -static void initchr(char_u *str) -{ - regparse = str; - prevchr_len = 0; - curchr = prevprevchr = prevchr = nextchr = -1; - at_start = true; - prev_at_start = false; -} - -/* - * Save the current parse state, so that it can be restored and parsing - * starts in the same state again. - */ -static void save_parse_state(parse_state_T *ps) -{ - ps->regparse = regparse; - ps->prevchr_len = prevchr_len; - ps->curchr = curchr; - ps->prevchr = prevchr; - ps->prevprevchr = prevprevchr; - ps->nextchr = nextchr; - ps->at_start = at_start; - ps->prev_at_start = prev_at_start; - ps->regnpar = regnpar; -} - -/* - * Restore a previously saved parse state. - */ -static void restore_parse_state(parse_state_T *ps) -{ - regparse = ps->regparse; - prevchr_len = ps->prevchr_len; - curchr = ps->curchr; - prevchr = ps->prevchr; - prevprevchr = ps->prevprevchr; - nextchr = ps->nextchr; - at_start = ps->at_start; - prev_at_start = ps->prev_at_start; - regnpar = ps->regnpar; -} - - -/* - * Get the next character without advancing. - */ -static int peekchr(void) -{ - static int after_slash = false; - - if (curchr != -1) { - return curchr; - } - - switch (curchr = regparse[0]) { - case '.': - case '[': - case '~': - /* magic when 'magic' is on */ - if (reg_magic >= MAGIC_ON) - curchr = Magic(curchr); - break; - case '(': - case ')': - case '{': - case '%': - case '+': - case '=': - case '?': - case '@': - case '!': - case '&': - case '|': - case '<': - case '>': - case '#': /* future ext. */ - case '"': /* future ext. */ - case '\'': /* future ext. */ - case ',': /* future ext. */ - case '-': /* future ext. */ - case ':': /* future ext. */ - case ';': /* future ext. */ - case '`': /* future ext. */ - case '/': /* Can't be used in / command */ - /* magic only after "\v" */ - if (reg_magic == MAGIC_ALL) - curchr = Magic(curchr); - break; - case '*': - // * is not magic as the very first character, eg "?*ptr", when - // after '^', eg "/^*ptr" and when after "\(", "\|", "\&". But - // "\(\*" is not magic, thus must be magic if "after_slash" - if (reg_magic >= MAGIC_ON - && !at_start - && !(prev_at_start && prevchr == Magic('^')) - && (after_slash - || (prevchr != Magic('(') - && prevchr != Magic('&') - && prevchr != Magic('|')))) { - curchr = Magic('*'); - } - break; - case '^': - // '^' is only magic as the very first character and if it's after - // "\(", "\|", "\&' or "\n" - if (reg_magic >= MAGIC_OFF - && (at_start - || reg_magic == MAGIC_ALL - || prevchr == Magic('(') - || prevchr == Magic('|') - || prevchr == Magic('&') - || prevchr == Magic('n') - || (no_Magic(prevchr) == '(' - && prevprevchr == Magic('%')))) { - curchr = Magic('^'); - at_start = true; - prev_at_start = false; - } - break; - case '$': - // '$' is only magic as the very last char and if it's in front of - // either "\|", "\)", "\&", or "\n" - if (reg_magic >= MAGIC_OFF) { - char_u *p = regparse + 1; - bool is_magic_all = (reg_magic == MAGIC_ALL); - - // ignore \c \C \m \M \v \V and \Z after '$' - while (p[0] == '\\' && (p[1] == 'c' || p[1] == 'C' - || p[1] == 'm' || p[1] == 'M' - || p[1] == 'v' || p[1] == 'V' - || p[1] == 'Z')) { - if (p[1] == 'v') { - is_magic_all = true; - } else if (p[1] == 'm' || p[1] == 'M' || p[1] == 'V') { - is_magic_all = false; - } - p += 2; - } - if (p[0] == NUL - || (p[0] == '\\' - && (p[1] == '|' || p[1] == '&' || p[1] == ')' - || p[1] == 'n')) - || (is_magic_all - && (p[0] == '|' || p[0] == '&' || p[0] == ')')) - || reg_magic == MAGIC_ALL) { - curchr = Magic('$'); - } - } - break; - case '\\': - { - int c = regparse[1]; - - if (c == NUL) { - curchr = '\\'; // trailing '\' - } else if (c <= '~' && META_flags[c]) { - // META contains everything that may be magic sometimes, - // except ^ and $ ("\^" and "\$" are only magic after - // "\V"). We now fetch the next character and toggle its - // magicness. Therefore, \ is so meta-magic that it is - // not in META. - curchr = -1; - prev_at_start = at_start; - at_start = false; // be able to say "/\*ptr" - regparse++; - after_slash++; - (void)peekchr(); - regparse--; - after_slash--; - curchr = toggle_Magic(curchr); - } else if (vim_strchr(REGEXP_ABBR, c)) { - /* - * Handle abbreviations, like "\t" for TAB -- webb - */ - curchr = backslash_trans(c); - } else if (reg_magic == MAGIC_NONE && (c == '$' || c == '^')) - curchr = toggle_Magic(c); - else { - /* - * Next character can never be (made) magic? - * Then backslashing it won't do anything. - */ - curchr = utf_ptr2char(regparse + 1); - } - break; - } - - default: - curchr = utf_ptr2char(regparse); - } - - return curchr; -} - -/* - * Eat one lexed character. Do this in a way that we can undo it. - */ -static void skipchr(void) -{ - /* peekchr() eats a backslash, do the same here */ - if (*regparse == '\\') - prevchr_len = 1; - else - prevchr_len = 0; - if (regparse[prevchr_len] != NUL) { - // Exclude composing chars that utfc_ptr2len does include. - prevchr_len += utf_ptr2len(regparse + prevchr_len); - } - regparse += prevchr_len; - prev_at_start = at_start; - at_start = false; - prevprevchr = prevchr; - prevchr = curchr; - curchr = nextchr; /* use previously unget char, or -1 */ - nextchr = -1; -} - -/* - * Skip a character while keeping the value of prev_at_start for at_start. - * prevchr and prevprevchr are also kept. - */ -static void skipchr_keepstart(void) -{ - int as = prev_at_start; - int pr = prevchr; - int prpr = prevprevchr; - - skipchr(); - at_start = as; - prevchr = pr; - prevprevchr = prpr; -} - -/* - * Get the next character from the pattern. We know about magic and such, so - * therefore we need a lexical analyzer. - */ -static int getchr(void) -{ - int chr = peekchr(); - - skipchr(); - return chr; -} - -/* - * put character back. Works only once! - */ -static void ungetchr(void) -{ - nextchr = curchr; - curchr = prevchr; - prevchr = prevprevchr; - at_start = prev_at_start; - prev_at_start = false; - - // Backup regparse, so that it's at the same position as before the - // getchr(). - regparse -= prevchr_len; -} - -/* - * Get and return the value of the hex string at the current position. - * Return -1 if there is no valid hex number. - * The position is updated: - * blahblah\%x20asdf - * before-^ ^-after - * The parameter controls the maximum number of input characters. This will be - * 2 when reading a \%x20 sequence and 4 when reading a \%u20AC sequence. - */ -static int64_t gethexchrs(int maxinputlen) -{ - int64_t nr = 0; - int c; - int i; - - for (i = 0; i < maxinputlen; ++i) { - c = regparse[0]; - if (!ascii_isxdigit(c)) - break; - nr <<= 4; - nr |= hex2nr(c); - ++regparse; - } - - if (i == 0) - return -1; - return nr; -} - -/* - * Get and return the value of the decimal string immediately after the - * current position. Return -1 for invalid. Consumes all digits. - */ -static int64_t getdecchrs(void) -{ - int64_t nr = 0; - int c; - int i; - - for (i = 0;; ++i) { - c = regparse[0]; - if (c < '0' || c > '9') - break; - nr *= 10; - nr += c - '0'; - ++regparse; - curchr = -1; /* no longer valid */ - } - - if (i == 0) - return -1; - return nr; -} - -/* - * get and return the value of the octal string immediately after the current - * position. Return -1 for invalid, or 0-255 for valid. Smart enough to handle - * numbers > 377 correctly (for example, 400 is treated as 40) and doesn't - * treat 8 or 9 as recognised characters. Position is updated: - * blahblah\%o210asdf - * before-^ ^-after - */ -static int64_t getoctchrs(void) -{ - int64_t nr = 0; - int c; - int i; - - for (i = 0; i < 3 && nr < 040; i++) { // -V536 - c = regparse[0]; - if (c < '0' || c > '7') - break; - nr <<= 3; - nr |= hex2nr(c); - ++regparse; - } - - if (i == 0) - return -1; - return nr; -} - -/* - * Get a number after a backslash that is inside []. - * When nothing is recognized return a backslash. - */ -static int coll_get_char(void) -{ - int64_t nr = -1; - - switch (*regparse++) { - case 'd': nr = getdecchrs(); break; - case 'o': nr = getoctchrs(); break; - case 'x': nr = gethexchrs(2); break; - case 'u': nr = gethexchrs(4); break; - case 'U': nr = gethexchrs(8); break; - } - if (nr < 0 || nr > INT_MAX) { - // If getting the number fails be backwards compatible: the character - // is a backslash. - regparse--; - nr = '\\'; - } - return nr; -} - -/* - * read_limits - Read two integers to be taken as a minimum and maximum. - * If the first character is '-', then the range is reversed. - * Should end with 'end'. If minval is missing, zero is default, if maxval is - * missing, a very big number is the default. - */ -static int read_limits(long *minval, long *maxval) -{ - int reverse = false; - char_u *first_char; - long tmp; - - if (*regparse == '-') { - // Starts with '-', so reverse the range later. - regparse++; - reverse = true; - } - first_char = regparse; - *minval = getdigits_long(®parse, false, 0); - if (*regparse == ',') { // There is a comma. - if (ascii_isdigit(*++regparse)) { - *maxval = getdigits_long(®parse, false, MAX_LIMIT); - } else { - *maxval = MAX_LIMIT; - } - } else if (ascii_isdigit(*first_char)) { - *maxval = *minval; // It was \{n} or \{-n} - } else { - *maxval = MAX_LIMIT; // It was \{} or \{-} - } - if (*regparse == '\\') { - regparse++; // Allow either \{...} or \{...\} - } - if (*regparse != '}') { - EMSG2_RET_FAIL(_("E554: Syntax error in %s{...}"), reg_magic == MAGIC_ALL); - } - - /* - * Reverse the range if there was a '-', or make sure it is in the right - * order otherwise. - */ - if ((!reverse && *minval > *maxval) || (reverse && *minval < *maxval)) { - tmp = *minval; - *minval = *maxval; - *maxval = tmp; - } - skipchr(); /* let's be friends with the lexer again */ - return OK; -} - -/* - * vim_regexec and friends - */ - -/* - * Global work variables for vim_regexec(). - */ - -/* Save the sub-expressions before attempting a match. */ -#define save_se(savep, posp, pp) \ - REG_MULTI ? save_se_multi((savep), (posp)) : save_se_one((savep), (pp)) - -/* After a failed match restore the sub-expressions. */ -#define restore_se(savep, posp, pp) { \ - if (REG_MULTI) \ - *(posp) = (savep)->se_u.pos; \ - else \ - *(pp) = (savep)->se_u.ptr; } - - -#ifdef REGEXP_DEBUG -int regnarrate = 0; -#endif - -// Sometimes need to save a copy of a line. Since alloc()/free() is very -// slow, we keep one allocated piece of memory and only re-allocate it when -// it's too small. It's freed in bt_regexec_both() when finished. -static char_u *reg_tofree = NULL; -static unsigned reg_tofreelen; - -// Structure used to store the execution state of the regex engine. -// Which ones are set depends on whether a single-line or multi-line match is -// done: -// single-line multi-line -// reg_match ®match_T NULL -// reg_mmatch NULL ®mmatch_T -// reg_startp reg_match->startp -// reg_endp reg_match->endp -// reg_startpos reg_mmatch->startpos -// reg_endpos reg_mmatch->endpos -// reg_win NULL window in which to search -// reg_buf curbuf buffer in which to search -// reg_firstlnum first line in which to search -// reg_maxline 0 last line nr -// reg_line_lbr false or true false -typedef struct { - regmatch_T *reg_match; - regmmatch_T *reg_mmatch; - char_u **reg_startp; - char_u **reg_endp; - lpos_T *reg_startpos; - lpos_T *reg_endpos; - win_T *reg_win; - buf_T *reg_buf; - linenr_T reg_firstlnum; - linenr_T reg_maxline; - bool reg_line_lbr; // "\n" in string is line break - - // The current match-position is remembered with these variables: - linenr_T lnum; ///< line number, relative to first line - char_u *line; ///< start of current line - char_u *input; ///< current input, points into "line" - - int need_clear_subexpr; ///< subexpressions still need to be cleared - int need_clear_zsubexpr; ///< extmatch subexpressions still need to be - ///< cleared - - - // Internal copy of 'ignorecase'. It is set at each call to vim_regexec(). - // Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern - // contains '\c' or '\C' the value is overruled. - bool reg_ic; - - // Similar to "reg_ic", but only for 'combining' characters. Set with \Z - // flag in the regexp. Defaults to false, always. - bool reg_icombine; - - // Copy of "rmm_maxcol": maximum column to search for a match. Zero when - // there is no maximum. - colnr_T reg_maxcol; - - // State for the NFA engine regexec. - int nfa_has_zend; ///< NFA regexp \ze operator encountered. - int nfa_has_backref; ///< NFA regexp \1 .. \9 encountered. - int nfa_nsubexpr; ///< Number of sub expressions actually being used - ///< during execution. 1 if only the whole match - ///< (subexpr 0) is used. - // listid is global, so that it increases on recursive calls to - // nfa_regmatch(), which means we don't have to clear the lastlist field of - // all the states. - int nfa_listid; - int nfa_alt_listid; - - int nfa_has_zsubexpr; ///< NFA regexp has \z( ), set zsubexpr. -} regexec_T; - -static regexec_T rex; -static bool rex_in_use = false; - -/* - * "regstack" and "backpos" are used by regmatch(). They are kept over calls - * to avoid invoking malloc() and free() often. - * "regstack" is a stack with regitem_T items, sometimes preceded by regstar_T - * or regbehind_T. - * "backpos_T" is a table with backpos_T for BACK - */ -static garray_T regstack = GA_EMPTY_INIT_VALUE; -static garray_T backpos = GA_EMPTY_INIT_VALUE; - -/* - * Both for regstack and backpos tables we use the following strategy of - * allocation (to reduce malloc/free calls): - * - Initial size is fairly small. - * - When needed, the tables are grown bigger (8 times at first, double after - * that). - * - After executing the match we free the memory only if the array has grown. - * Thus the memory is kept allocated when it's at the initial size. - * This makes it fast while not keeping a lot of memory allocated. - * A three times speed increase was observed when using many simple patterns. - */ -#define REGSTACK_INITIAL 2048 -#define BACKPOS_INITIAL 64 - -#if defined(EXITFREE) -void free_regexp_stuff(void) -{ - ga_clear(®stack); - ga_clear(&backpos); - xfree(reg_tofree); - xfree(reg_prev_sub); -} - -#endif - -// Return true if character 'c' is included in 'iskeyword' option for -// "reg_buf" buffer. -static bool reg_iswordc(int c) -{ - return vim_iswordc_buf(c, rex.reg_buf); -} - -/* - * Get pointer to the line "lnum", which is relative to "reg_firstlnum". - */ -static char_u *reg_getline(linenr_T lnum) -{ - // when looking behind for a match/no-match lnum is negative. But we - // can't go before line 1 - if (rex.reg_firstlnum + lnum < 1) { - return NULL; - } - if (lnum > rex.reg_maxline) { - // Must have matched the "\n" in the last line. - return (char_u *)""; - } - return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false); -} - -static regsave_T behind_pos; - -static char_u *reg_startzp[NSUBEXP]; /* Workspace to mark beginning */ -static char_u *reg_endzp[NSUBEXP]; /* and end of \z(...\) matches */ -static lpos_T reg_startzpos[NSUBEXP]; /* idem, beginning pos */ -static lpos_T reg_endzpos[NSUBEXP]; /* idem, end pos */ - -// true if using multi-line regexp. -#define REG_MULTI (rex.reg_match == NULL) - -/* - * Match a regexp against a string. - * "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). - * Uses curbuf for line count and 'iskeyword'. - * If "line_lbr" is true, consider a "\n" in "line" to be a line break. - * - * Returns 0 for failure, number of lines contained in the match otherwise. - */ -static int -bt_regexec_nl ( - regmatch_T *rmp, - char_u *line, /* string to match against */ - colnr_T col, /* column to start looking for match */ - bool line_lbr -) -{ - rex.reg_match = rmp; - rex.reg_mmatch = NULL; - rex.reg_maxline = 0; - rex.reg_line_lbr = line_lbr; - rex.reg_buf = curbuf; - rex.reg_win = NULL; - rex.reg_ic = rmp->rm_ic; - rex.reg_icombine = false; - rex.reg_maxcol = 0; - - long r = bt_regexec_both(line, col, NULL, NULL); - assert(r <= INT_MAX); - return (int)r; -} - -/// Wrapper around strchr which accounts for case-insensitive searches and -/// non-ASCII characters. -/// -/// This function is used a lot for simple searches, keep it fast! -/// -/// @param s string to search -/// @param c character to find in @a s -/// -/// @return NULL if no match, otherwise pointer to the position in @a s -static inline char_u *cstrchr(const char_u *const s, const int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL - FUNC_ATTR_ALWAYS_INLINE -{ - if (!rex.reg_ic) { - return vim_strchr(s, c); - } - - // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is - // expected to be highly optimized. - if (c > 0x80) { - const int folded_c = utf_fold(c); - for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { - if (utf_fold(utf_ptr2char(p)) == folded_c) { - return (char_u *)p; - } - } - return NULL; - } - - int cc; - if (ASCII_ISUPPER(c)) { - cc = TOLOWER_ASC(c); - } else if (ASCII_ISLOWER(c)) { - cc = TOUPPER_ASC(c); - } else { - return vim_strchr(s, c); - } - - char tofind[] = { (char)c, (char)cc, NUL }; - return (char_u *)strpbrk((const char *)s, tofind); -} - -/// Matches a regexp against multiple lines. -/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). -/// Uses curbuf for line count and 'iskeyword'. -/// -/// @param win Window in which to search or NULL -/// @param buf Buffer in which to search -/// @param lnum Number of line to start looking for match -/// @param col Column to start looking for match -/// @param tm Timeout limit or NULL -/// -/// @return zero if there is no match and number of lines contained in the match -/// otherwise. -static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, - linenr_T lnum, colnr_T col, - proftime_T *tm, int *timed_out) -{ - rex.reg_match = NULL; - rex.reg_mmatch = rmp; - rex.reg_buf = buf; - rex.reg_win = win; - rex.reg_firstlnum = lnum; - rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum; - rex.reg_line_lbr = false; - rex.reg_ic = rmp->rmm_ic; - rex.reg_icombine = false; - rex.reg_maxcol = rmp->rmm_maxcol; - - return bt_regexec_both(NULL, col, tm, timed_out); -} - -/// Match a regexp against a string ("line" points to the string) or multiple -/// lines (if "line" is NULL, use reg_getline()). -/// @return 0 for failure, or number of lines contained in the match. -static long bt_regexec_both(char_u *line, - colnr_T col, // column to start search - proftime_T *tm, // timeout limit or NULL - int *timed_out) // flag set on timeout or NULL -{ - bt_regprog_T *prog; - char_u *s; - long retval = 0L; - - /* Create "regstack" and "backpos" if they are not allocated yet. - * We allocate *_INITIAL amount of bytes first and then set the grow size - * to much bigger value to avoid many malloc calls in case of deep regular - * expressions. */ - if (regstack.ga_data == NULL) { - /* Use an item size of 1 byte, since we push different things - * onto the regstack. */ - ga_init(®stack, 1, REGSTACK_INITIAL); - ga_grow(®stack, REGSTACK_INITIAL); - ga_set_growsize(®stack, REGSTACK_INITIAL * 8); - } - - if (backpos.ga_data == NULL) { - ga_init(&backpos, sizeof(backpos_T), BACKPOS_INITIAL); - ga_grow(&backpos, BACKPOS_INITIAL); - ga_set_growsize(&backpos, BACKPOS_INITIAL * 8); - } - - if (REG_MULTI) { - prog = (bt_regprog_T *)rex.reg_mmatch->regprog; - line = reg_getline((linenr_T)0); - rex.reg_startpos = rex.reg_mmatch->startpos; - rex.reg_endpos = rex.reg_mmatch->endpos; - } else { - prog = (bt_regprog_T *)rex.reg_match->regprog; - rex.reg_startp = rex.reg_match->startp; - rex.reg_endp = rex.reg_match->endp; - } - - /* Be paranoid... */ - if (prog == NULL || line == NULL) { - iemsg(_(e_null)); - goto theend; - } - - /* Check validity of program. */ - if (prog_magic_wrong()) - goto theend; - - // If the start column is past the maximum column: no need to try. - if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { - goto theend; - } - - // If pattern contains "\c" or "\C": overrule value of rex.reg_ic - if (prog->regflags & RF_ICASE) { - rex.reg_ic = true; - } else if (prog->regflags & RF_NOICASE) { - rex.reg_ic = false; - } - - // If pattern contains "\Z" overrule value of rex.reg_icombine - if (prog->regflags & RF_ICOMBINE) { - rex.reg_icombine = true; - } - - /* If there is a "must appear" string, look for it. */ - if (prog->regmust != NULL) { - int c = utf_ptr2char(prog->regmust); - s = line + col; - - // This is used very often, esp. for ":global". Use two versions of - // the loop to avoid overhead of conditions. - if (!rex.reg_ic) { - while ((s = vim_strchr(s, c)) != NULL) { - if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { - break; // Found it. - } - MB_PTR_ADV(s); - } - } else { - while ((s = cstrchr(s, c)) != NULL) { - if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { - break; // Found it. - } - MB_PTR_ADV(s); - } - } - if (s == NULL) { // Not present. - goto theend; - } - } - - rex.line = line; - rex.lnum = 0; - reg_toolong = false; - - /* Simplest case: Anchored match need be tried only once. */ - if (prog->reganch) { - int c = utf_ptr2char(rex.line + col); - if (prog->regstart == NUL - || prog->regstart == c - || (rex.reg_ic - && (utf_fold(prog->regstart) == utf_fold(c) - || (c < 255 && prog->regstart < 255 - && mb_tolower(prog->regstart) == mb_tolower(c))))) { - retval = regtry(prog, col, tm, timed_out); - } else { - retval = 0; - } - } else { - int tm_count = 0; - /* Messy cases: unanchored match. */ - while (!got_int) { - if (prog->regstart != NUL) { - // Skip until the char we know it must start with. - s = cstrchr(rex.line + col, prog->regstart); - if (s == NULL) { - retval = 0; - break; - } - col = (int)(s - rex.line); - } - - // Check for maximum column to try. - if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { - retval = 0; - break; - } - - retval = regtry(prog, col, tm, timed_out); - if (retval > 0) { - break; - } - - // if not currently on the first line, get it again - if (rex.lnum != 0) { - rex.lnum = 0; - rex.line = reg_getline((linenr_T)0); - } - if (rex.line[col] == NUL) { - break; - } - col += utfc_ptr2len(rex.line + col); - // Check for timeout once in a twenty times to avoid overhead. - if (tm != NULL && ++tm_count == 20) { - tm_count = 0; - if (profile_passed_limit(*tm)) { - if (timed_out != NULL) { - *timed_out = true; - } - break; - } - } - } - } - -theend: - /* Free "reg_tofree" when it's a bit big. - * Free regstack and backpos if they are bigger than their initial size. */ - if (reg_tofreelen > 400) { - XFREE_CLEAR(reg_tofree); - } - if (regstack.ga_maxlen > REGSTACK_INITIAL) - ga_clear(®stack); - if (backpos.ga_maxlen > BACKPOS_INITIAL) - ga_clear(&backpos); - - if (retval > 0) { - // Make sure the end is never before the start. Can happen when \zs - // and \ze are used. - if (REG_MULTI) { - const lpos_T *const start = &rex.reg_mmatch->startpos[0]; - const lpos_T *const end = &rex.reg_mmatch->endpos[0]; - - if (end->lnum < start->lnum - || (end->lnum == start->lnum && end->col < start->col)) { - rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; - } - } else { - if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { - rex.reg_match->endp[0] = rex.reg_match->startp[0]; - } - } - } - - return retval; -} - - -/* - * Create a new extmatch and mark it as referenced once. - */ -static reg_extmatch_T *make_extmatch(void) - FUNC_ATTR_NONNULL_RET -{ - reg_extmatch_T *em = xcalloc(1, sizeof(reg_extmatch_T)); - em->refcnt = 1; - return em; -} - -/* - * Add a reference to an extmatch. - */ -reg_extmatch_T *ref_extmatch(reg_extmatch_T *em) -{ - if (em != NULL) - em->refcnt++; - return em; -} - -/* - * Remove a reference to an extmatch. If there are no references left, free - * the info. - */ -void unref_extmatch(reg_extmatch_T *em) -{ - int i; - - if (em != NULL && --em->refcnt <= 0) { - for (i = 0; i < NSUBEXP; ++i) - xfree(em->matches[i]); - xfree(em); - } -} - -/// Try match of "prog" with at rex.line["col"]. -/// @returns 0 for failure, or number of lines contained in the match. -static long regtry(bt_regprog_T *prog, - colnr_T col, - proftime_T *tm, // timeout limit or NULL - int *timed_out) // flag set on timeout or NULL -{ - rex.input = rex.line + col; - rex.need_clear_subexpr = true; - // Clear the external match subpointers if necessaey. - rex.need_clear_zsubexpr = (prog->reghasz == REX_SET); - - if (regmatch(prog->program + 1, tm, timed_out) == 0) { - return 0; - } - - cleanup_subexpr(); - if (REG_MULTI) { - if (rex.reg_startpos[0].lnum < 0) { - rex.reg_startpos[0].lnum = 0; - rex.reg_startpos[0].col = col; - } - if (rex.reg_endpos[0].lnum < 0) { - rex.reg_endpos[0].lnum = rex.lnum; - rex.reg_endpos[0].col = (int)(rex.input - rex.line); - } else { - // Use line number of "\ze". - rex.lnum = rex.reg_endpos[0].lnum; - } - } else { - if (rex.reg_startp[0] == NULL) { - rex.reg_startp[0] = rex.line + col; - } - if (rex.reg_endp[0] == NULL) { - rex.reg_endp[0] = rex.input; - } - } - /* Package any found \z(...\) matches for export. Default is none. */ - unref_extmatch(re_extmatch_out); - re_extmatch_out = NULL; - - if (prog->reghasz == REX_SET) { - int i; - - cleanup_zsubexpr(); - re_extmatch_out = make_extmatch(); - for (i = 0; i < NSUBEXP; i++) { - if (REG_MULTI) { - /* Only accept single line matches. */ - if (reg_startzpos[i].lnum >= 0 - && reg_endzpos[i].lnum == reg_startzpos[i].lnum - && reg_endzpos[i].col >= reg_startzpos[i].col) { - re_extmatch_out->matches[i] = - vim_strnsave(reg_getline(reg_startzpos[i].lnum) - + reg_startzpos[i].col, - reg_endzpos[i].col - - reg_startzpos[i].col); - } - } else { - if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) - re_extmatch_out->matches[i] = - vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); - } - } - } - return 1 + rex.lnum; -} - - -// Get class of previous character. -static int reg_prev_class(void) -{ - if (rex.input > rex.line) { - return mb_get_class_tab( - rex.input - 1 - utf_head_off(rex.line, rex.input - 1), - rex.reg_buf->b_chartab); - } - return -1; -} - - -// Return true if the current rex.input position matches the Visual area. -static bool reg_match_visual(void) -{ - pos_T top, bot; - linenr_T lnum; - colnr_T col; - win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win; - int mode; - colnr_T start, end; - colnr_T start2, end2; - colnr_T curswant; - - // Check if the buffer is the current buffer. - if (rex.reg_buf != curbuf || VIsual.lnum == 0) { - return false; - } - - if (VIsual_active) { - if (lt(VIsual, wp->w_cursor)) { - top = VIsual; - bot = wp->w_cursor; - } else { - top = wp->w_cursor; - bot = VIsual; - } - mode = VIsual_mode; - curswant = wp->w_curswant; - } else { - if (lt(curbuf->b_visual.vi_start, curbuf->b_visual.vi_end)) { - top = curbuf->b_visual.vi_start; - bot = curbuf->b_visual.vi_end; - } else { - top = curbuf->b_visual.vi_end; - bot = curbuf->b_visual.vi_start; - } - mode = curbuf->b_visual.vi_mode; - curswant = curbuf->b_visual.vi_curswant; - } - lnum = rex.lnum + rex.reg_firstlnum; - if (lnum < top.lnum || lnum > bot.lnum) { - return false; - } - - if (mode == 'v') { - col = (colnr_T)(rex.input - rex.line); - if ((lnum == top.lnum && col < top.col) - || (lnum == bot.lnum && col >= bot.col + (*p_sel != 'e'))) { - return false; - } - } else if (mode == Ctrl_V) { - getvvcol(wp, &top, &start, NULL, &end); - getvvcol(wp, &bot, &start2, NULL, &end2); - if (start2 < start) - start = start2; - if (end2 > end) - end = end2; - if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL) { - end = MAXCOL; - } - unsigned int cols_u = win_linetabsize(wp, rex.line, - (colnr_T)(rex.input - rex.line)); - assert(cols_u <= MAXCOL); - colnr_T cols = (colnr_T)cols_u; - if (cols < start || cols > end - (*p_sel == 'e')) { - return false; - } - } - return true; -} - -#define ADVANCE_REGINPUT() MB_PTR_ADV(rex.input) - -/* - * The arguments from BRACE_LIMITS are stored here. They are actually local - * to regmatch(), but they are here to reduce the amount of stack space used - * (it can be called recursively many times). - */ -static long bl_minval; -static long bl_maxval; - -/// Main matching routine -/// -/// Conceptually the strategy is simple: Check to see whether the current node -/// matches, push an item onto the regstack and loop to see whether the rest -/// matches, and then act accordingly. In practice we make some effort to -/// avoid using the regstack, in particular by going through "ordinary" nodes -/// (that don't need to know whether the rest of the match failed) by a nested -/// loop. -/// -/// Returns true when there is a match. Leaves rex.input and rex.lnum -/// just after the last matched character. -/// Returns false when there is no match. Leaves rex.input and rex.lnum in an -/// undefined state! -static bool regmatch( - char_u *scan, // Current node. - proftime_T *tm, // timeout limit or NULL - int *timed_out // flag set on timeout or NULL -) -{ - char_u *next; /* Next node. */ - int op; - int c; - regitem_T *rp; - int no; - int status; // one of the RA_ values: - int tm_count = 0; -#define RA_FAIL 1 // something failed, abort -#define RA_CONT 2 // continue in inner loop -#define RA_BREAK 3 // break inner loop -#define RA_MATCH 4 // successful match -#define RA_NOMATCH 5 // didn't match - - // Make "regstack" and "backpos" empty. They are allocated and freed in - // bt_regexec_both() to reduce malloc()/free() calls. - regstack.ga_len = 0; - backpos.ga_len = 0; - - /* - * Repeat until "regstack" is empty. - */ - for (;; ) { - /* Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q". - * Allow interrupting them with CTRL-C. */ - fast_breakcheck(); - -#ifdef REGEXP_DEBUG - if (scan != NULL && regnarrate) { - mch_errmsg((char *)regprop(scan)); - mch_errmsg("(\n"); - } -#endif - - /* - * Repeat for items that can be matched sequentially, without using the - * regstack. - */ - for (;; ) { - if (got_int || scan == NULL) { - status = RA_FAIL; - break; - } - // Check for timeout once in a 100 times to avoid overhead. - if (tm != NULL && ++tm_count == 100) { - tm_count = 0; - if (profile_passed_limit(*tm)) { - if (timed_out != NULL) { - *timed_out = true; - } - status = RA_FAIL; - break; - } - } - status = RA_CONT; - -#ifdef REGEXP_DEBUG - if (regnarrate) { - mch_errmsg((char *)regprop(scan)); - mch_errmsg("...\n"); - if (re_extmatch_in != NULL) { - int i; - - mch_errmsg(_("External submatches:\n")); - for (i = 0; i < NSUBEXP; i++) { - mch_errmsg(" \""); - if (re_extmatch_in->matches[i] != NULL) - mch_errmsg((char *)re_extmatch_in->matches[i]); - mch_errmsg("\"\n"); - } - } - } -#endif - next = regnext(scan); - - op = OP(scan); - // Check for character class with NL added. - if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI - && *rex.input == NUL && rex.lnum <= rex.reg_maxline) { - reg_nextline(); - } else if (rex.reg_line_lbr && WITH_NL(op) && *rex.input == '\n') { - ADVANCE_REGINPUT(); - } else { - if (WITH_NL(op)) { - op -= ADD_NL; - } - c = utf_ptr2char(rex.input); - switch (op) { - case BOL: - if (rex.input != rex.line) { - status = RA_NOMATCH; - } - break; - - case EOL: - if (c != NUL) { - status = RA_NOMATCH; - } - break; - - case RE_BOF: - // We're not at the beginning of the file when below the first - // line where we started, not at the start of the line or we - // didn't start at the first line of the buffer. - if (rex.lnum != 0 || rex.input != rex.line - || (REG_MULTI && rex.reg_firstlnum > 1)) { - status = RA_NOMATCH; - } - break; - - case RE_EOF: - if (rex.lnum != rex.reg_maxline || c != NUL) { - status = RA_NOMATCH; - } - break; - - case CURSOR: - // Check if the buffer is in a window and compare the - // rex.reg_win->w_cursor position to the match position. - if (rex.reg_win == NULL - || (rex.lnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum) - || ((colnr_T)(rex.input - rex.line) != - rex.reg_win->w_cursor.col)) { - status = RA_NOMATCH; - } - break; - - case RE_MARK: - /* Compare the mark position to the match position. */ - { - int mark = OPERAND(scan)[0]; - int cmp = OPERAND(scan)[1]; - pos_T *pos; - - pos = getmark_buf(rex.reg_buf, mark, false); - if (pos == NULL // mark doesn't exist - || pos->lnum <= 0) { // mark isn't set in reg_buf - status = RA_NOMATCH; - } else { - const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum - && pos->col == MAXCOL - ? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum)) - : pos->col; - - if (pos->lnum == rex.lnum + rex.reg_firstlnum - ? (pos_col == (colnr_T)(rex.input - rex.line) - ? (cmp == '<' || cmp == '>') - : (pos_col < (colnr_T)(rex.input - rex.line) - ? cmp != '>' - : cmp != '<')) - : (pos->lnum < rex.lnum + rex.reg_firstlnum - ? cmp != '>' - : cmp != '<')) { - status = RA_NOMATCH; - } - } - } - break; - - case RE_VISUAL: - if (!reg_match_visual()) - status = RA_NOMATCH; - break; - - case RE_LNUM: - assert(rex.lnum + rex.reg_firstlnum >= 0 - && (uintmax_t)(rex.lnum + rex.reg_firstlnum) <= UINT32_MAX); - if (!REG_MULTI - || !re_num_cmp((uint32_t)(rex.lnum + rex.reg_firstlnum), scan)) { - status = RA_NOMATCH; - } - break; - - case RE_COL: - assert(rex.input - rex.line + 1 >= 0 - && (uintmax_t)(rex.input - rex.line + 1) <= UINT32_MAX); - if (!re_num_cmp((uint32_t)(rex.input - rex.line + 1), scan)) { - status = RA_NOMATCH; - } - break; - - case RE_VCOL: - if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL - ? curwin : rex.reg_win, - rex.line, - (colnr_T)(rex.input - rex.line)) + 1, - scan)) { - status = RA_NOMATCH; - } - break; - - case BOW: // \b_chartab); - if (this_class <= 1) { - status = RA_NOMATCH; // Not on a word at all. - } else if (reg_prev_class() == this_class) { - status = RA_NOMATCH; // Previous char is in same word. - } - } - break; - - case EOW: // word\>; rex.input points after d - if (rex.input == rex.line) { // Can't match at start of line - status = RA_NOMATCH; - } else { - int this_class, prev_class; - - // Get class of current and previous char (if it exists). - this_class = mb_get_class_tab(rex.input, rex.reg_buf->b_chartab); - prev_class = reg_prev_class(); - if (this_class == prev_class - || prev_class == 0 || prev_class == 1) { - status = RA_NOMATCH; - } - } - break; // Matched with EOW - - case ANY: - // ANY does not match new lines. - if (c == NUL) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case IDENT: - if (!vim_isIDc(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case SIDENT: - if (ascii_isdigit(*rex.input) || !vim_isIDc(c)) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case KWORD: - if (!vim_iswordp_buf(rex.input, rex.reg_buf)) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case SKWORD: - if (ascii_isdigit(*rex.input) - || !vim_iswordp_buf(rex.input, rex.reg_buf)) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case FNAME: - if (!vim_isfilec(c)) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case SFNAME: - if (ascii_isdigit(*rex.input) || !vim_isfilec(c)) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case PRINT: - if (!vim_isprintc(utf_ptr2char(rex.input))) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case SPRINT: - if (ascii_isdigit(*rex.input) || !vim_isprintc(utf_ptr2char(rex.input))) { - status = RA_NOMATCH; - } else { - ADVANCE_REGINPUT(); - } - break; - - case WHITE: - if (!ascii_iswhite(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NWHITE: - if (c == NUL || ascii_iswhite(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case DIGIT: - if (!ri_digit(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NDIGIT: - if (c == NUL || ri_digit(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case HEX: - if (!ri_hex(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NHEX: - if (c == NUL || ri_hex(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case OCTAL: - if (!ri_octal(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NOCTAL: - if (c == NUL || ri_octal(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case WORD: - if (!ri_word(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NWORD: - if (c == NUL || ri_word(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case HEAD: - if (!ri_head(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NHEAD: - if (c == NUL || ri_head(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case ALPHA: - if (!ri_alpha(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NALPHA: - if (c == NUL || ri_alpha(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case LOWER: - if (!ri_lower(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NLOWER: - if (c == NUL || ri_lower(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case UPPER: - if (!ri_upper(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case NUPPER: - if (c == NUL || ri_upper(c)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case EXACTLY: - { - int len; - char_u *opnd; - - opnd = OPERAND(scan); - // Inline the first byte, for speed. - if (*opnd != *rex.input - && (!rex.reg_ic)) { - status = RA_NOMATCH; - } else if (*opnd == NUL) { - // match empty string always works; happens when "~" is - // empty. - } else { - if (opnd[1] == NUL && !rex.reg_ic) { - len = 1; // matched a single byte above - } else { - // Need to match first byte again for multi-byte. - len = (int)STRLEN(opnd); - if (cstrncmp(opnd, rex.input, &len) != 0) { - status = RA_NOMATCH; - } - } - // Check for following composing character, unless %C - // follows (skips over all composing chars). - if (status != RA_NOMATCH - && utf_composinglike(rex.input, rex.input + len) - && !rex.reg_icombine - && OP(next) != RE_COMPOSING) { - // raaron: This code makes a composing character get - // ignored, which is the correct behavior (sometimes) - // for voweled Hebrew texts. - status = RA_NOMATCH; - } - if (status != RA_NOMATCH) { - rex.input += len; - } - } - } - break; - - case ANYOF: - case ANYBUT: - if (c == NUL) - status = RA_NOMATCH; - else if ((cstrchr(OPERAND(scan), c) == NULL) == (op == ANYOF)) - status = RA_NOMATCH; - else - ADVANCE_REGINPUT(); - break; - - case MULTIBYTECODE: - { - int i, len; - - const char_u *opnd = OPERAND(scan); - // Safety check (just in case 'encoding' was changed since - // compiling the program). - if ((len = utfc_ptr2len(opnd)) < 2) { - status = RA_NOMATCH; - break; - } - const int opndc = utf_ptr2char(opnd); - if (utf_iscomposing(opndc)) { - // When only a composing char is given match at any - // position where that composing char appears. - status = RA_NOMATCH; - for (i = 0; rex.input[i] != NUL; - i += utf_ptr2len(rex.input + i)) { - const int inpc = utf_ptr2char(rex.input + i); - if (!utf_iscomposing(inpc)) { - if (i > 0) { - break; - } - } else if (opndc == inpc) { - // Include all following composing chars. - len = i + utfc_ptr2len(rex.input + i); - status = RA_MATCH; - break; - } - } - } else { - for (i = 0; i < len; i++) { - if (opnd[i] != rex.input[i]) { - status = RA_NOMATCH; - break; - } - } - } - rex.input += len; - } - break; - - case RE_COMPOSING: - { - // Skip composing characters. - while (utf_iscomposing(utf_ptr2char(rex.input))) { - MB_CPTR_ADV(rex.input); - } - } - break; - - case NOTHING: - break; - - case BACK: - { - int i; - - /* - * When we run into BACK we need to check if we don't keep - * looping without matching any input. The second and later - * times a BACK is encountered it fails if the input is still - * at the same position as the previous time. - * The positions are stored in "backpos" and found by the - * current value of "scan", the position in the RE program. - */ - backpos_T *bp = (backpos_T *)backpos.ga_data; - for (i = 0; i < backpos.ga_len; ++i) - if (bp[i].bp_scan == scan) - break; - if (i == backpos.ga_len) { - backpos_T *p = GA_APPEND_VIA_PTR(backpos_T, &backpos); - p->bp_scan = scan; - } else if (reg_save_equal(&bp[i].bp_pos)) - /* Still at same position as last time, fail. */ - status = RA_NOMATCH; - - assert(status != RA_FAIL); - if (status != RA_NOMATCH) { - reg_save(&bp[i].bp_pos, &backpos); - } - } - break; - - case MOPEN + 0: /* Match start: \zs */ - case MOPEN + 1: /* \( */ - case MOPEN + 2: - case MOPEN + 3: - case MOPEN + 4: - case MOPEN + 5: - case MOPEN + 6: - case MOPEN + 7: - case MOPEN + 8: - case MOPEN + 9: - { - no = op - MOPEN; - cleanup_subexpr(); - rp = regstack_push(RS_MOPEN, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = no; - save_se(&rp->rs_un.sesave, &rex.reg_startpos[no], - &rex.reg_startp[no]); - // We simply continue and handle the result when done. - } - } - break; - - case NOPEN: /* \%( */ - case NCLOSE: /* \) after \%( */ - if (regstack_push(RS_NOPEN, scan) == NULL) - status = RA_FAIL; - /* We simply continue and handle the result when done. */ - break; - - case ZOPEN + 1: - case ZOPEN + 2: - case ZOPEN + 3: - case ZOPEN + 4: - case ZOPEN + 5: - case ZOPEN + 6: - case ZOPEN + 7: - case ZOPEN + 8: - case ZOPEN + 9: - { - no = op - ZOPEN; - cleanup_zsubexpr(); - rp = regstack_push(RS_ZOPEN, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = no; - save_se(&rp->rs_un.sesave, ®_startzpos[no], - ®_startzp[no]); - /* We simply continue and handle the result when done. */ - } - } - break; - - case MCLOSE + 0: /* Match end: \ze */ - case MCLOSE + 1: /* \) */ - case MCLOSE + 2: - case MCLOSE + 3: - case MCLOSE + 4: - case MCLOSE + 5: - case MCLOSE + 6: - case MCLOSE + 7: - case MCLOSE + 8: - case MCLOSE + 9: - { - no = op - MCLOSE; - cleanup_subexpr(); - rp = regstack_push(RS_MCLOSE, scan); - if (rp == NULL) { - status = RA_FAIL; - } else { - rp->rs_no = no; - save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]); - // We simply continue and handle the result when done. - } - } - break; - - case ZCLOSE + 1: /* \) after \z( */ - case ZCLOSE + 2: - case ZCLOSE + 3: - case ZCLOSE + 4: - case ZCLOSE + 5: - case ZCLOSE + 6: - case ZCLOSE + 7: - case ZCLOSE + 8: - case ZCLOSE + 9: - { - no = op - ZCLOSE; - cleanup_zsubexpr(); - rp = regstack_push(RS_ZCLOSE, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = no; - save_se(&rp->rs_un.sesave, ®_endzpos[no], - ®_endzp[no]); - /* We simply continue and handle the result when done. */ - } - } - break; - - case BACKREF + 1: - case BACKREF + 2: - case BACKREF + 3: - case BACKREF + 4: - case BACKREF + 5: - case BACKREF + 6: - case BACKREF + 7: - case BACKREF + 8: - case BACKREF + 9: - { - int len; - - no = op - BACKREF; - cleanup_subexpr(); - if (!REG_MULTI) { // Single-line regexp - if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) { - // Backref was not set: Match an empty string. - len = 0; - } else { - // Compare current input with back-ref in the same line. - len = (int)(rex.reg_endp[no] - rex.reg_startp[no]); - if (cstrncmp(rex.reg_startp[no], rex.input, &len) != 0) { - status = RA_NOMATCH; - } - } - } else { // Multi-line regexp - if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) { - // Backref was not set: Match an empty string. - len = 0; - } else { - if (rex.reg_startpos[no].lnum == rex.lnum - && rex.reg_endpos[no].lnum == rex.lnum) { - // Compare back-ref within the current line. - len = rex.reg_endpos[no].col - rex.reg_startpos[no].col; - if (cstrncmp(rex.line + rex.reg_startpos[no].col, - rex.input, &len) != 0) { - status = RA_NOMATCH; - } - } else { - // Messy situation: Need to compare between two lines. - int r = match_with_backref(rex.reg_startpos[no].lnum, - rex.reg_startpos[no].col, - rex.reg_endpos[no].lnum, - rex.reg_endpos[no].col, - &len); - if (r != RA_MATCH) { - status = r; - } - } - } - } - - // Matched the backref, skip over it. - rex.input += len; - } - break; - - case ZREF + 1: - case ZREF + 2: - case ZREF + 3: - case ZREF + 4: - case ZREF + 5: - case ZREF + 6: - case ZREF + 7: - case ZREF + 8: - case ZREF + 9: - { - cleanup_zsubexpr(); - no = op - ZREF; - if (re_extmatch_in != NULL - && re_extmatch_in->matches[no] != NULL) { - int len = (int)STRLEN(re_extmatch_in->matches[no]); - if (cstrncmp(re_extmatch_in->matches[no], rex.input, &len) != 0) { - status = RA_NOMATCH; - } else { - rex.input += len; - } - } else { - // Backref was not set: Match an empty string. - } - } - break; - - case BRANCH: - { - if (OP(next) != BRANCH) /* No choice. */ - next = OPERAND(scan); /* Avoid recursion. */ - else { - rp = regstack_push(RS_BRANCH, scan); - if (rp == NULL) - status = RA_FAIL; - else - status = RA_BREAK; /* rest is below */ - } - } - break; - - case BRACE_LIMITS: - { - if (OP(next) == BRACE_SIMPLE) { - bl_minval = OPERAND_MIN(scan); - bl_maxval = OPERAND_MAX(scan); - } else if (OP(next) >= BRACE_COMPLEX - && OP(next) < BRACE_COMPLEX + 10) { - no = OP(next) - BRACE_COMPLEX; - brace_min[no] = OPERAND_MIN(scan); - brace_max[no] = OPERAND_MAX(scan); - brace_count[no] = 0; - } else { - internal_error("BRACE_LIMITS"); - status = RA_FAIL; - } - } - break; - - case BRACE_COMPLEX + 0: - case BRACE_COMPLEX + 1: - case BRACE_COMPLEX + 2: - case BRACE_COMPLEX + 3: - case BRACE_COMPLEX + 4: - case BRACE_COMPLEX + 5: - case BRACE_COMPLEX + 6: - case BRACE_COMPLEX + 7: - case BRACE_COMPLEX + 8: - case BRACE_COMPLEX + 9: - { - no = op - BRACE_COMPLEX; - ++brace_count[no]; - - /* If not matched enough times yet, try one more */ - if (brace_count[no] <= (brace_min[no] <= brace_max[no] - ? brace_min[no] : brace_max[no])) { - rp = regstack_push(RS_BRCPLX_MORE, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = no; - reg_save(&rp->rs_un.regsave, &backpos); - next = OPERAND(scan); - /* We continue and handle the result when done. */ - } - break; - } - - /* If matched enough times, may try matching some more */ - if (brace_min[no] <= brace_max[no]) { - /* Range is the normal way around, use longest match */ - if (brace_count[no] <= brace_max[no]) { - rp = regstack_push(RS_BRCPLX_LONG, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = no; - reg_save(&rp->rs_un.regsave, &backpos); - next = OPERAND(scan); - /* We continue and handle the result when done. */ - } - } - } else { - /* Range is backwards, use shortest match first */ - if (brace_count[no] <= brace_min[no]) { - rp = regstack_push(RS_BRCPLX_SHORT, scan); - if (rp == NULL) - status = RA_FAIL; - else { - reg_save(&rp->rs_un.regsave, &backpos); - /* We continue and handle the result when done. */ - } - } - } - } - break; - - case BRACE_SIMPLE: - case STAR: - case PLUS: - { - regstar_T rst; - - /* - * Lookahead to avoid useless match attempts when we know - * what character comes next. - */ - if (OP(next) == EXACTLY) { - rst.nextb = *OPERAND(next); - if (rex.reg_ic) { - if (mb_isupper(rst.nextb)) { - rst.nextb_ic = mb_tolower(rst.nextb); - } else { - rst.nextb_ic = mb_toupper(rst.nextb); - } - } else { - rst.nextb_ic = rst.nextb; - } - } else { - rst.nextb = NUL; - rst.nextb_ic = NUL; - } - if (op != BRACE_SIMPLE) { - rst.minval = (op == STAR) ? 0 : 1; - rst.maxval = MAX_LIMIT; - } else { - rst.minval = bl_minval; - rst.maxval = bl_maxval; - } - - /* - * When maxval > minval, try matching as much as possible, up - * to maxval. When maxval < minval, try matching at least the - * minimal number (since the range is backwards, that's also - * maxval!). - */ - rst.count = regrepeat(OPERAND(scan), rst.maxval); - if (got_int) { - status = RA_FAIL; - break; - } - if (rst.minval <= rst.maxval - ? rst.count >= rst.minval : rst.count >= rst.maxval) { - /* It could match. Prepare for trying to match what - * follows. The code is below. Parameters are stored in - * a regstar_T on the regstack. */ - if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { - emsg(_(e_maxmempat)); - status = RA_FAIL; - } else { - ga_grow(®stack, sizeof(regstar_T)); - regstack.ga_len += sizeof(regstar_T); - rp = regstack_push(rst.minval <= rst.maxval - ? RS_STAR_LONG : RS_STAR_SHORT, scan); - if (rp == NULL) - status = RA_FAIL; - else { - *(((regstar_T *)rp) - 1) = rst; - status = RA_BREAK; /* skip the restore bits */ - } - } - } else - status = RA_NOMATCH; - - } - break; - - case NOMATCH: - case MATCH: - case SUBPAT: - rp = regstack_push(RS_NOMATCH, scan); - if (rp == NULL) - status = RA_FAIL; - else { - rp->rs_no = op; - reg_save(&rp->rs_un.regsave, &backpos); - next = OPERAND(scan); - /* We continue and handle the result when done. */ - } - break; - - case BEHIND: - case NOBEHIND: - /* Need a bit of room to store extra positions. */ - if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { - emsg(_(e_maxmempat)); - status = RA_FAIL; - } else { - ga_grow(®stack, sizeof(regbehind_T)); - regstack.ga_len += sizeof(regbehind_T); - rp = regstack_push(RS_BEHIND1, scan); - if (rp == NULL) - status = RA_FAIL; - else { - /* Need to save the subexpr to be able to restore them - * when there is a match but we don't use it. */ - save_subexpr(((regbehind_T *)rp) - 1); - - rp->rs_no = op; - reg_save(&rp->rs_un.regsave, &backpos); - /* First try if what follows matches. If it does then we - * check the behind match by looping. */ - } - } - break; - - case BHPOS: - if (REG_MULTI) { - if (behind_pos.rs_u.pos.col != (colnr_T)(rex.input - rex.line) - || behind_pos.rs_u.pos.lnum != rex.lnum) { - status = RA_NOMATCH; - } - } else if (behind_pos.rs_u.ptr != rex.input) { - status = RA_NOMATCH; - } - break; - - case NEWL: - if ((c != NUL || !REG_MULTI || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) { - status = RA_NOMATCH; - } else if (rex.reg_line_lbr) { - ADVANCE_REGINPUT(); - } else { - reg_nextline(); - } - break; - - case END: - status = RA_MATCH; /* Success! */ - break; - - default: - iemsg(_(e_re_corr)); -#ifdef REGEXP_DEBUG - printf("Illegal op code %d\n", op); -#endif - status = RA_FAIL; - break; - } - } - - /* If we can't continue sequentially, break the inner loop. */ - if (status != RA_CONT) - break; - - /* Continue in inner loop, advance to next item. */ - scan = next; - - } /* end of inner loop */ - - /* - * If there is something on the regstack execute the code for the state. - * If the state is popped then loop and use the older state. - */ - while (!GA_EMPTY(®stack) && status != RA_FAIL) { - rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; - switch (rp->rs_state) { - case RS_NOPEN: - /* Result is passed on as-is, simply pop the state. */ - regstack_pop(&scan); - break; - - case RS_MOPEN: - // Pop the state. Restore pointers when there is no match. - if (status == RA_NOMATCH) { - restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no], - &rex.reg_startp[rp->rs_no]); - } - regstack_pop(&scan); - break; - - case RS_ZOPEN: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) - restore_se(&rp->rs_un.sesave, ®_startzpos[rp->rs_no], - ®_startzp[rp->rs_no]); - regstack_pop(&scan); - break; - - case RS_MCLOSE: - // Pop the state. Restore pointers when there is no match. - if (status == RA_NOMATCH) { - restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no], - &rex.reg_endp[rp->rs_no]); - } - regstack_pop(&scan); - break; - - case RS_ZCLOSE: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) - restore_se(&rp->rs_un.sesave, ®_endzpos[rp->rs_no], - ®_endzp[rp->rs_no]); - regstack_pop(&scan); - break; - - case RS_BRANCH: - if (status == RA_MATCH) - /* this branch matched, use it */ - regstack_pop(&scan); - else { - if (status != RA_BREAK) { - /* After a non-matching branch: try next one. */ - reg_restore(&rp->rs_un.regsave, &backpos); - scan = rp->rs_scan; - } - if (scan == NULL || OP(scan) != BRANCH) { - /* no more branches, didn't find a match */ - status = RA_NOMATCH; - regstack_pop(&scan); - } else { - /* Prepare to try a branch. */ - rp->rs_scan = regnext(scan); - reg_save(&rp->rs_un.regsave, &backpos); - scan = OPERAND(scan); - } - } - break; - - case RS_BRCPLX_MORE: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) { - reg_restore(&rp->rs_un.regsave, &backpos); - --brace_count[rp->rs_no]; /* decrement match count */ - } - regstack_pop(&scan); - break; - - case RS_BRCPLX_LONG: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) { - /* There was no match, but we did find enough matches. */ - reg_restore(&rp->rs_un.regsave, &backpos); - --brace_count[rp->rs_no]; - /* continue with the items after "\{}" */ - status = RA_CONT; - } - regstack_pop(&scan); - if (status == RA_CONT) - scan = regnext(scan); - break; - - case RS_BRCPLX_SHORT: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) - /* There was no match, try to match one more item. */ - reg_restore(&rp->rs_un.regsave, &backpos); - regstack_pop(&scan); - if (status == RA_NOMATCH) { - scan = OPERAND(scan); - status = RA_CONT; - } - break; - - case RS_NOMATCH: - /* Pop the state. If the operand matches for NOMATCH or - * doesn't match for MATCH/SUBPAT, we fail. Otherwise backup, - * except for SUBPAT, and continue with the next item. */ - if (status == (rp->rs_no == NOMATCH ? RA_MATCH : RA_NOMATCH)) - status = RA_NOMATCH; - else { - status = RA_CONT; - if (rp->rs_no != SUBPAT) /* zero-width */ - reg_restore(&rp->rs_un.regsave, &backpos); - } - regstack_pop(&scan); - if (status == RA_CONT) - scan = regnext(scan); - break; - - case RS_BEHIND1: - if (status == RA_NOMATCH) { - regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); - } else { - /* The stuff after BEHIND/NOBEHIND matches. Now try if - * the behind part does (not) match before the current - * position in the input. This must be done at every - * position in the input and checking if the match ends at - * the current position. */ - - /* save the position after the found match for next */ - reg_save(&(((regbehind_T *)rp) - 1)->save_after, &backpos); - - /* Start looking for a match with operand at the current - * position. Go back one character until we find the - * result, hitting the start of the line or the previous - * line (for multi-line matching). - * Set behind_pos to where the match should end, BHPOS - * will match it. Save the current value. */ - (((regbehind_T *)rp) - 1)->save_behind = behind_pos; - behind_pos = rp->rs_un.regsave; - - rp->rs_state = RS_BEHIND2; - - reg_restore(&rp->rs_un.regsave, &backpos); - scan = OPERAND(rp->rs_scan) + 4; - } - break; +// Structure used to store the execution state of the regex engine. +// Which ones are set depends on whether a single-line or multi-line match is +// done: +// single-line multi-line +// reg_match ®match_T NULL +// reg_mmatch NULL ®mmatch_T +// reg_startp reg_match->startp +// reg_endp reg_match->endp +// reg_startpos reg_mmatch->startpos +// reg_endpos reg_mmatch->endpos +// reg_win NULL window in which to search +// reg_buf curbuf buffer in which to search +// reg_firstlnum first line in which to search +// reg_maxline 0 last line nr +// reg_line_lbr false or true false +typedef struct { + regmatch_T *reg_match; + regmmatch_T *reg_mmatch; + char_u **reg_startp; + char_u **reg_endp; + lpos_T *reg_startpos; + lpos_T *reg_endpos; + win_T *reg_win; + buf_T *reg_buf; + linenr_T reg_firstlnum; + linenr_T reg_maxline; + bool reg_line_lbr; // "\n" in string is line break - case RS_BEHIND2: - /* - * Looping for BEHIND / NOBEHIND match. - */ - if (status == RA_MATCH && reg_save_equal(&behind_pos)) { - /* found a match that ends where "next" started */ - behind_pos = (((regbehind_T *)rp) - 1)->save_behind; - if (rp->rs_no == BEHIND) - reg_restore(&(((regbehind_T *)rp) - 1)->save_after, - &backpos); - else { - /* But we didn't want a match. Need to restore the - * subexpr, because what follows matched, so they have - * been set. */ - status = RA_NOMATCH; - restore_subexpr(((regbehind_T *)rp) - 1); - } - regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); - } else { - long limit; - - /* No match or a match that doesn't end where we want it: Go - * back one character. May go to previous line once. */ - no = OK; - limit = OPERAND_MIN(rp->rs_scan); - if (REG_MULTI) { - if (limit > 0 - && ((rp->rs_un.regsave.rs_u.pos.lnum - < behind_pos.rs_u.pos.lnum - ? (colnr_T)STRLEN(rex.line) - : behind_pos.rs_u.pos.col) - - rp->rs_un.regsave.rs_u.pos.col >= limit)) - no = FAIL; - else if (rp->rs_un.regsave.rs_u.pos.col == 0) { - if (rp->rs_un.regsave.rs_u.pos.lnum - < behind_pos.rs_u.pos.lnum - || reg_getline( - --rp->rs_un.regsave.rs_u.pos.lnum) - == NULL) - no = FAIL; - else { - reg_restore(&rp->rs_un.regsave, &backpos); - rp->rs_un.regsave.rs_u.pos.col = - (colnr_T)STRLEN(rex.line); - } - } else { - const char_u *const line = - reg_getline(rp->rs_un.regsave.rs_u.pos.lnum); + // The current match-position is remembered with these variables: + linenr_T lnum; ///< line number, relative to first line + char_u *line; ///< start of current line + char_u *input; ///< current input, points into "line" - rp->rs_un.regsave.rs_u.pos.col -= - utf_head_off(line, - line + rp->rs_un.regsave.rs_u.pos.col - 1) - + 1; - } - } else { - if (rp->rs_un.regsave.rs_u.ptr == rex.line) { - no = FAIL; - } else { - MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr); - if (limit > 0 - && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) { - no = FAIL; - } - } - } - if (no == OK) { - /* Advanced, prepare for finding match again. */ - reg_restore(&rp->rs_un.regsave, &backpos); - scan = OPERAND(rp->rs_scan) + 4; - if (status == RA_MATCH) { - /* We did match, so subexpr may have been changed, - * need to restore them for the next try. */ - status = RA_NOMATCH; - restore_subexpr(((regbehind_T *)rp) - 1); - } - } else { - /* Can't advance. For NOBEHIND that's a match. */ - behind_pos = (((regbehind_T *)rp) - 1)->save_behind; - if (rp->rs_no == NOBEHIND) { - reg_restore(&(((regbehind_T *)rp) - 1)->save_after, - &backpos); - status = RA_MATCH; - } else { - /* We do want a proper match. Need to restore the - * subexpr if we had a match, because they may have - * been set. */ - if (status == RA_MATCH) { - status = RA_NOMATCH; - restore_subexpr(((regbehind_T *)rp) - 1); - } - } - regstack_pop(&scan); - regstack.ga_len -= sizeof(regbehind_T); - } - } - break; + int need_clear_subexpr; ///< subexpressions still need to be cleared + int need_clear_zsubexpr; ///< extmatch subexpressions still need to be + ///< cleared - case RS_STAR_LONG: - case RS_STAR_SHORT: - { - regstar_T *rst = ((regstar_T *)rp) - 1; - if (status == RA_MATCH) { - regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); - break; - } + // Internal copy of 'ignorecase'. It is set at each call to vim_regexec(). + // Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern + // contains '\c' or '\C' the value is overruled. + bool reg_ic; - /* Tried once already, restore input pointers. */ - if (status != RA_BREAK) - reg_restore(&rp->rs_un.regsave, &backpos); - - /* Repeat until we found a position where it could match. */ - for (;; ) { - if (status != RA_BREAK) { - /* Tried first position already, advance. */ - if (rp->rs_state == RS_STAR_LONG) { - /* Trying for longest match, but couldn't or - * didn't match -- back up one char. */ - if (--rst->count < rst->minval) - break; - if (rex.input == rex.line) { - // backup to last char of previous line - rex.lnum--; - rex.line = reg_getline(rex.lnum); - // Just in case regrepeat() didn't count right. - if (rex.line == NULL) { - break; - } - rex.input = rex.line + STRLEN(rex.line); - fast_breakcheck(); - } else { - MB_PTR_BACK(rex.line, rex.input); - } - } else { - /* Range is backwards, use shortest match first. - * Careful: maxval and minval are exchanged! - * Couldn't or didn't match: try advancing one - * char. */ - if (rst->count == rst->minval - || regrepeat(OPERAND(rp->rs_scan), 1L) == 0) - break; - ++rst->count; - } - if (got_int) - break; - } else - status = RA_NOMATCH; - - // If it could match, try it. - if (rst->nextb == NUL || *rex.input == rst->nextb - || *rex.input == rst->nextb_ic) { - reg_save(&rp->rs_un.regsave, &backpos); - scan = regnext(rp->rs_scan); - status = RA_CONT; - break; - } - } - if (status != RA_CONT) { - /* Failed. */ - regstack_pop(&scan); - regstack.ga_len -= sizeof(regstar_T); - status = RA_NOMATCH; - } - } - break; - } + // Similar to "reg_ic", but only for 'combining' characters. Set with \Z + // flag in the regexp. Defaults to false, always. + bool reg_icombine; - /* If we want to continue the inner loop or didn't pop a state - * continue matching loop */ - if (status == RA_CONT || rp == (regitem_T *) - ((char *)regstack.ga_data + regstack.ga_len) - 1) - break; - } + // Copy of "rmm_maxcol": maximum column to search for a match. Zero when + // there is no maximum. + colnr_T reg_maxcol; - /* May need to continue with the inner loop, starting at "scan". */ - if (status == RA_CONT) - continue; + // State for the NFA engine regexec. + int nfa_has_zend; ///< NFA regexp \ze operator encountered. + int nfa_has_backref; ///< NFA regexp \1 .. \9 encountered. + int nfa_nsubexpr; ///< Number of sub expressions actually being used + ///< during execution. 1 if only the whole match + ///< (subexpr 0) is used. + // listid is global, so that it increases on recursive calls to + // nfa_regmatch(), which means we don't have to clear the lastlist field of + // all the states. + int nfa_listid; + int nfa_alt_listid; - /* - * If the regstack is empty or something failed we are done. - */ - if (GA_EMPTY(®stack) || status == RA_FAIL) { - if (scan == NULL) { - /* - * We get here only if there's trouble -- normally "case END" is - * the terminating point. - */ - iemsg(_(e_re_corr)); -#ifdef REGEXP_DEBUG - printf("Premature EOL\n"); -#endif - } - return status == RA_MATCH; - } + int nfa_has_zsubexpr; ///< NFA regexp has \z( ), set zsubexpr. +} regexec_T; - } /* End of loop until the regstack is empty. */ +static regexec_T rex; +static bool rex_in_use = false; - /* NOTREACHED */ +// Return true if character 'c' is included in 'iskeyword' option for +// "reg_buf" buffer. +static bool reg_iswordc(int c) +{ + return vim_iswordc_buf(c, rex.reg_buf); } /* - * Push an item onto the regstack. - * Returns pointer to new item. Returns NULL when out of memory. + * Get pointer to the line "lnum", which is relative to "reg_firstlnum". */ -static regitem_T *regstack_push(regstate_T state, char_u *scan) +static char_u *reg_getline(linenr_T lnum) { - regitem_T *rp; - - if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { - emsg(_(e_maxmempat)); + // when looking behind for a match/no-match lnum is negative. But we + // can't go before line 1 + if (rex.reg_firstlnum + lnum < 1) { return NULL; } - ga_grow(®stack, sizeof(regitem_T)); + if (lnum > rex.reg_maxline) { + // Must have matched the "\n" in the last line. + return (char_u *)""; + } + return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false); +} - rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len); - rp->rs_state = state; - rp->rs_scan = scan; +static char_u *reg_startzp[NSUBEXP]; // Workspace to mark beginning +static char_u *reg_endzp[NSUBEXP]; // and end of \z(...\) matches +static lpos_T reg_startzpos[NSUBEXP]; // idem, beginning pos +static lpos_T reg_endzpos[NSUBEXP]; // idem, end pos - regstack.ga_len += sizeof(regitem_T); - return rp; -} +// true if using multi-line regexp. +#define REG_MULTI (rex.reg_match == NULL) /* - * Pop an item from the regstack. + * Create a new extmatch and mark it as referenced once. */ -static void regstack_pop(char_u **scan) +static reg_extmatch_T *make_extmatch(void) + FUNC_ATTR_NONNULL_RET { - regitem_T *rp; - - rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; - *scan = rp->rs_scan; + reg_extmatch_T *em = xcalloc(1, sizeof(reg_extmatch_T)); + em->refcnt = 1; + return em; +} - regstack.ga_len -= sizeof(regitem_T); +/* + * Add a reference to an extmatch. + */ +reg_extmatch_T *ref_extmatch(reg_extmatch_T *em) +{ + if (em != NULL) + em->refcnt++; + return em; } /* - * regrepeat - repeatedly match something simple, return how many. - * Advances rex.input (and rex.lnum) to just after the matched chars. + * Remove a reference to an extmatch. If there are no references left, free + * the info. */ -static int -regrepeat ( - char_u *p, - long maxcount /* maximum number of matches allowed */ -) +void unref_extmatch(reg_extmatch_T *em) { - long count = 0; - char_u *opnd; - int mask; - int testval = 0; - - char_u *scan = rex.input; // Make local copy of rex.input for speed. - opnd = OPERAND(p); - switch (OP(p)) { - case ANY: - case ANY + ADD_NL: - while (count < maxcount) { - /* Matching anything means we continue until end-of-line (or - * end-of-file for ANY + ADD_NL), only limited by maxcount. */ - while (*scan != NUL && count < maxcount) { - count++; - MB_PTR_ADV(scan); - } - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr || count == maxcount) { - break; - } - count++; // count the line-break - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } - break; + int i; - case IDENT: - case IDENT + ADD_NL: - testval = 1; - FALLTHROUGH; - case SIDENT: - case SIDENT + ADD_NL: - while (count < maxcount) { - if (vim_isIDc(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) { - MB_PTR_ADV(scan); - } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else { - break; - } - ++count; - } - break; + if (em != NULL && --em->refcnt <= 0) { + for (i = 0; i < NSUBEXP; ++i) + xfree(em->matches[i]); + xfree(em); + } +} - case KWORD: - case KWORD + ADD_NL: - testval = 1; - FALLTHROUGH; - case SKWORD: - case SKWORD + ADD_NL: - while (count < maxcount) { - if (vim_iswordp_buf(scan, rex.reg_buf) - && (testval || !ascii_isdigit(*scan))) { - MB_PTR_ADV(scan); - } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else { - break; - } - count++; - } - break; +// Get class of previous character. +static int reg_prev_class(void) +{ + if (rex.input > rex.line) { + return mb_get_class_tab( + rex.input - 1 - utf_head_off(rex.line, rex.input - 1), + rex.reg_buf->b_chartab); + } + return -1; +} - case FNAME: - case FNAME + ADD_NL: - testval = 1; - FALLTHROUGH; - case SFNAME: - case SFNAME + ADD_NL: - while (count < maxcount) { - if (vim_isfilec(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) { - MB_PTR_ADV(scan); - } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else { - break; - } - count++; - } - break; - case PRINT: - case PRINT + ADD_NL: - testval = 1; - FALLTHROUGH; - case SPRINT: - case SPRINT + ADD_NL: - while (count < maxcount) { - if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if (vim_isprintc(utf_ptr2char(scan)) == 1 - && (testval || !ascii_isdigit(*scan))) { - MB_PTR_ADV(scan); - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else { - break; - } - count++; - } - break; +// Return true if the current rex.input position matches the Visual area. +static bool reg_match_visual(void) +{ + pos_T top, bot; + linenr_T lnum; + colnr_T col; + win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win; + int mode; + colnr_T start, end; + colnr_T start2, end2; + colnr_T curswant; - case WHITE: - case WHITE + ADD_NL: - testval = mask = RI_WHITE; -do_class: - while (count < maxcount) { - int l; - if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if ((l = utfc_ptr2len(scan)) > 1) { - if (testval != 0) { - break; - } - scan += l; - } else if ((class_tab[*scan] & mask) == testval) { - scan++; - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else { - break; - } - ++count; - } - break; + // Check if the buffer is the current buffer. + if (rex.reg_buf != curbuf || VIsual.lnum == 0) { + return false; + } - case NWHITE: - case NWHITE + ADD_NL: - mask = RI_WHITE; - goto do_class; - case DIGIT: - case DIGIT + ADD_NL: - testval = mask = RI_DIGIT; - goto do_class; - case NDIGIT: - case NDIGIT + ADD_NL: - mask = RI_DIGIT; - goto do_class; - case HEX: - case HEX + ADD_NL: - testval = mask = RI_HEX; - goto do_class; - case NHEX: - case NHEX + ADD_NL: - mask = RI_HEX; - goto do_class; - case OCTAL: - case OCTAL + ADD_NL: - testval = mask = RI_OCTAL; - goto do_class; - case NOCTAL: - case NOCTAL + ADD_NL: - mask = RI_OCTAL; - goto do_class; - case WORD: - case WORD + ADD_NL: - testval = mask = RI_WORD; - goto do_class; - case NWORD: - case NWORD + ADD_NL: - mask = RI_WORD; - goto do_class; - case HEAD: - case HEAD + ADD_NL: - testval = mask = RI_HEAD; - goto do_class; - case NHEAD: - case NHEAD + ADD_NL: - mask = RI_HEAD; - goto do_class; - case ALPHA: - case ALPHA + ADD_NL: - testval = mask = RI_ALPHA; - goto do_class; - case NALPHA: - case NALPHA + ADD_NL: - mask = RI_ALPHA; - goto do_class; - case LOWER: - case LOWER + ADD_NL: - testval = mask = RI_LOWER; - goto do_class; - case NLOWER: - case NLOWER + ADD_NL: - mask = RI_LOWER; - goto do_class; - case UPPER: - case UPPER + ADD_NL: - testval = mask = RI_UPPER; - goto do_class; - case NUPPER: - case NUPPER + ADD_NL: - mask = RI_UPPER; - goto do_class; - - case EXACTLY: - { - int cu, cl; - - // This doesn't do a multi-byte character, because a MULTIBYTECODE - // would have been used for it. It does handle single-byte - // characters, such as latin1. - if (rex.reg_ic) { - cu = mb_toupper(*opnd); - cl = mb_tolower(*opnd); - while (count < maxcount && (*scan == cu || *scan == cl)) { - count++; - scan++; - } + if (VIsual_active) { + if (lt(VIsual, wp->w_cursor)) { + top = VIsual; + bot = wp->w_cursor; } else { - cu = *opnd; - while (count < maxcount && *scan == cu) { - count++; - scan++; - } + top = wp->w_cursor; + bot = VIsual; } - break; - } - - case MULTIBYTECODE: - { - int i, len, cf = 0; - - /* Safety check (just in case 'encoding' was changed since - * compiling the program). */ - if ((len = utfc_ptr2len(opnd)) > 1) { - if (rex.reg_ic) { - cf = utf_fold(utf_ptr2char(opnd)); - } - while (count < maxcount && utfc_ptr2len(scan) >= len) { - for (i = 0; i < len; i++) { - if (opnd[i] != scan[i]) { - break; - } - } - if (i < len && (!rex.reg_ic - || utf_fold(utf_ptr2char(scan)) != cf)) { - break; - } - scan += len; - ++count; - } + mode = VIsual_mode; + curswant = wp->w_curswant; + } else { + if (lt(curbuf->b_visual.vi_start, curbuf->b_visual.vi_end)) { + top = curbuf->b_visual.vi_start; + bot = curbuf->b_visual.vi_end; + } else { + top = curbuf->b_visual.vi_end; + bot = curbuf->b_visual.vi_start; } + mode = curbuf->b_visual.vi_mode; + curswant = curbuf->b_visual.vi_curswant; } - break; - - case ANYOF: - case ANYOF + ADD_NL: - testval = 1; - FALLTHROUGH; - - case ANYBUT: - case ANYBUT + ADD_NL: - while (count < maxcount) { - int len; - if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline - || rex.reg_line_lbr) { - break; - } - reg_nextline(); - scan = rex.input; - if (got_int) { - break; - } - } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { - scan++; - } else if ((len = utfc_ptr2len(scan)) > 1) { - if ((cstrchr(opnd, utf_ptr2char(scan)) == NULL) == testval) { - break; - } - scan += len; - } else { - if ((cstrchr(opnd, *scan) == NULL) == testval) - break; - ++scan; - } - ++count; - } - break; - - case NEWL: - while (count < maxcount - && ((*scan == NUL && rex.lnum <= rex.reg_maxline && !rex.reg_line_lbr - && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) { - count++; - if (rex.reg_line_lbr) { - ADVANCE_REGINPUT(); - } else { - reg_nextline(); - } - scan = rex.input; - if (got_int) { - break; - } - } - break; - - default: // Oh dear. Called inappropriately. - iemsg(_(e_re_corr)); -#ifdef REGEXP_DEBUG - printf("Called regrepeat with op code %d\n", OP(p)); -#endif - break; + lnum = rex.lnum + rex.reg_firstlnum; + if (lnum < top.lnum || lnum > bot.lnum) { + return false; } - rex.input = scan; - - return (int)count; -} - -/* - * regnext - dig the "next" pointer out of a node - * Returns NULL when calculating size, when there is no next item and when - * there is an error. - */ -static char_u *regnext(char_u *p) - FUNC_ATTR_NONNULL_ALL -{ - int offset; - - if (p == JUST_CALC_SIZE || reg_toolong) - return NULL; - - offset = NEXT(p); - if (offset == 0) - return NULL; - - if (OP(p) == BACK) - return p - offset; - else - return p + offset; + if (mode == 'v') { + col = (colnr_T)(rex.input - rex.line); + if ((lnum == top.lnum && col < top.col) + || (lnum == bot.lnum && col >= bot.col + (*p_sel != 'e'))) { + return false; + } + } else if (mode == Ctrl_V) { + getvvcol(wp, &top, &start, NULL, &end); + getvvcol(wp, &bot, &start2, NULL, &end2); + if (start2 < start) + start = start2; + if (end2 > end) + end = end2; + if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL) { + end = MAXCOL; + } + unsigned int cols_u = win_linetabsize(wp, rex.line, + (colnr_T)(rex.input - rex.line)); + assert(cols_u <= MAXCOL); + colnr_T cols = (colnr_T)cols_u; + if (cols < start || cols > end - (*p_sel == 'e')) { + return false; + } + } + return true; } /* @@ -5654,7 +1208,7 @@ static void cleanup_zsubexpr(void) { if (rex.need_clear_zsubexpr) { if (REG_MULTI) { - /* Use 0xff to set lnum to -1 */ + // Use 0xff to set lnum to -1 memset(reg_startzpos, 0xff, sizeof(lpos_T) * NSUBEXP); memset(reg_endzpos, 0xff, sizeof(lpos_T) * NSUBEXP); } else { @@ -5665,45 +1219,6 @@ static void cleanup_zsubexpr(void) } } -// Save the current subexpr to "bp", so that they can be restored -// later by restore_subexpr(). -static void save_subexpr(regbehind_T *bp) - FUNC_ATTR_NONNULL_ALL -{ - // When "rex.need_clear_subexpr" is set we don't need to save the values, only - // remember that this flag needs to be set again when restoring. - bp->save_need_clear_subexpr = rex.need_clear_subexpr; - if (!rex.need_clear_subexpr) { - for (int i = 0; i < NSUBEXP; i++) { - if (REG_MULTI) { - bp->save_start[i].se_u.pos = rex.reg_startpos[i]; - bp->save_end[i].se_u.pos = rex.reg_endpos[i]; - } else { - bp->save_start[i].se_u.ptr = rex.reg_startp[i]; - bp->save_end[i].se_u.ptr = rex.reg_endp[i]; - } - } - } -} - -// Restore the subexpr from "bp". -static void restore_subexpr(regbehind_T *bp) - FUNC_ATTR_NONNULL_ALL -{ - // Only need to restore saved values when they are not to be cleared. - rex.need_clear_subexpr = bp->save_need_clear_subexpr; - if (!rex.need_clear_subexpr) { - for (int i = 0; i < NSUBEXP; i++) { - if (REG_MULTI) { - rex.reg_startpos[i] = bp->save_start[i].se_u.pos; - rex.reg_endpos[i] = bp->save_end[i].se_u.pos; - } else { - rex.reg_startp[i] = bp->save_start[i].se_u.ptr; - rex.reg_endp[i] = bp->save_end[i].se_u.ptr; - } - } - } -} // Advance rex.lnum, rex.line and rex.input to the next line. static void reg_nextline(void) @@ -5713,81 +1228,6 @@ static void reg_nextline(void) fast_breakcheck(); } -// Save the input line and position in a regsave_T. -static void reg_save(regsave_T *save, garray_T *gap) - FUNC_ATTR_NONNULL_ALL -{ - if (REG_MULTI) { - save->rs_u.pos.col = (colnr_T)(rex.input - rex.line); - save->rs_u.pos.lnum = rex.lnum; - } else { - save->rs_u.ptr = rex.input; - } - save->rs_len = gap->ga_len; -} - -// Restore the input line and position from a regsave_T. -static void reg_restore(regsave_T *save, garray_T *gap) - FUNC_ATTR_NONNULL_ALL -{ - if (REG_MULTI) { - if (rex.lnum != save->rs_u.pos.lnum) { - // only call reg_getline() when the line number changed to save - // a bit of time - rex.lnum = save->rs_u.pos.lnum; - rex.line = reg_getline(rex.lnum); - } - rex.input = rex.line + save->rs_u.pos.col; - } else { - rex.input = save->rs_u.ptr; - } - gap->ga_len = save->rs_len; -} - -// Return true if current position is equal to saved position. -static bool reg_save_equal(const regsave_T *save) - FUNC_ATTR_NONNULL_ALL -{ - if (REG_MULTI) { - return rex.lnum == save->rs_u.pos.lnum - && rex.input == rex.line + save->rs_u.pos.col; - } - return rex.input == save->rs_u.ptr; -} - -/* - * Tentatively set the sub-expression start to the current position (after - * calling regmatch() they will have changed). Need to save the existing - * values for when there is no match. - * Use se_save() to use pointer (save_se_multi()) or position (save_se_one()), - * depending on REG_MULTI. - */ -static void save_se_multi(save_se_T *savep, lpos_T *posp) -{ - savep->se_u.pos = *posp; - posp->lnum = rex.lnum; - posp->col = (colnr_T)(rex.input - rex.line); -} - -static void save_se_one(save_se_T *savep, char_u **pp) -{ - savep->se_u.ptr = *pp; - *pp = rex.input; -} - -/* - * Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL. - */ -static int re_num_cmp(uint32_t val, char_u *scan) -{ - uint32_t n = (uint32_t)OPERAND_MIN(scan); - - if (OPERAND_CMP(scan) == '>') - return val > n; - if (OPERAND_CMP(scan) == '<') - return val < n; - return val == n; -} /* * Check whether a backreference matches. @@ -5795,7 +1235,12 @@ static int re_num_cmp(uint32_t val, char_u *scan) * If "bytelen" is not NULL, it is set to the byte length of the match in the * last line. */ -static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T end_lnum, colnr_T end_col, int *bytelen) +static int match_with_backref( + linenr_T start_lnum, + colnr_T start_col, + linenr_T end_lnum, + colnr_T end_col, + int *bytelen) { linenr_T clnum = start_lnum; colnr_T ccol = start_col; @@ -5810,7 +1255,7 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e if (rex.line != reg_tofree) { len = (int)STRLEN(rex.line); if (reg_tofree == NULL || len >= (int)reg_tofreelen) { - len += 50; /* get some extra */ + len += 50; // get some extra xfree(reg_tofree); reg_tofree = xmalloc(len); reg_tofreelen = len; @@ -5820,7 +1265,7 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e rex.line = reg_tofree; } - /* Get the line to compare with. */ + // Get the line to compare with. p = reg_getline(clnum); assert(p); @@ -5842,7 +1287,7 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e return RA_NOMATCH; // text too short } - /* Advance to next line. */ + // Advance to next line. reg_nextline(); if (bytelen != NULL) *bytelen = 0; @@ -5857,520 +1302,72 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e return RA_MATCH; } -#ifdef BT_REGEXP_DUMP - -/* - * regdump - dump a regexp onto stdout in vaguely comprehensible form - */ -static void regdump(char_u *pattern, bt_regprog_T *r) -{ - char_u *s; - int op = EXACTLY; /* Arbitrary non-END op. */ - char_u *next; - char_u *end = NULL; - FILE *f; - -#ifdef BT_REGEXP_LOG - f = fopen("bt_regexp_log.log", "a"); -#else - f = stdout; -#endif - if (f == NULL) - return; - fprintf(f, "-------------------------------------\n\r\nregcomp(%s):\r\n", - pattern); - - s = r->program + 1; - /* - * Loop until we find the END that isn't before a referred next (an END - * can also appear in a NOMATCH operand). - */ - while (op != END || s <= end) { - op = OP(s); - fprintf(f, "%2d%s", (int)(s - r->program), regprop(s)); /* Where, what. */ - next = regnext(s); - if (next == NULL) /* Next ptr. */ - fprintf(f, "(0)"); - else - fprintf(f, "(%d)", (int)((s - r->program) + (next - s))); - if (end < next) - end = next; - if (op == BRACE_LIMITS) { - /* Two ints */ - fprintf(f, " minval %" PRId64 ", maxval %" PRId64, - (int64_t)OPERAND_MIN(s), (int64_t)OPERAND_MAX(s)); - s += 8; - } else if (op == BEHIND || op == NOBEHIND) { - /* one int */ - fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s)); - s += 4; - } else if (op == RE_LNUM || op == RE_COL || op == RE_VCOL) { - // one int plus comparator - fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s)); - s += 5; - } - s += 3; - if (op == ANYOF || op == ANYOF + ADD_NL - || op == ANYBUT || op == ANYBUT + ADD_NL - || op == EXACTLY) { - /* Literal string, where present. */ - fprintf(f, "\nxxxxxxxxx\n"); - while (*s != NUL) - fprintf(f, "%c", *s++); - fprintf(f, "\nxxxxxxxxx\n"); - s++; - } - fprintf(f, "\r\n"); - } - - /* Header fields of interest. */ - if (r->regstart != NUL) - fprintf(f, "start `%s' 0x%x; ", r->regstart < 256 - ? (char *)transchar(r->regstart) - : "multibyte", r->regstart); - if (r->reganch) - fprintf(f, "anchored; "); - if (r->regmust != NULL) - fprintf(f, "must have \"%s\"", r->regmust); - fprintf(f, "\r\n"); - -#ifdef BT_REGEXP_LOG - fclose(f); -#endif -} -#endif /* BT_REGEXP_DUMP */ - -#ifdef REGEXP_DEBUG -/* - * regprop - printable representation of opcode - */ -static char_u *regprop(char_u *op) +/// Used in a place where no * or \+ can follow. +static bool re_mult_next(char *what) { - char *p; - static char buf[50]; - - STRCPY(buf, ":"); - - switch ((int) OP(op)) { - case BOL: - p = "BOL"; - break; - case EOL: - p = "EOL"; - break; - case RE_BOF: - p = "BOF"; - break; - case RE_EOF: - p = "EOF"; - break; - case CURSOR: - p = "CURSOR"; - break; - case RE_VISUAL: - p = "RE_VISUAL"; - break; - case RE_LNUM: - p = "RE_LNUM"; - break; - case RE_MARK: - p = "RE_MARK"; - break; - case RE_COL: - p = "RE_COL"; - break; - case RE_VCOL: - p = "RE_VCOL"; - break; - case BOW: - p = "BOW"; - break; - case EOW: - p = "EOW"; - break; - case ANY: - p = "ANY"; - break; - case ANY + ADD_NL: - p = "ANY+NL"; - break; - case ANYOF: - p = "ANYOF"; - break; - case ANYOF + ADD_NL: - p = "ANYOF+NL"; - break; - case ANYBUT: - p = "ANYBUT"; - break; - case ANYBUT + ADD_NL: - p = "ANYBUT+NL"; - break; - case IDENT: - p = "IDENT"; - break; - case IDENT + ADD_NL: - p = "IDENT+NL"; - break; - case SIDENT: - p = "SIDENT"; - break; - case SIDENT + ADD_NL: - p = "SIDENT+NL"; - break; - case KWORD: - p = "KWORD"; - break; - case KWORD + ADD_NL: - p = "KWORD+NL"; - break; - case SKWORD: - p = "SKWORD"; - break; - case SKWORD + ADD_NL: - p = "SKWORD+NL"; - break; - case FNAME: - p = "FNAME"; - break; - case FNAME + ADD_NL: - p = "FNAME+NL"; - break; - case SFNAME: - p = "SFNAME"; - break; - case SFNAME + ADD_NL: - p = "SFNAME+NL"; - break; - case PRINT: - p = "PRINT"; - break; - case PRINT + ADD_NL: - p = "PRINT+NL"; - break; - case SPRINT: - p = "SPRINT"; - break; - case SPRINT + ADD_NL: - p = "SPRINT+NL"; - break; - case WHITE: - p = "WHITE"; - break; - case WHITE + ADD_NL: - p = "WHITE+NL"; - break; - case NWHITE: - p = "NWHITE"; - break; - case NWHITE + ADD_NL: - p = "NWHITE+NL"; - break; - case DIGIT: - p = "DIGIT"; - break; - case DIGIT + ADD_NL: - p = "DIGIT+NL"; - break; - case NDIGIT: - p = "NDIGIT"; - break; - case NDIGIT + ADD_NL: - p = "NDIGIT+NL"; - break; - case HEX: - p = "HEX"; - break; - case HEX + ADD_NL: - p = "HEX+NL"; - break; - case NHEX: - p = "NHEX"; - break; - case NHEX + ADD_NL: - p = "NHEX+NL"; - break; - case OCTAL: - p = "OCTAL"; - break; - case OCTAL + ADD_NL: - p = "OCTAL+NL"; - break; - case NOCTAL: - p = "NOCTAL"; - break; - case NOCTAL + ADD_NL: - p = "NOCTAL+NL"; - break; - case WORD: - p = "WORD"; - break; - case WORD + ADD_NL: - p = "WORD+NL"; - break; - case NWORD: - p = "NWORD"; - break; - case NWORD + ADD_NL: - p = "NWORD+NL"; - break; - case HEAD: - p = "HEAD"; - break; - case HEAD + ADD_NL: - p = "HEAD+NL"; - break; - case NHEAD: - p = "NHEAD"; - break; - case NHEAD + ADD_NL: - p = "NHEAD+NL"; - break; - case ALPHA: - p = "ALPHA"; - break; - case ALPHA + ADD_NL: - p = "ALPHA+NL"; - break; - case NALPHA: - p = "NALPHA"; - break; - case NALPHA + ADD_NL: - p = "NALPHA+NL"; - break; - case LOWER: - p = "LOWER"; - break; - case LOWER + ADD_NL: - p = "LOWER+NL"; - break; - case NLOWER: - p = "NLOWER"; - break; - case NLOWER + ADD_NL: - p = "NLOWER+NL"; - break; - case UPPER: - p = "UPPER"; - break; - case UPPER + ADD_NL: - p = "UPPER+NL"; - break; - case NUPPER: - p = "NUPPER"; - break; - case NUPPER + ADD_NL: - p = "NUPPER+NL"; - break; - case BRANCH: - p = "BRANCH"; - break; - case EXACTLY: - p = "EXACTLY"; - break; - case NOTHING: - p = "NOTHING"; - break; - case BACK: - p = "BACK"; - break; - case END: - p = "END"; - break; - case MOPEN + 0: - p = "MATCH START"; - break; - case MOPEN + 1: - case MOPEN + 2: - case MOPEN + 3: - case MOPEN + 4: - case MOPEN + 5: - case MOPEN + 6: - case MOPEN + 7: - case MOPEN + 8: - case MOPEN + 9: - sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN); - p = NULL; - break; - case MCLOSE + 0: - p = "MATCH END"; - break; - case MCLOSE + 1: - case MCLOSE + 2: - case MCLOSE + 3: - case MCLOSE + 4: - case MCLOSE + 5: - case MCLOSE + 6: - case MCLOSE + 7: - case MCLOSE + 8: - case MCLOSE + 9: - sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE); - p = NULL; - break; - case BACKREF + 1: - case BACKREF + 2: - case BACKREF + 3: - case BACKREF + 4: - case BACKREF + 5: - case BACKREF + 6: - case BACKREF + 7: - case BACKREF + 8: - case BACKREF + 9: - sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF); - p = NULL; - break; - case NOPEN: - p = "NOPEN"; - break; - case NCLOSE: - p = "NCLOSE"; - break; - case ZOPEN + 1: - case ZOPEN + 2: - case ZOPEN + 3: - case ZOPEN + 4: - case ZOPEN + 5: - case ZOPEN + 6: - case ZOPEN + 7: - case ZOPEN + 8: - case ZOPEN + 9: - sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN); - p = NULL; - break; - case ZCLOSE + 1: - case ZCLOSE + 2: - case ZCLOSE + 3: - case ZCLOSE + 4: - case ZCLOSE + 5: - case ZCLOSE + 6: - case ZCLOSE + 7: - case ZCLOSE + 8: - case ZCLOSE + 9: - sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE); - p = NULL; - break; - case ZREF + 1: - case ZREF + 2: - case ZREF + 3: - case ZREF + 4: - case ZREF + 5: - case ZREF + 6: - case ZREF + 7: - case ZREF + 8: - case ZREF + 9: - sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF); - p = NULL; - break; - case STAR: - p = "STAR"; - break; - case PLUS: - p = "PLUS"; - break; - case NOMATCH: - p = "NOMATCH"; - break; - case MATCH: - p = "MATCH"; - break; - case BEHIND: - p = "BEHIND"; - break; - case NOBEHIND: - p = "NOBEHIND"; - break; - case SUBPAT: - p = "SUBPAT"; - break; - case BRACE_LIMITS: - p = "BRACE_LIMITS"; - break; - case BRACE_SIMPLE: - p = "BRACE_SIMPLE"; - break; - case BRACE_COMPLEX + 0: - case BRACE_COMPLEX + 1: - case BRACE_COMPLEX + 2: - case BRACE_COMPLEX + 3: - case BRACE_COMPLEX + 4: - case BRACE_COMPLEX + 5: - case BRACE_COMPLEX + 6: - case BRACE_COMPLEX + 7: - case BRACE_COMPLEX + 8: - case BRACE_COMPLEX + 9: - sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX); - p = NULL; - break; - case MULTIBYTECODE: - p = "MULTIBYTECODE"; - break; - case NEWL: - p = "NEWL"; - break; - default: - sprintf(buf + STRLEN(buf), "corrupt %d", OP(op)); - p = NULL; - break; + if (re_multi_type(peekchr()) == MULTI_MULT) { + semsg(_("E888: (NFA regexp) cannot repeat %s"), what); + rc_did_emsg = true; + return false; } - if (p != NULL) - STRCAT(buf, p); - return (char_u *)buf; + return true; } -#endif /* REGEXP_DEBUG */ - +typedef struct { + int a, b, c; +} decomp_T; -/* 0xfb20 - 0xfb4f */ +// 0xfb20 - 0xfb4f static decomp_T decomp_table[0xfb4f-0xfb20+1] = { - {0x5e2,0,0}, /* 0xfb20 alt ayin */ - {0x5d0,0,0}, /* 0xfb21 alt alef */ - {0x5d3,0,0}, /* 0xfb22 alt dalet */ - {0x5d4,0,0}, /* 0xfb23 alt he */ - {0x5db,0,0}, /* 0xfb24 alt kaf */ - {0x5dc,0,0}, /* 0xfb25 alt lamed */ - {0x5dd,0,0}, /* 0xfb26 alt mem-sofit */ - {0x5e8,0,0}, /* 0xfb27 alt resh */ - {0x5ea,0,0}, /* 0xfb28 alt tav */ - {'+', 0, 0}, /* 0xfb29 alt plus */ - {0x5e9, 0x5c1, 0}, /* 0xfb2a shin+shin-dot */ - {0x5e9, 0x5c2, 0}, /* 0xfb2b shin+sin-dot */ - {0x5e9, 0x5c1, 0x5bc}, /* 0xfb2c shin+shin-dot+dagesh */ - {0x5e9, 0x5c2, 0x5bc}, /* 0xfb2d shin+sin-dot+dagesh */ - {0x5d0, 0x5b7, 0}, /* 0xfb2e alef+patah */ - {0x5d0, 0x5b8, 0}, /* 0xfb2f alef+qamats */ - {0x5d0, 0x5b4, 0}, /* 0xfb30 alef+hiriq */ - {0x5d1, 0x5bc, 0}, /* 0xfb31 bet+dagesh */ - {0x5d2, 0x5bc, 0}, /* 0xfb32 gimel+dagesh */ - {0x5d3, 0x5bc, 0}, /* 0xfb33 dalet+dagesh */ - {0x5d4, 0x5bc, 0}, /* 0xfb34 he+dagesh */ - {0x5d5, 0x5bc, 0}, /* 0xfb35 vav+dagesh */ - {0x5d6, 0x5bc, 0}, /* 0xfb36 zayin+dagesh */ - {0xfb37, 0, 0}, /* 0xfb37 -- */ - {0x5d8, 0x5bc, 0}, /* 0xfb38 tet+dagesh */ - {0x5d9, 0x5bc, 0}, /* 0xfb39 yud+dagesh */ - {0x5da, 0x5bc, 0}, /* 0xfb3a kaf sofit+dagesh */ - {0x5db, 0x5bc, 0}, /* 0xfb3b kaf+dagesh */ - {0x5dc, 0x5bc, 0}, /* 0xfb3c lamed+dagesh */ - {0xfb3d, 0, 0}, /* 0xfb3d -- */ - {0x5de, 0x5bc, 0}, /* 0xfb3e mem+dagesh */ - {0xfb3f, 0, 0}, /* 0xfb3f -- */ - {0x5e0, 0x5bc, 0}, /* 0xfb40 nun+dagesh */ - {0x5e1, 0x5bc, 0}, /* 0xfb41 samech+dagesh */ - {0xfb42, 0, 0}, /* 0xfb42 -- */ - {0x5e3, 0x5bc, 0}, /* 0xfb43 pe sofit+dagesh */ - {0x5e4, 0x5bc,0}, /* 0xfb44 pe+dagesh */ - {0xfb45, 0, 0}, /* 0xfb45 -- */ - {0x5e6, 0x5bc, 0}, /* 0xfb46 tsadi+dagesh */ - {0x5e7, 0x5bc, 0}, /* 0xfb47 qof+dagesh */ - {0x5e8, 0x5bc, 0}, /* 0xfb48 resh+dagesh */ - {0x5e9, 0x5bc, 0}, /* 0xfb49 shin+dagesh */ - {0x5ea, 0x5bc, 0}, /* 0xfb4a tav+dagesh */ - {0x5d5, 0x5b9, 0}, /* 0xfb4b vav+holam */ - {0x5d1, 0x5bf, 0}, /* 0xfb4c bet+rafe */ - {0x5db, 0x5bf, 0}, /* 0xfb4d kaf+rafe */ - {0x5e4, 0x5bf, 0}, /* 0xfb4e pe+rafe */ - {0x5d0, 0x5dc, 0} /* 0xfb4f alef-lamed */ + { 0x5e2, 0, 0 }, // 0xfb20 alt ayin + { 0x5d0, 0, 0 }, // 0xfb21 alt alef + { 0x5d3, 0, 0 }, // 0xfb22 alt dalet + { 0x5d4, 0, 0 }, // 0xfb23 alt he + { 0x5db, 0, 0 }, // 0xfb24 alt kaf + { 0x5dc, 0, 0 }, // 0xfb25 alt lamed + { 0x5dd, 0, 0 }, // 0xfb26 alt mem-sofit + { 0x5e8, 0, 0 }, // 0xfb27 alt resh + { 0x5ea, 0, 0 }, // 0xfb28 alt tav + { '+', 0, 0 }, // 0xfb29 alt plus + { 0x5e9, 0x5c1, 0 }, // 0xfb2a shin+shin-dot + { 0x5e9, 0x5c2, 0 }, // 0xfb2b shin+sin-dot + { 0x5e9, 0x5c1, 0x5bc }, // 0xfb2c shin+shin-dot+dagesh + { 0x5e9, 0x5c2, 0x5bc }, // 0xfb2d shin+sin-dot+dagesh + { 0x5d0, 0x5b7, 0 }, // 0xfb2e alef+patah + { 0x5d0, 0x5b8, 0 }, // 0xfb2f alef+qamats + { 0x5d0, 0x5b4, 0 }, // 0xfb30 alef+hiriq + { 0x5d1, 0x5bc, 0 }, // 0xfb31 bet+dagesh + { 0x5d2, 0x5bc, 0 }, // 0xfb32 gimel+dagesh + { 0x5d3, 0x5bc, 0 }, // 0xfb33 dalet+dagesh + { 0x5d4, 0x5bc, 0 }, // 0xfb34 he+dagesh + { 0x5d5, 0x5bc, 0 }, // 0xfb35 vav+dagesh + { 0x5d6, 0x5bc, 0 }, // 0xfb36 zayin+dagesh + { 0xfb37, 0, 0 }, // 0xfb37 -- UNUSED + { 0x5d8, 0x5bc, 0 }, // 0xfb38 tet+dagesh + { 0x5d9, 0x5bc, 0 }, // 0xfb39 yud+dagesh + { 0x5da, 0x5bc, 0 }, // 0xfb3a kaf sofit+dagesh + { 0x5db, 0x5bc, 0 }, // 0xfb3b kaf+dagesh + { 0x5dc, 0x5bc, 0 }, // 0xfb3c lamed+dagesh + { 0xfb3d, 0, 0 }, // 0xfb3d -- UNUSED + { 0x5de, 0x5bc, 0 }, // 0xfb3e mem+dagesh + { 0xfb3f, 0, 0 }, // 0xfb3f -- UNUSED + { 0x5e0, 0x5bc, 0 }, // 0xfb40 nun+dagesh + { 0x5e1, 0x5bc, 0 }, // 0xfb41 samech+dagesh + { 0xfb42, 0, 0 }, // 0xfb42 -- UNUSED + { 0x5e3, 0x5bc, 0 }, // 0xfb43 pe sofit+dagesh + { 0x5e4, 0x5bc, 0 }, // 0xfb44 pe+dagesh + { 0xfb45, 0, 0 }, // 0xfb45 -- UNUSED + { 0x5e6, 0x5bc, 0 }, // 0xfb46 tsadi+dagesh + { 0x5e7, 0x5bc, 0 }, // 0xfb47 qof+dagesh + { 0x5e8, 0x5bc, 0 }, // 0xfb48 resh+dagesh + { 0x5e9, 0x5bc, 0 }, // 0xfb49 shin+dagesh + { 0x5ea, 0x5bc, 0 }, // 0xfb4a tav+dagesh + { 0x5d5, 0x5b9, 0 }, // 0xfb4b vav+holam + { 0x5d1, 0x5bf, 0 }, // 0xfb4c bet+rafe + { 0x5db, 0x5bf, 0 }, // 0xfb4d kaf+rafe + { 0x5e4, 0x5bf, 0 }, // 0xfb4e pe+rafe + { 0x5d0, 0x5dc, 0 } // 0xfb4f alef-lamed }; static void mb_decompose(int c, int *c1, int *c2, int *c3) @@ -6439,12 +1436,53 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) return result; } +/// Wrapper around strchr which accounts for case-insensitive searches and +/// non-ASCII characters. +/// +/// This function is used a lot for simple searches, keep it fast! +/// +/// @param s string to search +/// @param c character to find in @a s +/// +/// @return NULL if no match, otherwise pointer to the position in @a s +static inline char_u *cstrchr(const char_u *const s, const int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_ALWAYS_INLINE +{ + if (!rex.reg_ic) { + return vim_strchr(s, c); + } + + // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is + // expected to be highly optimized. + if (c > 0x80) { + const int folded_c = utf_fold(c); + for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { + if (utf_fold(utf_ptr2char(p)) == folded_c) { + return (char_u *)p; + } + } + return NULL; + } + + int cc; + if (ASCII_ISUPPER(c)) { + cc = TOLOWER_ASC(c); + } else if (ASCII_ISLOWER(c)) { + cc = TOUPPER_ASC(c); + } else { + return vim_strchr(s, c); + } + + char tofind[] = { (char)c, (char)cc, NUL }; + return (char_u *)strpbrk((const char *)s, tofind); +} + //////////////////////////////////////////////////////////////// // regsub stuff // //////////////////////////////////////////////////////////////// -/* This stuff below really confuses cc on an SGI -- webb */ - +// This stuff below really confuses cc on an SGI -- webb static fptr_T do_upper(int *d, int c) @@ -6498,13 +1536,13 @@ char_u *regtilde(char_u *source, int magic) for (p = newsub; *p; ++p) { if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) { if (reg_prev_sub != NULL) { - /* length = len(newsub) - 1 + len(prev_sub) + 1 */ + // length = len(newsub) - 1 + len(prev_sub) + 1 prevlen = (int)STRLEN(reg_prev_sub); tmpsub = xmalloc(STRLEN(newsub) + prevlen); - /* copy prefix */ - len = (int)(p - newsub); /* not including ~ */ + // copy prefix + len = (int)(p - newsub); // not including ~ memmove(tmpsub, newsub, (size_t)len); - /* interpret tilde */ + // interpret tilde memmove(tmpsub + len, reg_prev_sub, (size_t)prevlen); // copy postfix if (!magic) { @@ -6512,15 +1550,17 @@ char_u *regtilde(char_u *source, int magic) } STRCPY(tmpsub + len + prevlen, p + 1); - if (newsub != source) /* already allocated newsub */ + if (newsub != source) { // already allocated newsub xfree(newsub); + } newsub = tmpsub; p = newsub + len + prevlen; - } else if (magic) - STRMOVE(p, p + 1); /* remove '~' */ - else - STRMOVE(p, p + 2); /* remove '\~' */ - --p; + } else if (magic) { + STRMOVE(p, p + 1); // remove '~' + } else { + STRMOVE(p, p + 2); // remove '\~' + } + p--; } else { if (*p == '\\' && p[1]) { // skip escaped characters p++; @@ -6639,7 +1679,8 @@ int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, return result; } -int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash) +int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, + int copy, int magic, int backslash) { regexec_T rex_save; bool rex_in_use_save = rex_in_use; @@ -6677,8 +1718,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int no = -1; fptr_T func_all = (fptr_T)NULL; fptr_T func_one = (fptr_T)NULL; - linenr_T clnum = 0; /* init for GCC */ - int len = 0; /* init for GCC */ + linenr_T clnum = 0; // init for GCC + int len = 0; // init for GCC static char_u *eval_result = NULL; // We need to keep track of how many backslashes we escape, so that the byte @@ -6790,7 +1831,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, } } if (had_backslash && backslash) { - /* Backslashes will be consumed, need to double them. */ + // Backslashes will be consumed, need to double them. s = vim_strsave_escaped(eval_result, (char_u *)"\\"); xfree(eval_result); eval_result = s; @@ -6830,9 +1871,9 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, } } } - if (no < 0) { /* Ordinary character. */ + if (no < 0) { // Ordinary character. if (c == K_SPECIAL && src[0] != NUL && src[1] != NUL) { - /* Copy a special key as-is. */ + // Copy a special key as-is. if (copy) { *dst++ = c; *dst++ = *src++; @@ -6960,14 +2001,15 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, } else { c = utf_ptr2char(s); - if (func_one != (fptr_T)NULL) - /* Turbo C complains without the typecast */ + if (func_one != (fptr_T)NULL) { + // Turbo C complains without the typecast func_one = (fptr_T)(func_one(&cc, c)); - else if (func_all != (fptr_T)NULL) - /* Turbo C complains without the typecast */ + } else if (func_all != (fptr_T)NULL) { + // Turbo C complains without the typecast func_all = (fptr_T)(func_all(&cc, c)); - else /* just copy */ + } else { // just copy cc = c; + } { int l; @@ -7162,6 +2204,12 @@ list_T *reg_submatch_list(int no) return list; } +// XXX Do not allow headers generator to catch definitions from regexp_nfa.c +#ifndef DO_NOT_DEFINE_EMPTY_ATTRIBUTES +# include "nvim/regexp_bt.c" +# include "nvim/regexp_nfa.c" +#endif + static regengine_T bt_regengine = { bt_regcomp, @@ -7171,12 +2219,6 @@ static regengine_T bt_regengine = (char_u *)"" }; - -// XXX Do not allow headers generator to catch definitions from regexp_nfa.c -#ifndef DO_NOT_DEFINE_EMPTY_ATTRIBUTES -# include "nvim/regexp_nfa.c" -#endif - static regengine_T nfa_regengine = { nfa_regcomp, @@ -7212,7 +2254,7 @@ regprog_T *vim_regcomp(char_u *expr_arg, int re_flags) regexp_engine = p_re; - /* Check for prefix "\%#=", that sets the regexp engine */ + // Check for prefix "\%#=", that sets the regexp engine if (STRNCMP(expr, "\\%#=", 4) == 0) { int newengine = expr[4] - '0'; @@ -7297,6 +2339,18 @@ void vim_regfree(regprog_T *prog) prog->engine->regfree(prog); } + +#if defined(EXITFREE) +void free_regexp_stuff(void) +{ + ga_clear(®stack); + ga_clear(&backpos); + xfree(reg_tofree); + xfree(reg_prev_sub); +} + +#endif + static void report_re_switch(char_u *pat) { if (p_verbose > 0) { @@ -7319,8 +2373,7 @@ static void report_re_switch(char_u *pat) /// @param nl /// /// @return true if there is a match, false if not. -static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, - bool nl) +static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, bool nl) { regexec_T rex_save; bool rex_in_use_save = rex_in_use; @@ -7377,8 +2430,7 @@ static bool vim_regexec_string(regmatch_T *rmp, char_u *line, colnr_T col, // Note: "*prog" may be freed and changed. // Return true if there is a match, false if not. -bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, - colnr_T col) +bool vim_regexec_prog(regprog_T **prog, bool ignore_case, char_u *line, colnr_T col) { regmatch_T regmatch = { .regprog = *prog, .rm_ic = ignore_case }; bool r = vim_regexec_string(®match, line, col, false); @@ -7410,13 +2462,12 @@ bool vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) /// match otherwise. long vim_regexec_multi( regmmatch_T *rmp, - win_T *win, // window in which to search or NULL - buf_T *buf, // buffer in which to search - linenr_T lnum, // nr of line to start looking for match - colnr_T col, // column to start looking for match - proftime_T *tm, // timeout limit or NULL - int *timed_out // flag is set when timeout limit reached -) + win_T *win, // window in which to search or NULL + buf_T *buf, // buffer in which to search + linenr_T lnum, // nr of line to start looking for match + colnr_T col, // column to start looking for match + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag is set when timeout limit reached FUNC_ATTR_NONNULL_ARG(1) { regexec_T rex_save; diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h index 9527afed58..6edbcf8a31 100644 --- a/src/nvim/regexp.h +++ b/src/nvim/regexp.h @@ -19,6 +19,7 @@ // regexp.c #ifdef INCLUDE_GENERATED_DECLARATIONS # include "regexp.h.generated.h" +# include "regexp_bt.h.generated.h" #endif #endif // NVIM_REGEXP_H diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c new file mode 100644 index 0000000000..f6804e8415 --- /dev/null +++ b/src/nvim/regexp_bt.c @@ -0,0 +1,4951 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// uncrustify:off + +/* + * + * Backtracking regular expression implementation. + * + * This file is included in "regexp.c". + * + * NOTICE: + * + * This is NOT the original regular expression code as written by Henry + * Spencer. This code has been modified specifically for use with the VIM + * editor, and should not be used separately from Vim. If you want a good + * regular expression library, get the original code. The copyright notice + * that follows is from the original. + * + * END NOTICE + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + * + * Changes have been made by Tony Andrews, Olaf 'Rhialto' Seibert, Robert + * Webb, Ciaran McCreesh and Bram Moolenaar. + * Named character class support added by Walter Briscoe (1998 Jul 01) + */ + + +/* + * The "internal use only" fields in regexp_defs.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; NUL if none obvious; Can be a + * multi-byte character. + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * regflags RF_ values or'ed together + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that vim_regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in vim_regexec() needs it and vim_regcomp() is + * computing it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH and BRACES_COMPLEX implement concatenation; a "next" + * pointer with a BRANCH on both ends of it is connecting two alternatives. + * (Here we have one of the subtle syntax dependencies: an individual BRANCH + * (as opposed to a collection of them) is never concatenated with anything + * because of operator precedence). The "next" pointer of a BRACES_COMPLEX + * node points to the node after the stuff to be repeated. + * The operand of some types of node is a literal string; for others, it is a + * node leading into a sub-FSM. In particular, the operand of a BRANCH node + * is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects to the + * thing following the set of BRANCHes.) + * + * pattern is coded like: + * + * +-----------------+ + * | V + * \| BRANCH BRANCH --> END + * | ^ | ^ + * +------+ +----------+ + * + * + * +------------------+ + * V | + * * BRANCH BRANCH --> BACK BRANCH --> NOTHING --> END + * | | ^ ^ + * | +---------------+ | + * +---------------------------------------------+ + * + * + * +----------------------+ + * V | + * \+ BRANCH --> BRANCH --> BACK BRANCH --> NOTHING --> END + * | | ^ ^ + * | +-----------+ | + * +--------------------------------------------------+ + * + * + * +-------------------------+ + * V | + * \{} BRANCH BRACE_LIMITS --> BRACE_COMPLEX --> BACK END + * | | ^ + * | +----------------+ + * +-----------------------------------------------+ + * + * + * \@! BRANCH NOMATCH --> END --> END + * | | ^ ^ + * | +----------------+ | + * +--------------------------------+ + * + * +---------+ + * | V + * \z[abc] BRANCH BRANCH a BRANCH b BRANCH c BRANCH NOTHING --> END + * | | | | ^ ^ + * | | | +-----+ | + * | | +----------------+ | + * | +---------------------------+ | + * +------------------------------------------------------+ + * + * They all start with a BRANCH for "\|" alternatives, even when there is only + * one alternative. + */ + +#include +#include +#include +#include + +#include "nvim/regexp.h" +#include "nvim/garray.h" + +/* + * The opcodes are: + */ + +// definition number opnd? meaning +#define END 0 // End of program or NOMATCH operand. +#define BOL 1 // Match "" at beginning of line. +#define EOL 2 // Match "" at end of line. +#define BRANCH 3 // node Match this alternative, or the + // next... +#define BACK 4 // Match "", "next" ptr points backward. +#define EXACTLY 5 // str Match this string. +#define NOTHING 6 // Match empty string. +#define STAR 7 // node Match this (simple) thing 0 or more + // times. +#define PLUS 8 // node Match this (simple) thing 1 or more + // times. +#define MATCH 9 // node match the operand zero-width +#define NOMATCH 10 // node check for no match with operand +#define BEHIND 11 // node look behind for a match with operand +#define NOBEHIND 12 // node look behind for no match with operand +#define SUBPAT 13 // node match the operand here +#define BRACE_SIMPLE 14 // node Match this (simple) thing between m and + // n times (\{m,n\}). +#define BOW 15 // Match "" after [^a-zA-Z0-9_] +#define EOW 16 // Match "" at [^a-zA-Z0-9_] +#define BRACE_LIMITS 17 // nr nr define the min & max for BRACE_SIMPLE + // and BRACE_COMPLEX. +#define NEWL 18 // Match line-break +#define BHPOS 19 // End position for BEHIND or NOBEHIND + + +// character classes: 20-48 normal, 50-78 include a line-break +#define ADD_NL 30 +#define FIRST_NL ANY + ADD_NL +#define ANY 20 // Match any one character. +#define ANYOF 21 // str Match any character in this string. +#define ANYBUT 22 // str Match any character not in this + // string. +#define IDENT 23 // Match identifier char +#define SIDENT 24 // Match identifier char but no digit +#define KWORD 25 // Match keyword char +#define SKWORD 26 // Match word char but no digit +#define FNAME 27 // Match file name char +#define SFNAME 28 // Match file name char but no digit +#define PRINT 29 // Match printable char +#define SPRINT 30 // Match printable char but no digit +#define WHITE 31 // Match whitespace char +#define NWHITE 32 // Match non-whitespace char +#define DIGIT 33 // Match digit char +#define NDIGIT 34 // Match non-digit char +#define HEX 35 // Match hex char +#define NHEX 36 // Match non-hex char +#define OCTAL 37 // Match octal char +#define NOCTAL 38 // Match non-octal char +#define WORD 39 // Match word char +#define NWORD 40 // Match non-word char +#define HEAD 41 // Match head char +#define NHEAD 42 // Match non-head char +#define ALPHA 43 // Match alpha char +#define NALPHA 44 // Match non-alpha char +#define LOWER 45 // Match lowercase char +#define NLOWER 46 // Match non-lowercase char +#define UPPER 47 // Match uppercase char +#define NUPPER 48 // Match non-uppercase char +#define LAST_NL NUPPER + ADD_NL +// -V:WITH_NL:560 +#define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL) + +#define MOPEN 80 // -89 Mark this point in input as start of + // \( … \) subexpr. MOPEN + 0 marks start of + // match. +#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks + // end of match. +#define BACKREF 100 // -109 node Match same string again \1-\9. + +# define ZOPEN 110 // -119 Mark this point in input as start of + // \z( … \) subexpr. +# define ZCLOSE 120 // -129 Analogous to ZOPEN. +# define ZREF 130 // -139 node Match external submatch \z1-\z9 + +#define BRACE_COMPLEX 140 // -149 node Match nodes between m & n times + +#define NOPEN 150 // Mark this point in input as start of + // \%( subexpr. +#define NCLOSE 151 // Analogous to NOPEN. + +#define MULTIBYTECODE 200 // mbc Match one multi-byte character +#define RE_BOF 201 // Match "" at beginning of file. +#define RE_EOF 202 // Match "" at end of file. +#define CURSOR 203 // Match location of cursor. + +#define RE_LNUM 204 // nr cmp Match line number +#define RE_COL 205 // nr cmp Match column number +#define RE_VCOL 206 // nr cmp Match virtual column number + +#define RE_MARK 207 // mark cmp Match mark position +#define RE_VISUAL 208 // Match Visual area +#define RE_COMPOSING 209 // any composing characters + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 0x1 // Known never to match null string. +#define SIMPLE 0x2 // Simple enough to be STAR/PLUS operand. +#define SPSTART 0x4 // Starts with * or +. +#define HASNL 0x8 // Contains some \n. +#define HASLOOKBH 0x10 // Contains "\@<=" or "\@ 1 + && (re_multi_type(peekchr()) != NOT_MULTI + || utf_iscomposing(c)); +} + + +/* + * Emit (if appropriate) a byte of code + */ +static void regc(int b) +{ + if (regcode == JUST_CALC_SIZE) + regsize++; + else + *regcode++ = b; +} + +/* + * Emit (if appropriate) a multi-byte character of code + */ +static void regmbc(int c) +{ + if (regcode == JUST_CALC_SIZE) { + regsize += utf_char2len(c); + } else { + regcode += utf_char2bytes(c, regcode); + } +} + +#define REGMBC(x) regmbc(x); +#define CASEMBC(x) case x: + +/* + * Produce the bytes for equivalence class "c". + * Currently only handles latin1, latin9 and utf-8. + * NOTE: When changing this function, also change nfa_emit_equi_class() + */ +static void reg_equi_class(int c) +{ + { + switch (c) { + // Do not use '\300' style, it results in a negative number. + case 'A': case 0xc0: case 0xc1: case 0xc2: + case 0xc3: case 0xc4: case 0xc5: + CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd) + CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2) + regmbc('A'); regmbc(0xc0); regmbc(0xc1); + regmbc(0xc2); regmbc(0xc3); regmbc(0xc4); + regmbc(0xc5); + REGMBC(0x100) REGMBC(0x102) REGMBC(0x104) + REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0) + REGMBC(0x1ea2) + return; + case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06) + regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06) + return; + case 'C': case 0xc7: + CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c) + regmbc('C'); regmbc(0xc7); + REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a) + REGMBC(0x10c) + return; + case 'D': CASEMBC(0x10e) CASEMBC(0x110) CASEMBC(0x1e0a) + CASEMBC(0x1e0e) CASEMBC(0x1e10) + regmbc('D'); REGMBC(0x10e) REGMBC(0x110) + REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10) + return; + case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb: + CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118) + CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc) + regmbc('E'); regmbc(0xc8); regmbc(0xc9); + regmbc(0xca); regmbc(0xcb); + REGMBC(0x112) REGMBC(0x114) REGMBC(0x116) + REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba) + REGMBC(0x1ebc) + return; + case 'F': CASEMBC(0x1e1e) + regmbc('F'); REGMBC(0x1e1e) + return; + case 'G': CASEMBC(0x11c) CASEMBC(0x11e) CASEMBC(0x120) + CASEMBC(0x122) CASEMBC(0x1e4) CASEMBC(0x1e6) CASEMBC(0x1f4) + CASEMBC(0x1e20) + regmbc('G'); REGMBC(0x11c) REGMBC(0x11e) + REGMBC(0x120) REGMBC(0x122) REGMBC(0x1e4) + REGMBC(0x1e6) REGMBC(0x1f4) REGMBC(0x1e20) + return; + case 'H': CASEMBC(0x124) CASEMBC(0x126) CASEMBC(0x1e22) + CASEMBC(0x1e26) CASEMBC(0x1e28) + regmbc('H'); REGMBC(0x124) REGMBC(0x126) + REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28) + return; + case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf: + CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e) + CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8) + regmbc('I'); regmbc(0xcc); regmbc(0xcd); + regmbc(0xce); regmbc(0xcf); + REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c) + REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf) + REGMBC(0x1ec8) + return; + case 'J': CASEMBC(0x134) + regmbc('J'); REGMBC(0x134) + return; + case 'K': CASEMBC(0x136) CASEMBC(0x1e8) CASEMBC(0x1e30) + CASEMBC(0x1e34) + regmbc('K'); REGMBC(0x136) REGMBC(0x1e8) + REGMBC(0x1e30) REGMBC(0x1e34) + return; + case 'L': CASEMBC(0x139) CASEMBC(0x13b) CASEMBC(0x13d) + CASEMBC(0x13f) CASEMBC(0x141) CASEMBC(0x1e3a) + regmbc('L'); REGMBC(0x139) REGMBC(0x13b) + REGMBC(0x13d) REGMBC(0x13f) REGMBC(0x141) + REGMBC(0x1e3a) + return; + case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40) + regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40) + return; + case 'N': case 0xd1: + CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44) + CASEMBC(0x1e48) + regmbc('N'); regmbc(0xd1); + REGMBC(0x143) REGMBC(0x145) REGMBC(0x147) + REGMBC(0x1e44) REGMBC(0x1e48) + return; + case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5: + case 0xd6: case 0xd8: + CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0) + CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece) + regmbc('O'); regmbc(0xd2); regmbc(0xd3); + regmbc(0xd4); regmbc(0xd5); regmbc(0xd6); + regmbc(0xd8); + REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150) + REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea) + REGMBC(0x1ec) REGMBC(0x1ece) + return; + case 'P': case 0x1e54: case 0x1e56: + regmbc('P'); REGMBC(0x1e54) REGMBC(0x1e56) + return; + case 'R': CASEMBC(0x154) CASEMBC(0x156) CASEMBC(0x158) + CASEMBC(0x1e58) CASEMBC(0x1e5e) + regmbc('R'); REGMBC(0x154) REGMBC(0x156) REGMBC(0x158) + REGMBC(0x1e58) REGMBC(0x1e5e) + return; + case 'S': CASEMBC(0x15a) CASEMBC(0x15c) CASEMBC(0x15e) + CASEMBC(0x160) CASEMBC(0x1e60) + regmbc('S'); REGMBC(0x15a) REGMBC(0x15c) + REGMBC(0x15e) REGMBC(0x160) REGMBC(0x1e60) + return; + case 'T': CASEMBC(0x162) CASEMBC(0x164) CASEMBC(0x166) + CASEMBC(0x1e6a) CASEMBC(0x1e6e) + regmbc('T'); REGMBC(0x162) REGMBC(0x164) + REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e) + return; + case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc: + CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e) + CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3) + CASEMBC(0x1ee6) + regmbc('U'); regmbc(0xd9); regmbc(0xda); + regmbc(0xdb); regmbc(0xdc); + REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c) + REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172) + REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6) + return; + case 'V': CASEMBC(0x1e7c) + regmbc('V'); REGMBC(0x1e7c) + return; + case 'W': CASEMBC(0x174) CASEMBC(0x1e80) CASEMBC(0x1e82) + CASEMBC(0x1e84) CASEMBC(0x1e86) + regmbc('W'); REGMBC(0x174) REGMBC(0x1e80) + REGMBC(0x1e82) REGMBC(0x1e84) REGMBC(0x1e86) + return; + case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c) + regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c) + return; + case 'Y': case 0xdd: + CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2) + CASEMBC(0x1ef6) CASEMBC(0x1ef8) + regmbc('Y'); regmbc(0xdd); + REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e) + REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8) + return; + case 'Z': CASEMBC(0x179) CASEMBC(0x17b) CASEMBC(0x17d) + CASEMBC(0x1b5) CASEMBC(0x1e90) CASEMBC(0x1e94) + regmbc('Z'); REGMBC(0x179) REGMBC(0x17b) + REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90) + REGMBC(0x1e94) + return; + case 'a': case 0xe0: case 0xe1: case 0xe2: + case 0xe3: case 0xe4: case 0xe5: + CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce) + CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3) + regmbc('a'); regmbc(0xe0); regmbc(0xe1); + regmbc(0xe2); regmbc(0xe3); regmbc(0xe4); + regmbc(0xe5); + REGMBC(0x101) REGMBC(0x103) REGMBC(0x105) + REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1) + REGMBC(0x1ea3) + return; + case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07) + regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07) + return; + case 'c': case 0xe7: + CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d) + regmbc('c'); regmbc(0xe7); + REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b) + REGMBC(0x10d) + return; + case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b) + CASEMBC(0x1e0f) CASEMBC(0x1e11) + regmbc('d'); REGMBC(0x10f) REGMBC(0x111) + REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11) + return; + case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb: + CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119) + CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd) + regmbc('e'); regmbc(0xe8); regmbc(0xe9); + regmbc(0xea); regmbc(0xeb); + REGMBC(0x113) REGMBC(0x115) REGMBC(0x117) + REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb) + REGMBC(0x1ebd) + return; + case 'f': CASEMBC(0x1e1f) + regmbc('f'); REGMBC(0x1e1f) + return; + case 'g': CASEMBC(0x11d) CASEMBC(0x11f) CASEMBC(0x121) + CASEMBC(0x123) CASEMBC(0x1e5) CASEMBC(0x1e7) CASEMBC(0x1f5) + CASEMBC(0x1e21) + regmbc('g'); REGMBC(0x11d) REGMBC(0x11f) + REGMBC(0x121) REGMBC(0x123) REGMBC(0x1e5) + REGMBC(0x1e7) REGMBC(0x1f5) REGMBC(0x1e21) + return; + case 'h': CASEMBC(0x125) CASEMBC(0x127) CASEMBC(0x1e23) + CASEMBC(0x1e27) CASEMBC(0x1e29) CASEMBC(0x1e96) + regmbc('h'); REGMBC(0x125) REGMBC(0x127) + REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29) + REGMBC(0x1e96) + return; + case 'i': case 0xec: case 0xed: case 0xee: case 0xef: + CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f) + CASEMBC(0x1d0) CASEMBC(0x1ec9) + regmbc('i'); regmbc(0xec); regmbc(0xed); + regmbc(0xee); regmbc(0xef); + REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d) + REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9) + return; + case 'j': CASEMBC(0x135) CASEMBC(0x1f0) + regmbc('j'); REGMBC(0x135) REGMBC(0x1f0) + return; + case 'k': CASEMBC(0x137) CASEMBC(0x1e9) CASEMBC(0x1e31) + CASEMBC(0x1e35) + regmbc('k'); REGMBC(0x137) REGMBC(0x1e9) + REGMBC(0x1e31) REGMBC(0x1e35) + return; + case 'l': CASEMBC(0x13a) CASEMBC(0x13c) CASEMBC(0x13e) + CASEMBC(0x140) CASEMBC(0x142) CASEMBC(0x1e3b) + regmbc('l'); REGMBC(0x13a) REGMBC(0x13c) + REGMBC(0x13e) REGMBC(0x140) REGMBC(0x142) + REGMBC(0x1e3b) + return; + case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41) + regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41) + return; + case 'n': case 0xf1: + CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149) + CASEMBC(0x1e45) CASEMBC(0x1e49) + regmbc('n'); regmbc(0xf1); + REGMBC(0x144) REGMBC(0x146) REGMBC(0x148) + REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49) + return; + case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5: + case 0xf6: case 0xf8: + CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1) + CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf) + regmbc('o'); regmbc(0xf2); regmbc(0xf3); + regmbc(0xf4); regmbc(0xf5); regmbc(0xf6); + regmbc(0xf8); + REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151) + REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb) + REGMBC(0x1ed) REGMBC(0x1ecf) + return; + case 'p': CASEMBC(0x1e55) CASEMBC(0x1e57) + regmbc('p'); REGMBC(0x1e55) REGMBC(0x1e57) + return; + case 'r': CASEMBC(0x155) CASEMBC(0x157) CASEMBC(0x159) + CASEMBC(0x1e59) CASEMBC(0x1e5f) + regmbc('r'); REGMBC(0x155) REGMBC(0x157) REGMBC(0x159) + REGMBC(0x1e59) REGMBC(0x1e5f) + return; + case 's': CASEMBC(0x15b) CASEMBC(0x15d) CASEMBC(0x15f) + CASEMBC(0x161) CASEMBC(0x1e61) + regmbc('s'); REGMBC(0x15b) REGMBC(0x15d) + REGMBC(0x15f) REGMBC(0x161) REGMBC(0x1e61) + return; + case 't': CASEMBC(0x163) CASEMBC(0x165) CASEMBC(0x167) + CASEMBC(0x1e6b) CASEMBC(0x1e6f) CASEMBC(0x1e97) + regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167) + REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97) + return; + case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc: + CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f) + CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4) + CASEMBC(0x1ee7) + regmbc('u'); regmbc(0xf9); regmbc(0xfa); + regmbc(0xfb); regmbc(0xfc); + REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d) + REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173) + REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7) + return; + case 'v': CASEMBC(0x1e7d) + regmbc('v'); REGMBC(0x1e7d) + return; + case 'w': CASEMBC(0x175) CASEMBC(0x1e81) CASEMBC(0x1e83) + CASEMBC(0x1e85) CASEMBC(0x1e87) CASEMBC(0x1e98) + regmbc('w'); REGMBC(0x175) REGMBC(0x1e81) + REGMBC(0x1e83) REGMBC(0x1e85) REGMBC(0x1e87) + REGMBC(0x1e98) + return; + case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d) + regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d) + return; + case 'y': case 0xfd: case 0xff: + CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99) + CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9) + regmbc('y'); regmbc(0xfd); regmbc(0xff); + REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99) + REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9) + return; + case 'z': CASEMBC(0x17a) CASEMBC(0x17c) CASEMBC(0x17e) + CASEMBC(0x1b6) CASEMBC(0x1e91) CASEMBC(0x1e95) + regmbc('z'); REGMBC(0x17a) REGMBC(0x17c) + REGMBC(0x17e) REGMBC(0x1b6) REGMBC(0x1e91) + REGMBC(0x1e95) + return; + } + } + regmbc(c); +} + + + +/* + * Emit a node. + * Return pointer to generated code. + */ +static char_u *regnode(int op) +{ + char_u *ret; + + ret = regcode; + if (ret == JUST_CALC_SIZE) + regsize += 3; + else { + *regcode++ = op; + *regcode++ = NUL; // Null "next" pointer. + *regcode++ = NUL; + } + return ret; +} + +/* + * Write a four bytes number at "p" and return pointer to the next char. + */ +static char_u *re_put_uint32(char_u *p, uint32_t val) +{ + *p++ = (char_u)((val >> 24) & 0377); + *p++ = (char_u)((val >> 16) & 0377); + *p++ = (char_u)((val >> 8) & 0377); + *p++ = (char_u)(val & 0377); + return p; +} + +/* + * regnext - dig the "next" pointer out of a node + * Returns NULL when calculating size, when there is no next item and when + * there is an error. + */ +static char_u *regnext(char_u *p) + FUNC_ATTR_NONNULL_ALL +{ + int offset; + + if (p == JUST_CALC_SIZE || reg_toolong) + return NULL; + + offset = NEXT(p); + if (offset == 0) + return NULL; + + if (OP(p) == BACK) + return p - offset; + else + return p + offset; +} + +// Set the next-pointer at the end of a node chain. +static void regtail(char_u *p, char_u *val) +{ + int offset; + + if (p == JUST_CALC_SIZE) { + return; + } + + // Find last node. + char_u *scan = p; + for (;; ) { + char_u *temp = regnext(scan); + if (temp == NULL) { + break; + } + scan = temp; + } + + if (OP(scan) == BACK) { + offset = (int)(scan - val); + } else { + offset = (int)(val - scan); + } + // When the offset uses more than 16 bits it can no longer fit in the two + // bytes available. Use a global flag to avoid having to check return + // values in too many places. + if (offset > 0xffff) { + reg_toolong = true; + } else { + *(scan + 1) = (char_u)(((unsigned)offset >> 8) & 0377); + *(scan + 2) = (char_u)(offset & 0377); + } +} + +/* + * Like regtail, on item after a BRANCH; nop if none. + */ +static void regoptail(char_u *p, char_u *val) +{ + // When op is neither BRANCH nor BRACE_COMPLEX0-9, it is "operandless" + if (p == NULL || p == JUST_CALC_SIZE + || (OP(p) != BRANCH + && (OP(p) < BRACE_COMPLEX || OP(p) > BRACE_COMPLEX + 9))) + return; + regtail(OPERAND(p), val); +} + + +/* + * Insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void reginsert(int op, char_u *opnd) +{ + char_u *src; + char_u *dst; + char_u *place; + + if (regcode == JUST_CALC_SIZE) { + regsize += 3; + return; + } + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; // Op node, where operand used to be. + *place++ = op; + *place++ = NUL; + *place = NUL; +} + +/* + * Insert an operator in front of already-emitted operand. + * Add a number to the operator. + */ +static void reginsert_nr(int op, long val, char_u *opnd) +{ + char_u *src; + char_u *dst; + char_u *place; + + if (regcode == JUST_CALC_SIZE) { + regsize += 7; + return; + } + src = regcode; + regcode += 7; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; // Op node, where operand used to be. + *place++ = op; + *place++ = NUL; + *place++ = NUL; + assert(val >= 0 && (uintmax_t)val <= UINT32_MAX); + re_put_uint32(place, (uint32_t)val); +} + +/* + * Insert an operator in front of already-emitted operand. + * The operator has the given limit values as operands. Also set next pointer. + * + * Means relocating the operand. + */ +static void reginsert_limits(int op, long minval, long maxval, char_u *opnd) +{ + char_u *src; + char_u *dst; + char_u *place; + + if (regcode == JUST_CALC_SIZE) { + regsize += 11; + return; + } + src = regcode; + regcode += 11; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; // Op node, where operand used to be. + *place++ = op; + *place++ = NUL; + *place++ = NUL; + assert(minval >= 0 && (uintmax_t)minval <= UINT32_MAX); + place = re_put_uint32(place, (uint32_t)minval); + assert(maxval >= 0 && (uintmax_t)maxval <= UINT32_MAX); + place = re_put_uint32(place, (uint32_t)maxval); + regtail(opnd, place); +} + +/// Return true if the back reference is legal. We must have seen the close +/// brace. +/// TODO(vim): Should also check that we don't refer to something repeated +/// (+*=): what instance of the repetition should we match? +static int seen_endbrace(int refnum) +{ + if (!had_endbrace[refnum]) { + char_u *p; + + // Trick: check if "@<=" or "@'): + ret = regnode(EOW); + break; + + case Magic('_'): + c = no_Magic(getchr()); + if (c == '^') { // "\_^" is start-of-line + ret = regnode(BOL); + break; + } + if (c == '$') { // "\_$" is end-of-line + ret = regnode(EOL); + had_eol = true; + break; + } + + extra = ADD_NL; + *flagp |= HASNL; + + // "\_[" is character range plus newline + if (c == '[') + goto collection; + + // "\_x" is character class plus newline + FALLTHROUGH; + + // Character classes. + case Magic('.'): + case Magic('i'): + case Magic('I'): + case Magic('k'): + case Magic('K'): + case Magic('f'): + case Magic('F'): + case Magic('p'): + case Magic('P'): + case Magic('s'): + case Magic('S'): + case Magic('d'): + case Magic('D'): + case Magic('x'): + case Magic('X'): + case Magic('o'): + case Magic('O'): + case Magic('w'): + case Magic('W'): + case Magic('h'): + case Magic('H'): + case Magic('a'): + case Magic('A'): + case Magic('l'): + case Magic('L'): + case Magic('u'): + case Magic('U'): + p = vim_strchr(classchars, no_Magic(c)); + if (p == NULL) + EMSG_RET_NULL(_("E63: invalid use of \\_")); + // When '.' is followed by a composing char ignore the dot, so that + // the composing char is matched here. + if (c == Magic('.') && utf_iscomposing(peekchr())) { + c = getchr(); + goto do_multibyte; + } + ret = regnode(classcodes[p - classchars] + extra); + *flagp |= HASWIDTH | SIMPLE; + break; + + case Magic('n'): + if (reg_string) { + // In a string "\n" matches a newline character. + ret = regnode(EXACTLY); + regc(NL); + regc(NUL); + *flagp |= HASWIDTH | SIMPLE; + } else { + // In buffer text "\n" matches the end of a line. + ret = regnode(NEWL); + *flagp |= HASWIDTH | HASNL; + } + break; + + case Magic('('): + if (one_exactly) + EMSG_ONE_RET_NULL; + ret = reg(REG_PAREN, &flags); + if (ret == NULL) + return NULL; + *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH); + break; + + case NUL: + case Magic('|'): + case Magic('&'): + case Magic(')'): + if (one_exactly) + EMSG_ONE_RET_NULL; + IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier. + // NOTREACHED + + case Magic('='): + case Magic('?'): + case Magic('+'): + case Magic('@'): + case Magic('{'): + case Magic('*'): + c = no_Magic(c); + EMSG3_RET_NULL(_("E64: %s%c follows nothing"), + (c == '*' ? reg_magic >= MAGIC_ON : reg_magic == MAGIC_ALL), c); + // NOTREACHED + + case Magic('~'): // previous substitute pattern + if (reg_prev_sub != NULL) { + char_u *lp; + + ret = regnode(EXACTLY); + lp = reg_prev_sub; + while (*lp != NUL) + regc(*lp++); + regc(NUL); + if (*reg_prev_sub != NUL) { + *flagp |= HASWIDTH; + if ((lp - reg_prev_sub) == 1) + *flagp |= SIMPLE; + } + } else + EMSG_RET_NULL(_(e_nopresub)); + break; + + case Magic('1'): + case Magic('2'): + case Magic('3'): + case Magic('4'): + case Magic('5'): + case Magic('6'): + case Magic('7'): + case Magic('8'): + case Magic('9'): + { + int refnum; + + refnum = c - Magic('0'); + if (!seen_endbrace(refnum)) { + return NULL; + } + ret = regnode(BACKREF + refnum); + } + break; + + case Magic('z'): + { + c = no_Magic(getchr()); + switch (c) { + case '(': if ((reg_do_extmatch & REX_SET) == 0) + EMSG_RET_NULL(_(e_z_not_allowed)); + if (one_exactly) + EMSG_ONE_RET_NULL; + ret = reg(REG_ZPAREN, &flags); + if (ret == NULL) + return NULL; + *flagp |= flags & (HASWIDTH|SPSTART|HASNL|HASLOOKBH); + re_has_z = REX_SET; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': if ((reg_do_extmatch & REX_USE) == 0) + EMSG_RET_NULL(_(e_z1_not_allowed)); + ret = regnode(ZREF + c - '0'); + re_has_z = REX_USE; + break; + + case 's': ret = regnode(MOPEN + 0); + if (!re_mult_next("\\zs")) { + return NULL; + } + break; + + case 'e': ret = regnode(MCLOSE + 0); + if (!re_mult_next("\\ze")) { + return NULL; + } + break; + + default: EMSG_RET_NULL(_("E68: Invalid character after \\z")); + } + } + break; + + case Magic('%'): + { + c = no_Magic(getchr()); + switch (c) { + // () without a back reference + case '(': + if (one_exactly) + EMSG_ONE_RET_NULL; + ret = reg(REG_NPAREN, &flags); + if (ret == NULL) + return NULL; + *flagp |= flags & (HASWIDTH | SPSTART | HASNL | HASLOOKBH); + break; + + // Catch \%^ and \%$ regardless of where they appear in the + // pattern -- regardless of whether or not it makes sense. + case '^': + ret = regnode(RE_BOF); + break; + + case '$': + ret = regnode(RE_EOF); + break; + + case '#': + ret = regnode(CURSOR); + break; + + case 'V': + ret = regnode(RE_VISUAL); + break; + + case 'C': + ret = regnode(RE_COMPOSING); + break; + + // \%[abc]: Emit as a list of branches, all ending at the last + // branch which matches nothing. + case '[': + if (one_exactly) // doesn't nest + EMSG_ONE_RET_NULL; + { + char_u *lastbranch; + char_u *lastnode = NULL; + char_u *br; + + ret = NULL; + while ((c = getchr()) != ']') { + if (c == NUL) + EMSG2_RET_NULL(_(e_missing_sb), + reg_magic == MAGIC_ALL); + br = regnode(BRANCH); + if (ret == NULL) { + ret = br; + } else { + regtail(lastnode, br); + if (reg_toolong) { + return NULL; + } + } + + ungetchr(); + one_exactly = true; + lastnode = regatom(flagp); + one_exactly = false; + if (lastnode == NULL) { + return NULL; + } + } + if (ret == NULL) + EMSG2_RET_NULL(_(e_empty_sb), + reg_magic == MAGIC_ALL); + lastbranch = regnode(BRANCH); + br = regnode(NOTHING); + if (ret != JUST_CALC_SIZE) { + regtail(lastnode, br); + regtail(lastbranch, br); + // connect all branches to the NOTHING + // branch at the end + for (br = ret; br != lastnode; ) { + if (OP(br) == BRANCH) { + regtail(br, lastbranch); + if (reg_toolong) { + return NULL; + } + br = OPERAND(br); + } else + br = regnext(br); + } + } + *flagp &= ~(HASWIDTH | SIMPLE); + break; + } + + case 'd': // %d123 decimal + case 'o': // %o123 octal + case 'x': // %xab hex 2 + case 'u': // %uabcd hex 4 + case 'U': // %U1234abcd hex 8 + { + int64_t i; + + switch (c) { + case 'd': i = getdecchrs(); break; + case 'o': i = getoctchrs(); break; + case 'x': i = gethexchrs(2); break; + case 'u': i = gethexchrs(4); break; + case 'U': i = gethexchrs(8); break; + default: i = -1; break; + } + + if (i < 0 || i > INT_MAX) { + EMSG2_RET_NULL(_("E678: Invalid character after %s%%[dxouU]"), + reg_magic == MAGIC_ALL); + } + if (use_multibytecode(i)) { + ret = regnode(MULTIBYTECODE); + } else { + ret = regnode(EXACTLY); + } + if (i == 0) { + regc(0x0a); + } else { + regmbc(i); + } + regc(NUL); + *flagp |= HASWIDTH; + break; + } + + default: + if (ascii_isdigit(c) || c == '<' || c == '>' + || c == '\'') { + uint32_t n = 0; + int cmp; + + cmp = c; + if (cmp == '<' || cmp == '>') + c = getchr(); + while (ascii_isdigit(c)) { + n = n * 10 + (uint32_t)(c - '0'); + c = getchr(); + } + if (c == '\'' && n == 0) { + // "\%'m", "\%<'m" and "\%>'m": Mark + c = getchr(); + ret = regnode(RE_MARK); + if (ret == JUST_CALC_SIZE) + regsize += 2; + else { + *regcode++ = c; + *regcode++ = cmp; + } + break; + } else if (c == 'l' || c == 'c' || c == 'v') { + if (c == 'l') { + ret = regnode(RE_LNUM); + if (save_prev_at_start) { + at_start = true; + } + } else if (c == 'c') { + ret = regnode(RE_COL); + } else { + ret = regnode(RE_VCOL); + } + if (ret == JUST_CALC_SIZE) { + regsize += 5; + } else { + // put the number and the optional + // comparator after the opcode + regcode = re_put_uint32(regcode, n); + *regcode++ = cmp; + } + break; + } + } + + EMSG2_RET_NULL(_("E71: Invalid character after %s%%"), + reg_magic == MAGIC_ALL); + } + } + break; + + case Magic('['): +collection: + { + char_u *lp; + + // If there is no matching ']', we assume the '[' is a normal + // character. This makes 'incsearch' and ":help [" work. + lp = skip_anyof(regparse); + if (*lp == ']') { // there is a matching ']' + int startc = -1; // > 0 when next '-' is a range + int endc; + + // In a character class, different parsing rules apply. + // Not even \ is special anymore, nothing is. + if (*regparse == '^') { // Complement of range. + ret = regnode(ANYBUT + extra); + regparse++; + } else + ret = regnode(ANYOF + extra); + + // At the start ']' and '-' mean the literal character. + if (*regparse == ']' || *regparse == '-') { + startc = *regparse; + regc(*regparse++); + } + + while (*regparse != NUL && *regparse != ']') { + if (*regparse == '-') { + ++regparse; + // The '-' is not used for a range at the end and + // after or before a '\n'. + if (*regparse == ']' || *regparse == NUL + || startc == -1 + || (regparse[0] == '\\' && regparse[1] == 'n')) { + regc('-'); + startc = '-'; // [--x] is a range + } else { + // Also accept "a-[.z.]" + endc = 0; + if (*regparse == '[') + endc = get_coll_element(®parse); + if (endc == 0) { + endc = mb_ptr2char_adv((const char_u **)®parse); + } + + // Handle \o40, \x20 and \u20AC style sequences + if (endc == '\\' && !reg_cpo_lit) + endc = coll_get_char(); + + if (startc > endc) { + EMSG_RET_NULL(_(e_reverse_range)); + } + if (utf_char2len(startc) > 1 + || utf_char2len(endc) > 1) { + // Limit to a range of 256 chars + if (endc > startc + 256) { + EMSG_RET_NULL(_(e_large_class)); + } + while (++startc <= endc) { + regmbc(startc); + } + } else { + while (++startc <= endc) + regc(startc); + } + startc = -1; + } + } + // Only "\]", "\^", "\]" and "\\" are special in Vi. Vim + // accepts "\t", "\e", etc., but only when the 'l' flag in + // 'cpoptions' is not included. + else if (*regparse == '\\' + && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL + || (!reg_cpo_lit + && vim_strchr(REGEXP_ABBR, + regparse[1]) != NULL))) { + regparse++; + if (*regparse == 'n') { + // '\n' in range: also match NL + if (ret != JUST_CALC_SIZE) { + // Using \n inside [^] does not change what + // matches. "[^\n]" is the same as ".". + if (*ret == ANYOF) { + *ret = ANYOF + ADD_NL; + *flagp |= HASNL; + } + // else: must have had a \n already + } + regparse++; + startc = -1; + } else if (*regparse == 'd' + || *regparse == 'o' + || *regparse == 'x' + || *regparse == 'u' + || *regparse == 'U') { + startc = coll_get_char(); + if (startc == 0) + regc(0x0a); + else + regmbc(startc); + } else { + startc = backslash_trans(*regparse++); + regc(startc); + } + } else if (*regparse == '[') { + int c_class; + int cu; + + c_class = get_char_class(®parse); + startc = -1; + // Characters assumed to be 8 bits! + switch (c_class) { + case CLASS_NONE: + c_class = get_equi_class(®parse); + if (c_class != 0) { + // produce equivalence class + reg_equi_class(c_class); + } else if ((c_class = + get_coll_element(®parse)) != 0) { + // produce a collating element + regmbc(c_class); + } else { + // literal '[', allow [[-x] as a range + startc = *regparse++; + regc(startc); + } + break; + case CLASS_ALNUM: + for (cu = 1; cu < 128; cu++) { + if (isalnum(cu)) { + regmbc(cu); + } + } + break; + case CLASS_ALPHA: + for (cu = 1; cu < 128; cu++) { + if (isalpha(cu)) { + regmbc(cu); + } + } + break; + case CLASS_BLANK: + regc(' '); + regc('\t'); + break; + case CLASS_CNTRL: + for (cu = 1; cu <= 127; cu++) { + if (iscntrl(cu)) { + regmbc(cu); + } + } + break; + case CLASS_DIGIT: + for (cu = 1; cu <= 127; cu++) { + if (ascii_isdigit(cu)) { + regmbc(cu); + } + } + break; + case CLASS_GRAPH: + for (cu = 1; cu <= 127; cu++) { + if (isgraph(cu)) { + regmbc(cu); + } + } + break; + case CLASS_LOWER: + for (cu = 1; cu <= 255; cu++) { + if (mb_islower(cu) && cu != 170 && cu != 186) { + regmbc(cu); + } + } + break; + case CLASS_PRINT: + for (cu = 1; cu <= 255; cu++) { + if (vim_isprintc(cu)) { + regmbc(cu); + } + } + break; + case CLASS_PUNCT: + for (cu = 1; cu < 128; cu++) { + if (ispunct(cu)) { + regmbc(cu); + } + } + break; + case CLASS_SPACE: + for (cu = 9; cu <= 13; cu++) + regc(cu); + regc(' '); + break; + case CLASS_UPPER: + for (cu = 1; cu <= 255; cu++) { + if (mb_isupper(cu)) { + regmbc(cu); + } + } + break; + case CLASS_XDIGIT: + for (cu = 1; cu <= 255; cu++) { + if (ascii_isxdigit(cu)) { + regmbc(cu); + } + } + break; + case CLASS_TAB: + regc('\t'); + break; + case CLASS_RETURN: + regc('\r'); + break; + case CLASS_BACKSPACE: + regc('\b'); + break; + case CLASS_ESCAPE: + regc(ESC); + break; + case CLASS_IDENT: + for (cu = 1; cu <= 255; cu++) { + if (vim_isIDc(cu)) { + regmbc(cu); + } + } + break; + case CLASS_KEYWORD: + for (cu = 1; cu <= 255; cu++) { + if (reg_iswordc(cu)) { + regmbc(cu); + } + } + break; + case CLASS_FNAME: + for (cu = 1; cu <= 255; cu++) { + if (vim_isfilec(cu)) { + regmbc(cu); + } + } + break; + } + } else { + // produce a multibyte character, including any + // following composing characters. + startc = utf_ptr2char(regparse); + int len = utfc_ptr2len(regparse); + if (utf_char2len(startc) != len) { + // composing chars + startc = -1; + } + while (--len >= 0) { + regc(*regparse++); + } + } + } + regc(NUL); + prevchr_len = 1; // last char was the ']' + if (*regparse != ']') + EMSG_RET_NULL(_(e_toomsbra)); // Cannot happen? + skipchr(); // let's be friends with the lexer again + *flagp |= HASWIDTH | SIMPLE; + break; + } else if (reg_strict) + EMSG2_RET_NULL(_(e_missingbracket), reg_magic > MAGIC_OFF); + } + FALLTHROUGH; + + default: + { + int len; + + // A multi-byte character is handled as a separate atom if it's + // before a multi and when it's a composing char. + if (use_multibytecode(c)) { +do_multibyte: + ret = regnode(MULTIBYTECODE); + regmbc(c); + *flagp |= HASWIDTH | SIMPLE; + break; + } + + ret = regnode(EXACTLY); + + // Append characters as long as: + // - there is no following multi, we then need the character in + // front of it as a single character operand + // - not running into a Magic character + // - "one_exactly" is not set + // But always emit at least one character. Might be a Multi, + // e.g., a "[" without matching "]". + for (len = 0; c != NUL && (len == 0 + || (re_multi_type(peekchr()) == NOT_MULTI + && !one_exactly + && !is_Magic(c))); ++len) { + c = no_Magic(c); + { + regmbc(c); + { + int l; + + // Need to get composing character too. + for (;; ) { + l = utf_ptr2len(regparse); + if (!utf_composinglike(regparse, regparse + l)) { + break; + } + regmbc(utf_ptr2char(regparse)); + skipchr(); + } + } + } + c = getchr(); + } + ungetchr(); + + regc(NUL); + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + } + break; + } + + return ret; +} + +/* + * Parse something followed by possible [*+=]. + * + * Note that the branching code sequences used for = and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char_u *regpiece(int *flagp) +{ + char_u *ret; + int op; + char_u *next; + int flags; + long minval; + long maxval; + + ret = regatom(&flags); + if (ret == NULL) + return NULL; + + op = peekchr(); + if (re_multi_type(op) == NOT_MULTI) { + *flagp = flags; + return ret; + } + // default flags + *flagp = (WORST | SPSTART | (flags & (HASNL | HASLOOKBH))); + + skipchr(); + switch (op) { + case Magic('*'): + if (flags & SIMPLE) + reginsert(STAR, ret); + else { + // Emit x* as (x&|), where & means "self". + reginsert(BRANCH, ret); // Either x + regoptail(ret, regnode(BACK)); // and loop + regoptail(ret, ret); // back + regtail(ret, regnode(BRANCH)); // or + regtail(ret, regnode(NOTHING)); // null. + } + break; + + case Magic('+'): + if (flags & SIMPLE) + reginsert(PLUS, ret); + else { + // Emit x+ as x(&|), where & means "self". + next = regnode(BRANCH); // Either + regtail(ret, next); + regtail(regnode(BACK), ret); // loop back + regtail(next, regnode(BRANCH)); // or + regtail(ret, regnode(NOTHING)); // null. + } + *flagp = (WORST | HASWIDTH | (flags & (HASNL | HASLOOKBH))); + break; + + case Magic('@'): + { + int lop = END; + int64_t nr = getdecchrs(); + + switch (no_Magic(getchr())) { + case '=': lop = MATCH; break; // \@= + case '!': lop = NOMATCH; break; // \@! + case '>': lop = SUBPAT; break; // \@> + case '<': switch (no_Magic(getchr())) { + case '=': lop = BEHIND; break; // \@<= + case '!': lop = NOBEHIND; break; // \@= 10) + EMSG2_RET_NULL(_("E60: Too many complex %s{...}s"), + reg_magic == MAGIC_ALL); + reginsert(BRACE_COMPLEX + num_complex_braces, ret); + regoptail(ret, regnode(BACK)); + regoptail(ret, ret); + reginsert_limits(BRACE_LIMITS, minval, maxval, ret); + ++num_complex_braces; + } + if (minval > 0 && maxval > 0) + *flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH))); + break; + } + if (re_multi_type(peekchr()) != NOT_MULTI) { + // Can't have a multi follow a multi. + if (peekchr() == Magic('*')) { + EMSG2_RET_NULL(_("E61: Nested %s*"), reg_magic >= MAGIC_ON); + } + EMSG3_RET_NULL(_("E62: Nested %s%c"), reg_magic == MAGIC_ALL, no_Magic(peekchr())); + } + + return ret; +} + +/* + * Parse one alternative of an | or & operator. + * Implements the concatenation operator. + */ +static char_u *regconcat(int *flagp) +{ + char_u *first = NULL; + char_u *chain = NULL; + char_u *latest; + int flags; + int cont = true; + + *flagp = WORST; // Tentatively. + + while (cont) { + switch (peekchr()) { + case NUL: + case Magic('|'): + case Magic('&'): + case Magic(')'): + cont = false; + break; + case Magic('Z'): + regflags |= RF_ICOMBINE; + skipchr_keepstart(); + break; + case Magic('c'): + regflags |= RF_ICASE; + skipchr_keepstart(); + break; + case Magic('C'): + regflags |= RF_NOICASE; + skipchr_keepstart(); + break; + case Magic('v'): + reg_magic = MAGIC_ALL; + skipchr_keepstart(); + curchr = -1; + break; + case Magic('m'): + reg_magic = MAGIC_ON; + skipchr_keepstart(); + curchr = -1; + break; + case Magic('M'): + reg_magic = MAGIC_OFF; + skipchr_keepstart(); + curchr = -1; + break; + case Magic('V'): + reg_magic = MAGIC_NONE; + skipchr_keepstart(); + curchr = -1; + break; + default: + latest = regpiece(&flags); + if (latest == NULL || reg_toolong) + return NULL; + *flagp |= flags & (HASWIDTH | HASNL | HASLOOKBH); + if (chain == NULL) // First piece. + *flagp |= flags & SPSTART; + else + regtail(chain, latest); + chain = latest; + if (first == NULL) + first = latest; + break; + } + } + if (first == NULL) // Loop ran zero times. + first = regnode(NOTHING); + return first; +} + +/* + * Parse one alternative of an | operator. + * Implements the & operator. + */ +static char_u *regbranch(int *flagp) +{ + char_u *ret; + char_u *chain = NULL; + char_u *latest; + int flags; + + *flagp = WORST | HASNL; // Tentatively. + + ret = regnode(BRANCH); + for (;; ) { + latest = regconcat(&flags); + if (latest == NULL) + return NULL; + // If one of the branches has width, the whole thing has. If one of + // the branches anchors at start-of-line, the whole thing does. + // If one of the branches uses look-behind, the whole thing does. + *flagp |= flags & (HASWIDTH | SPSTART | HASLOOKBH); + // If one of the branches doesn't match a line-break, the whole thing + // doesn't. + *flagp &= ~HASNL | (flags & HASNL); + if (chain != NULL) + regtail(chain, latest); + if (peekchr() != Magic('&')) + break; + skipchr(); + regtail(latest, regnode(END)); // operand ends + if (reg_toolong) + break; + reginsert(MATCH, latest); + chain = latest; + } + + return ret; +} + +/* + * Parse regular expression, i.e. main body or parenthesized thing. + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char_u *reg( + int paren, // REG_NOPAREN, REG_PAREN, REG_NPAREN or REG_ZPAREN + int *flagp) +{ + char_u *ret; + char_u *br; + char_u *ender; + int parno = 0; + int flags; + + *flagp = HASWIDTH; // Tentatively. + + if (paren == REG_ZPAREN) { + // Make a ZOPEN node. + if (regnzpar >= NSUBEXP) + EMSG_RET_NULL(_("E50: Too many \\z(")); + parno = regnzpar; + regnzpar++; + ret = regnode(ZOPEN + parno); + } else if (paren == REG_PAREN) { + // Make a MOPEN node. + if (regnpar >= NSUBEXP) + EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL); + parno = regnpar; + ++regnpar; + ret = regnode(MOPEN + parno); + } else if (paren == REG_NPAREN) { + // Make a NOPEN node. + ret = regnode(NOPEN); + } else + ret = NULL; + + // Pick up the branches, linking them together. + br = regbranch(&flags); + if (br == NULL) + return NULL; + if (ret != NULL) + regtail(ret, br); // [MZ]OPEN -> first. + else + ret = br; + // If one of the branches can be zero-width, the whole thing can. + // If one of the branches has * at start or matches a line-break, the + // whole thing can. + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & (SPSTART | HASNL | HASLOOKBH); + while (peekchr() == Magic('|')) { + skipchr(); + br = regbranch(&flags); + if (br == NULL || reg_toolong) + return NULL; + regtail(ret, br); // BRANCH -> BRANCH. + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & (SPSTART | HASNL | HASLOOKBH); + } + + // Make a closing node, and hook it on the end. + ender = regnode( + paren == REG_ZPAREN ? ZCLOSE + parno : + paren == REG_PAREN ? MCLOSE + parno : + paren == REG_NPAREN ? NCLOSE : END); + regtail(ret, ender); + + // Hook the tails of the branches to the closing node. + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + // Check for proper termination. + if (paren != REG_NOPAREN && getchr() != Magic(')')) { + if (paren == REG_ZPAREN) + EMSG_RET_NULL(_("E52: Unmatched \\z(")); + else if (paren == REG_NPAREN) + EMSG2_RET_NULL(_(e_unmatchedpp), reg_magic == MAGIC_ALL); + else + EMSG2_RET_NULL(_(e_unmatchedp), reg_magic == MAGIC_ALL); + } else if (paren == REG_NOPAREN && peekchr() != NUL) { + if (curchr == Magic(')')) + EMSG2_RET_NULL(_(e_unmatchedpar), reg_magic == MAGIC_ALL); + else + EMSG_RET_NULL(_(e_trailing)); // "Can't happen". + // NOTREACHED + } + // Here we set the flag allowing back references to this set of + // parentheses. + if (paren == REG_PAREN) { + had_endbrace[parno] = true; // have seen the close paren + } + return ret; +} + + +/* + * bt_regcomp() - compile a regular expression into internal code for the + * traditional back track matcher. + * Returns the program in allocated space. Returns NULL for an error. + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Whether upper/lower case is to be ignored is decided when executing the + * program, it does not matter here. + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + * "re_flags": RE_MAGIC and/or RE_STRING. + */ +static regprog_T *bt_regcomp(char_u *expr, int re_flags) +{ + char_u *scan; + char_u *longest; + int len; + int flags; + + if (expr == NULL) { + IEMSG_RET_NULL(_(e_null)); + } + + init_class_tab(); + + // First pass: determine size, legality. + regcomp_start(expr, re_flags); + regcode = JUST_CALC_SIZE; + regc(REGMAGIC); + if (reg(REG_NOPAREN, &flags) == NULL) + return NULL; + + // Allocate space. + bt_regprog_T *r = xmalloc(sizeof(bt_regprog_T) + regsize); + r->re_in_use = false; + + // Second pass: emit code. + regcomp_start(expr, re_flags); + regcode = r->program; + regc(REGMAGIC); + if (reg(REG_NOPAREN, &flags) == NULL || reg_toolong) { + xfree(r); + if (reg_toolong) + EMSG_RET_NULL(_("E339: Pattern too long")); + return NULL; + } + + // Dig out information for optimizations. + r->regstart = NUL; // Worst-case defaults. + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + r->regflags = regflags; + if (flags & HASNL) + r->regflags |= RF_HASNL; + if (flags & HASLOOKBH) + r->regflags |= RF_LOOKBH; + // Remember whether this pattern has any \z specials in it. + r->reghasz = re_has_z; + scan = r->program + 1; // First BRANCH. + if (OP(regnext(scan)) == END) { // Only one top-level choice. + scan = OPERAND(scan); + + // Starting-point info. + if (OP(scan) == BOL || OP(scan) == RE_BOF) { + r->reganch++; + scan = regnext(scan); + } + + if (OP(scan) == EXACTLY) { + r->regstart = utf_ptr2char(OPERAND(scan)); + } else if (OP(scan) == BOW + || OP(scan) == EOW + || OP(scan) == NOTHING + || OP(scan) == MOPEN + 0 || OP(scan) == NOPEN + || OP(scan) == MCLOSE + 0 || OP(scan) == NCLOSE) { + char_u *regnext_scan = regnext(scan); + if (OP(regnext_scan) == EXACTLY) { + r->regstart = utf_ptr2char(OPERAND(regnext_scan)); + } + } + + // If there's something expensive in the r.e., find the longest + // literal string that must appear and make it the regmust. Resolve + // ties in favor of later strings, since the regstart check works + // with the beginning of the r.e. and avoiding duplication + // strengthens checking. Not a strong reason, but sufficient in the + // absence of others. + + // When the r.e. starts with BOW, it is faster to look for a regmust + // first. Used a lot for "#" and "*" commands. (Added by mool). + if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW) + && !(flags & HASNL)) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) { + longest = OPERAND(scan); + len = (int)STRLEN(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } +#ifdef BT_REGEXP_DUMP + regdump(expr, r); +#endif + r->engine = &bt_regengine; + return (regprog_T *)r; +} + +/* + * Check if during the previous call to vim_regcomp the EOL item "$" has been + * found. This is messy, but it works fine. + */ +int vim_regcomp_had_eol(void) +{ + return had_eol; +} + +/* + * Get a number after a backslash that is inside []. + * When nothing is recognized return a backslash. + */ +static int coll_get_char(void) +{ + int64_t nr = -1; + + switch (*regparse++) { + case 'd': nr = getdecchrs(); break; + case 'o': nr = getoctchrs(); break; + case 'x': nr = gethexchrs(2); break; + case 'u': nr = gethexchrs(4); break; + case 'U': nr = gethexchrs(8); break; + } + if (nr < 0 || nr > INT_MAX) { + // If getting the number fails be backwards compatible: the character + // is a backslash. + regparse--; + nr = '\\'; + } + return nr; +} + +/* + * Free a compiled regexp program, returned by bt_regcomp(). + */ +static void bt_regfree(regprog_T *prog) +{ + xfree(prog); +} + +#define ADVANCE_REGINPUT() MB_PTR_ADV(rex.input) + +/* + * The arguments from BRACE_LIMITS are stored here. They are actually local + * to regmatch(), but they are here to reduce the amount of stack space used + * (it can be called recursively many times). + */ +static long bl_minval; +static long bl_maxval; + +// Save the input line and position in a regsave_T. +static void reg_save(regsave_T *save, garray_T *gap) + FUNC_ATTR_NONNULL_ALL +{ + if (REG_MULTI) { + save->rs_u.pos.col = (colnr_T)(rex.input - rex.line); + save->rs_u.pos.lnum = rex.lnum; + } else { + save->rs_u.ptr = rex.input; + } + save->rs_len = gap->ga_len; +} + +// Restore the input line and position from a regsave_T. +static void reg_restore(regsave_T *save, garray_T *gap) + FUNC_ATTR_NONNULL_ALL +{ + if (REG_MULTI) { + if (rex.lnum != save->rs_u.pos.lnum) { + // only call reg_getline() when the line number changed to save + // a bit of time + rex.lnum = save->rs_u.pos.lnum; + rex.line = reg_getline(rex.lnum); + } + rex.input = rex.line + save->rs_u.pos.col; + } else { + rex.input = save->rs_u.ptr; + } + gap->ga_len = save->rs_len; +} + +// Return true if current position is equal to saved position. +static bool reg_save_equal(const regsave_T *save) + FUNC_ATTR_NONNULL_ALL +{ + if (REG_MULTI) { + return rex.lnum == save->rs_u.pos.lnum + && rex.input == rex.line + save->rs_u.pos.col; + } + return rex.input == save->rs_u.ptr; +} + +// Save the sub-expressions before attempting a match. +#define save_se(savep, posp, pp) \ + REG_MULTI ? save_se_multi((savep), (posp)) : save_se_one((savep), (pp)) + +// After a failed match restore the sub-expressions. +#define restore_se(savep, posp, pp) { \ + if (REG_MULTI) \ + *(posp) = (savep)->se_u.pos; \ + else \ + *(pp) = (savep)->se_u.ptr; } + +/* + * Tentatively set the sub-expression start to the current position (after + * calling regmatch() they will have changed). Need to save the existing + * values for when there is no match. + * Use se_save() to use pointer (save_se_multi()) or position (save_se_one()), + * depending on REG_MULTI. + */ +static void save_se_multi(save_se_T *savep, lpos_T *posp) +{ + savep->se_u.pos = *posp; + posp->lnum = rex.lnum; + posp->col = (colnr_T)(rex.input - rex.line); +} + +static void save_se_one(save_se_T *savep, char_u **pp) +{ + savep->se_u.ptr = *pp; + *pp = rex.input; +} + +/* + * regrepeat - repeatedly match something simple, return how many. + * Advances rex.input (and rex.lnum) to just after the matched chars. + */ + static int +regrepeat( + char_u *p, + long maxcount) // maximum number of matches allowed +{ + long count = 0; + char_u *opnd; + int mask; + int testval = 0; + + char_u *scan = rex.input; // Make local copy of rex.input for speed. + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + case ANY + ADD_NL: + while (count < maxcount) { + // Matching anything means we continue until end-of-line (or + // end-of-file for ANY + ADD_NL), only limited by maxcount. + while (*scan != NUL && count < maxcount) { + count++; + MB_PTR_ADV(scan); + } + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr || count == maxcount) { + break; + } + count++; // count the line-break + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } + break; + + case IDENT: + case IDENT + ADD_NL: + testval = 1; + FALLTHROUGH; + case SIDENT: + case SIDENT + ADD_NL: + while (count < maxcount) { + if (vim_isIDc(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) { + MB_PTR_ADV(scan); + } else if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { + break; + } + ++count; + } + break; + + case KWORD: + case KWORD + ADD_NL: + testval = 1; + FALLTHROUGH; + case SKWORD: + case SKWORD + ADD_NL: + while (count < maxcount) { + if (vim_iswordp_buf(scan, rex.reg_buf) + && (testval || !ascii_isdigit(*scan))) { + MB_PTR_ADV(scan); + } else if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { + break; + } + count++; + } + break; + + case FNAME: + case FNAME + ADD_NL: + testval = 1; + FALLTHROUGH; + case SFNAME: + case SFNAME + ADD_NL: + while (count < maxcount) { + if (vim_isfilec(utf_ptr2char(scan)) && (testval || !ascii_isdigit(*scan))) { + MB_PTR_ADV(scan); + } else if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { + break; + } + count++; + } + break; + + case PRINT: + case PRINT + ADD_NL: + testval = 1; + FALLTHROUGH; + case SPRINT: + case SPRINT + ADD_NL: + while (count < maxcount) { + if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if (vim_isprintc(utf_ptr2char(scan)) == 1 + && (testval || !ascii_isdigit(*scan))) { + MB_PTR_ADV(scan); + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { + break; + } + count++; + } + break; + + case WHITE: + case WHITE + ADD_NL: + testval = mask = RI_WHITE; +do_class: + while (count < maxcount) { + int l; + if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if ((l = utfc_ptr2len(scan)) > 1) { + if (testval != 0) { + break; + } + scan += l; + } else if ((class_tab[*scan] & mask) == testval) { + scan++; + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { + break; + } + ++count; + } + break; + + case NWHITE: + case NWHITE + ADD_NL: + mask = RI_WHITE; + goto do_class; + case DIGIT: + case DIGIT + ADD_NL: + testval = mask = RI_DIGIT; + goto do_class; + case NDIGIT: + case NDIGIT + ADD_NL: + mask = RI_DIGIT; + goto do_class; + case HEX: + case HEX + ADD_NL: + testval = mask = RI_HEX; + goto do_class; + case NHEX: + case NHEX + ADD_NL: + mask = RI_HEX; + goto do_class; + case OCTAL: + case OCTAL + ADD_NL: + testval = mask = RI_OCTAL; + goto do_class; + case NOCTAL: + case NOCTAL + ADD_NL: + mask = RI_OCTAL; + goto do_class; + case WORD: + case WORD + ADD_NL: + testval = mask = RI_WORD; + goto do_class; + case NWORD: + case NWORD + ADD_NL: + mask = RI_WORD; + goto do_class; + case HEAD: + case HEAD + ADD_NL: + testval = mask = RI_HEAD; + goto do_class; + case NHEAD: + case NHEAD + ADD_NL: + mask = RI_HEAD; + goto do_class; + case ALPHA: + case ALPHA + ADD_NL: + testval = mask = RI_ALPHA; + goto do_class; + case NALPHA: + case NALPHA + ADD_NL: + mask = RI_ALPHA; + goto do_class; + case LOWER: + case LOWER + ADD_NL: + testval = mask = RI_LOWER; + goto do_class; + case NLOWER: + case NLOWER + ADD_NL: + mask = RI_LOWER; + goto do_class; + case UPPER: + case UPPER + ADD_NL: + testval = mask = RI_UPPER; + goto do_class; + case NUPPER: + case NUPPER + ADD_NL: + mask = RI_UPPER; + goto do_class; + + case EXACTLY: + { + int cu, cl; + + // This doesn't do a multi-byte character, because a MULTIBYTECODE + // would have been used for it. It does handle single-byte + // characters, such as latin1. + if (rex.reg_ic) { + cu = mb_toupper(*opnd); + cl = mb_tolower(*opnd); + while (count < maxcount && (*scan == cu || *scan == cl)) { + count++; + scan++; + } + } else { + cu = *opnd; + while (count < maxcount && *scan == cu) { + count++; + scan++; + } + } + break; + } + + case MULTIBYTECODE: + { + int i, len, cf = 0; + + // Safety check (just in case 'encoding' was changed since + // compiling the program). + if ((len = utfc_ptr2len(opnd)) > 1) { + if (rex.reg_ic) { + cf = utf_fold(utf_ptr2char(opnd)); + } + while (count < maxcount && utfc_ptr2len(scan) >= len) { + for (i = 0; i < len; i++) { + if (opnd[i] != scan[i]) { + break; + } + } + if (i < len && (!rex.reg_ic + || utf_fold(utf_ptr2char(scan)) != cf)) { + break; + } + scan += len; + ++count; + } + } + } + break; + + case ANYOF: + case ANYOF + ADD_NL: + testval = 1; + FALLTHROUGH; + + case ANYBUT: + case ANYBUT + ADD_NL: + while (count < maxcount) { + int len; + if (*scan == NUL) { + if (!REG_MULTI || !WITH_NL(OP(p)) || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) { + break; + } + reg_nextline(); + scan = rex.input; + if (got_int) { + break; + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else if ((len = utfc_ptr2len(scan)) > 1) { + if ((cstrchr(opnd, utf_ptr2char(scan)) == NULL) == testval) { + break; + } + scan += len; + } else { + if ((cstrchr(opnd, *scan) == NULL) == testval) + break; + ++scan; + } + ++count; + } + break; + + case NEWL: + while (count < maxcount + && ((*scan == NUL && rex.lnum <= rex.reg_maxline && !rex.reg_line_lbr + && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) { + count++; + if (rex.reg_line_lbr) { + ADVANCE_REGINPUT(); + } else { + reg_nextline(); + } + scan = rex.input; + if (got_int) { + break; + } + } + break; + + default: // Oh dear. Called inappropriately. + iemsg(_(e_re_corr)); +#ifdef REGEXP_DEBUG + printf("Called regrepeat with op code %d\n", OP(p)); +#endif + break; + } + + rex.input = scan; + + return (int)count; +} + +/* + * Push an item onto the regstack. + * Returns pointer to new item. Returns NULL when out of memory. + */ +static regitem_T *regstack_push(regstate_T state, char_u *scan) +{ + regitem_T *rp; + + if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { + emsg(_(e_maxmempat)); + return NULL; + } + ga_grow(®stack, sizeof(regitem_T)); + + rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len); + rp->rs_state = state; + rp->rs_scan = scan; + + regstack.ga_len += sizeof(regitem_T); + return rp; +} + +/* + * Pop an item from the regstack. + */ +static void regstack_pop(char_u **scan) +{ + regitem_T *rp; + + rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; + *scan = rp->rs_scan; + + regstack.ga_len -= sizeof(regitem_T); +} + +// Save the current subexpr to "bp", so that they can be restored +// later by restore_subexpr(). +static void save_subexpr(regbehind_T *bp) + FUNC_ATTR_NONNULL_ALL +{ + // When "rex.need_clear_subexpr" is set we don't need to save the values, only + // remember that this flag needs to be set again when restoring. + bp->save_need_clear_subexpr = rex.need_clear_subexpr; + if (!rex.need_clear_subexpr) { + for (int i = 0; i < NSUBEXP; i++) { + if (REG_MULTI) { + bp->save_start[i].se_u.pos = rex.reg_startpos[i]; + bp->save_end[i].se_u.pos = rex.reg_endpos[i]; + } else { + bp->save_start[i].se_u.ptr = rex.reg_startp[i]; + bp->save_end[i].se_u.ptr = rex.reg_endp[i]; + } + } + } +} + +// Restore the subexpr from "bp". +static void restore_subexpr(regbehind_T *bp) + FUNC_ATTR_NONNULL_ALL +{ + // Only need to restore saved values when they are not to be cleared. + rex.need_clear_subexpr = bp->save_need_clear_subexpr; + if (!rex.need_clear_subexpr) { + for (int i = 0; i < NSUBEXP; i++) { + if (REG_MULTI) { + rex.reg_startpos[i] = bp->save_start[i].se_u.pos; + rex.reg_endpos[i] = bp->save_end[i].se_u.pos; + } else { + rex.reg_startp[i] = bp->save_start[i].se_u.ptr; + rex.reg_endp[i] = bp->save_end[i].se_u.ptr; + } + } + } +} +/// Main matching routine +/// +/// Conceptually the strategy is simple: Check to see whether the current node +/// matches, push an item onto the regstack and loop to see whether the rest +/// matches, and then act accordingly. In practice we make some effort to +/// avoid using the regstack, in particular by going through "ordinary" nodes +/// (that don't need to know whether the rest of the match failed) by a nested +/// loop. +/// +/// Returns true when there is a match. Leaves rex.input and rex.lnum +/// just after the last matched character. +/// Returns false when there is no match. Leaves rex.input and rex.lnum in an +/// undefined state! +static bool regmatch( + char_u *scan, // Current node. + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL +{ + char_u *next; // Next node. + int op; + int c; + regitem_T *rp; + int no; + int status; // one of the RA_ values: + int tm_count = 0; + + // Make "regstack" and "backpos" empty. They are allocated and freed in + // bt_regexec_both() to reduce malloc()/free() calls. + regstack.ga_len = 0; + backpos.ga_len = 0; + + // Repeat until "regstack" is empty. + for (;; ) { + // Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q". + // Allow interrupting them with CTRL-C. + fast_breakcheck(); + +#ifdef REGEXP_DEBUG + if (scan != NULL && regnarrate) { + mch_errmsg((char *)regprop(scan)); + mch_errmsg("(\n"); + } +#endif + + // Repeat for items that can be matched sequentially, without using the + // regstack. + for (;; ) { + if (got_int || scan == NULL) { + status = RA_FAIL; + break; + } + // Check for timeout once in a 100 times to avoid overhead. + if (tm != NULL && ++tm_count == 100) { + tm_count = 0; + if (profile_passed_limit(*tm)) { + if (timed_out != NULL) { + *timed_out = true; + } + status = RA_FAIL; + break; + } + } + status = RA_CONT; + +#ifdef REGEXP_DEBUG + if (regnarrate) { + mch_errmsg((char *)regprop(scan)); + mch_errmsg("...\n"); + if (re_extmatch_in != NULL) { + int i; + + mch_errmsg(_("External submatches:\n")); + for (i = 0; i < NSUBEXP; i++) { + mch_errmsg(" \""); + if (re_extmatch_in->matches[i] != NULL) + mch_errmsg((char *)re_extmatch_in->matches[i]); + mch_errmsg("\"\n"); + } + } + } +#endif + next = regnext(scan); + + op = OP(scan); + // Check for character class with NL added. + if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI + && *rex.input == NUL && rex.lnum <= rex.reg_maxline) { + reg_nextline(); + } else if (rex.reg_line_lbr && WITH_NL(op) && *rex.input == '\n') { + ADVANCE_REGINPUT(); + } else { + if (WITH_NL(op)) { + op -= ADD_NL; + } + c = utf_ptr2char(rex.input); + switch (op) { + case BOL: + if (rex.input != rex.line) { + status = RA_NOMATCH; + } + break; + + case EOL: + if (c != NUL) { + status = RA_NOMATCH; + } + break; + + case RE_BOF: + // We're not at the beginning of the file when below the first + // line where we started, not at the start of the line or we + // didn't start at the first line of the buffer. + if (rex.lnum != 0 || rex.input != rex.line + || (REG_MULTI && rex.reg_firstlnum > 1)) { + status = RA_NOMATCH; + } + break; + + case RE_EOF: + if (rex.lnum != rex.reg_maxline || c != NUL) { + status = RA_NOMATCH; + } + break; + + case CURSOR: + // Check if the buffer is in a window and compare the + // rex.reg_win->w_cursor position to the match position. + if (rex.reg_win == NULL + || (rex.lnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum) + || ((colnr_T)(rex.input - rex.line) != + rex.reg_win->w_cursor.col)) { + status = RA_NOMATCH; + } + break; + + case RE_MARK: + // Compare the mark position to the match position. + { + int mark = OPERAND(scan)[0]; + int cmp = OPERAND(scan)[1]; + pos_T *pos; + + pos = getmark_buf(rex.reg_buf, mark, false); + if (pos == NULL // mark doesn't exist + || pos->lnum <= 0) { // mark isn't set in reg_buf + status = RA_NOMATCH; + } else { + const colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum + && pos->col == MAXCOL + ? (colnr_T)STRLEN(reg_getline(pos->lnum - rex.reg_firstlnum)) + : pos->col; + + if (pos->lnum == rex.lnum + rex.reg_firstlnum + ? (pos_col == (colnr_T)(rex.input - rex.line) + ? (cmp == '<' || cmp == '>') + : (pos_col < (colnr_T)(rex.input - rex.line) + ? cmp != '>' + : cmp != '<')) + : (pos->lnum < rex.lnum + rex.reg_firstlnum + ? cmp != '>' + : cmp != '<')) { + status = RA_NOMATCH; + } + } + } + break; + + case RE_VISUAL: + if (!reg_match_visual()) + status = RA_NOMATCH; + break; + + case RE_LNUM: + assert(rex.lnum + rex.reg_firstlnum >= 0 + && (uintmax_t)(rex.lnum + rex.reg_firstlnum) <= UINT32_MAX); + if (!REG_MULTI + || !re_num_cmp((uint32_t)(rex.lnum + rex.reg_firstlnum), scan)) { + status = RA_NOMATCH; + } + break; + + case RE_COL: + assert(rex.input - rex.line + 1 >= 0 + && (uintmax_t)(rex.input - rex.line + 1) <= UINT32_MAX); + if (!re_num_cmp((uint32_t)(rex.input - rex.line + 1), scan)) { + status = RA_NOMATCH; + } + break; + + case RE_VCOL: + if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL + ? curwin : rex.reg_win, + rex.line, + (colnr_T)(rex.input - rex.line)) + 1, + scan)) { + status = RA_NOMATCH; + } + break; + + case BOW: // \b_chartab); + if (this_class <= 1) { + status = RA_NOMATCH; // Not on a word at all. + } else if (reg_prev_class() == this_class) { + status = RA_NOMATCH; // Previous char is in same word. + } + } + break; + + case EOW: // word\>; rex.input points after d + if (rex.input == rex.line) { // Can't match at start of line + status = RA_NOMATCH; + } else { + int this_class, prev_class; + + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(rex.input, rex.reg_buf->b_chartab); + prev_class = reg_prev_class(); + if (this_class == prev_class + || prev_class == 0 || prev_class == 1) { + status = RA_NOMATCH; + } + } + break; // Matched with EOW + + case ANY: + // ANY does not match new lines. + if (c == NUL) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case IDENT: + if (!vim_isIDc(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case SIDENT: + if (ascii_isdigit(*rex.input) || !vim_isIDc(c)) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case KWORD: + if (!vim_iswordp_buf(rex.input, rex.reg_buf)) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case SKWORD: + if (ascii_isdigit(*rex.input) + || !vim_iswordp_buf(rex.input, rex.reg_buf)) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case FNAME: + if (!vim_isfilec(c)) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case SFNAME: + if (ascii_isdigit(*rex.input) || !vim_isfilec(c)) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case PRINT: + if (!vim_isprintc(utf_ptr2char(rex.input))) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case SPRINT: + if (ascii_isdigit(*rex.input) || !vim_isprintc(utf_ptr2char(rex.input))) { + status = RA_NOMATCH; + } else { + ADVANCE_REGINPUT(); + } + break; + + case WHITE: + if (!ascii_iswhite(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NWHITE: + if (c == NUL || ascii_iswhite(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case DIGIT: + if (!ri_digit(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NDIGIT: + if (c == NUL || ri_digit(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case HEX: + if (!ri_hex(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NHEX: + if (c == NUL || ri_hex(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case OCTAL: + if (!ri_octal(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NOCTAL: + if (c == NUL || ri_octal(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case WORD: + if (!ri_word(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NWORD: + if (c == NUL || ri_word(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case HEAD: + if (!ri_head(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NHEAD: + if (c == NUL || ri_head(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case ALPHA: + if (!ri_alpha(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NALPHA: + if (c == NUL || ri_alpha(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case LOWER: + if (!ri_lower(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NLOWER: + if (c == NUL || ri_lower(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case UPPER: + if (!ri_upper(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case NUPPER: + if (c == NUL || ri_upper(c)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case EXACTLY: + { + int len; + char_u *opnd; + + opnd = OPERAND(scan); + // Inline the first byte, for speed. + if (*opnd != *rex.input + && (!rex.reg_ic)) { + status = RA_NOMATCH; + } else if (*opnd == NUL) { + // match empty string always works; happens when "~" is + // empty. + } else { + if (opnd[1] == NUL && !rex.reg_ic) { + len = 1; // matched a single byte above + } else { + // Need to match first byte again for multi-byte. + len = (int)STRLEN(opnd); + if (cstrncmp(opnd, rex.input, &len) != 0) { + status = RA_NOMATCH; + } + } + // Check for following composing character, unless %C + // follows (skips over all composing chars). + if (status != RA_NOMATCH + && utf_composinglike(rex.input, rex.input + len) + && !rex.reg_icombine + && OP(next) != RE_COMPOSING) { + // raaron: This code makes a composing character get + // ignored, which is the correct behavior (sometimes) + // for voweled Hebrew texts. + status = RA_NOMATCH; + } + if (status != RA_NOMATCH) { + rex.input += len; + } + } + } + break; + + case ANYOF: + case ANYBUT: + if (c == NUL) + status = RA_NOMATCH; + else if ((cstrchr(OPERAND(scan), c) == NULL) == (op == ANYOF)) + status = RA_NOMATCH; + else + ADVANCE_REGINPUT(); + break; + + case MULTIBYTECODE: + { + int i, len; + + const char_u *opnd = OPERAND(scan); + // Safety check (just in case 'encoding' was changed since + // compiling the program). + if ((len = utfc_ptr2len(opnd)) < 2) { + status = RA_NOMATCH; + break; + } + const int opndc = utf_ptr2char(opnd); + if (utf_iscomposing(opndc)) { + // When only a composing char is given match at any + // position where that composing char appears. + status = RA_NOMATCH; + for (i = 0; rex.input[i] != NUL; + i += utf_ptr2len(rex.input + i)) { + const int inpc = utf_ptr2char(rex.input + i); + if (!utf_iscomposing(inpc)) { + if (i > 0) { + break; + } + } else if (opndc == inpc) { + // Include all following composing chars. + len = i + utfc_ptr2len(rex.input + i); + status = RA_MATCH; + break; + } + } + } else { + for (i = 0; i < len; i++) { + if (opnd[i] != rex.input[i]) { + status = RA_NOMATCH; + break; + } + } + } + rex.input += len; + } + break; + + case RE_COMPOSING: + { + // Skip composing characters. + while (utf_iscomposing(utf_ptr2char(rex.input))) { + MB_CPTR_ADV(rex.input); + } + } + break; + + case NOTHING: + break; + + case BACK: + { + int i; + + // When we run into BACK we need to check if we don't keep + // looping without matching any input. The second and later + // times a BACK is encountered it fails if the input is still + // at the same position as the previous time. + // The positions are stored in "backpos" and found by the + // current value of "scan", the position in the RE program. + backpos_T *bp = (backpos_T *)backpos.ga_data; + for (i = 0; i < backpos.ga_len; ++i) + if (bp[i].bp_scan == scan) + break; + if (i == backpos.ga_len) { + backpos_T *p = GA_APPEND_VIA_PTR(backpos_T, &backpos); + p->bp_scan = scan; + } else if (reg_save_equal(&bp[i].bp_pos)) + // Still at same position as last time, fail. + status = RA_NOMATCH; + + assert(status != RA_FAIL); + if (status != RA_NOMATCH) { + reg_save(&bp[i].bp_pos, &backpos); + } + } + break; + + case MOPEN + 0: // Match start: \zs + case MOPEN + 1: // \( + case MOPEN + 2: + case MOPEN + 3: + case MOPEN + 4: + case MOPEN + 5: + case MOPEN + 6: + case MOPEN + 7: + case MOPEN + 8: + case MOPEN + 9: + { + no = op - MOPEN; + cleanup_subexpr(); + rp = regstack_push(RS_MOPEN, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = no; + save_se(&rp->rs_un.sesave, &rex.reg_startpos[no], + &rex.reg_startp[no]); + // We simply continue and handle the result when done. + } + } + break; + + case NOPEN: // \%( + case NCLOSE: // \) after \%( + if (regstack_push(RS_NOPEN, scan) == NULL) + status = RA_FAIL; + // We simply continue and handle the result when done. + break; + + case ZOPEN + 1: + case ZOPEN + 2: + case ZOPEN + 3: + case ZOPEN + 4: + case ZOPEN + 5: + case ZOPEN + 6: + case ZOPEN + 7: + case ZOPEN + 8: + case ZOPEN + 9: + { + no = op - ZOPEN; + cleanup_zsubexpr(); + rp = regstack_push(RS_ZOPEN, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = no; + save_se(&rp->rs_un.sesave, ®_startzpos[no], + ®_startzp[no]); + // We simply continue and handle the result when done. + } + } + break; + + case MCLOSE + 0: // Match end: \ze + case MCLOSE + 1: // \) + case MCLOSE + 2: + case MCLOSE + 3: + case MCLOSE + 4: + case MCLOSE + 5: + case MCLOSE + 6: + case MCLOSE + 7: + case MCLOSE + 8: + case MCLOSE + 9: + { + no = op - MCLOSE; + cleanup_subexpr(); + rp = regstack_push(RS_MCLOSE, scan); + if (rp == NULL) { + status = RA_FAIL; + } else { + rp->rs_no = no; + save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]); + // We simply continue and handle the result when done. + } + } + break; + + case ZCLOSE + 1: // \) after \z( + case ZCLOSE + 2: + case ZCLOSE + 3: + case ZCLOSE + 4: + case ZCLOSE + 5: + case ZCLOSE + 6: + case ZCLOSE + 7: + case ZCLOSE + 8: + case ZCLOSE + 9: + { + no = op - ZCLOSE; + cleanup_zsubexpr(); + rp = regstack_push(RS_ZCLOSE, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = no; + save_se(&rp->rs_un.sesave, ®_endzpos[no], + ®_endzp[no]); + // We simply continue and handle the result when done. + } + } + break; + + case BACKREF + 1: + case BACKREF + 2: + case BACKREF + 3: + case BACKREF + 4: + case BACKREF + 5: + case BACKREF + 6: + case BACKREF + 7: + case BACKREF + 8: + case BACKREF + 9: + { + int len; + + no = op - BACKREF; + cleanup_subexpr(); + if (!REG_MULTI) { // Single-line regexp + if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) { + // Backref was not set: Match an empty string. + len = 0; + } else { + // Compare current input with back-ref in the same line. + len = (int)(rex.reg_endp[no] - rex.reg_startp[no]); + if (cstrncmp(rex.reg_startp[no], rex.input, &len) != 0) { + status = RA_NOMATCH; + } + } + } else { // Multi-line regexp + if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) { + // Backref was not set: Match an empty string. + len = 0; + } else { + if (rex.reg_startpos[no].lnum == rex.lnum + && rex.reg_endpos[no].lnum == rex.lnum) { + // Compare back-ref within the current line. + len = rex.reg_endpos[no].col - rex.reg_startpos[no].col; + if (cstrncmp(rex.line + rex.reg_startpos[no].col, + rex.input, &len) != 0) { + status = RA_NOMATCH; + } + } else { + // Messy situation: Need to compare between two lines. + int r = match_with_backref(rex.reg_startpos[no].lnum, + rex.reg_startpos[no].col, + rex.reg_endpos[no].lnum, + rex.reg_endpos[no].col, + &len); + if (r != RA_MATCH) { + status = r; + } + } + } + } + + // Matched the backref, skip over it. + rex.input += len; + } + break; + + case ZREF + 1: + case ZREF + 2: + case ZREF + 3: + case ZREF + 4: + case ZREF + 5: + case ZREF + 6: + case ZREF + 7: + case ZREF + 8: + case ZREF + 9: + { + cleanup_zsubexpr(); + no = op - ZREF; + if (re_extmatch_in != NULL + && re_extmatch_in->matches[no] != NULL) { + int len = (int)STRLEN(re_extmatch_in->matches[no]); + if (cstrncmp(re_extmatch_in->matches[no], rex.input, &len) != 0) { + status = RA_NOMATCH; + } else { + rex.input += len; + } + } else { + // Backref was not set: Match an empty string. + } + } + break; + + case BRANCH: + { + if (OP(next) != BRANCH) // No choice. + next = OPERAND(scan); // Avoid recursion. + else { + rp = regstack_push(RS_BRANCH, scan); + if (rp == NULL) + status = RA_FAIL; + else + status = RA_BREAK; // rest is below + } + } + break; + + case BRACE_LIMITS: + { + if (OP(next) == BRACE_SIMPLE) { + bl_minval = OPERAND_MIN(scan); + bl_maxval = OPERAND_MAX(scan); + } else if (OP(next) >= BRACE_COMPLEX + && OP(next) < BRACE_COMPLEX + 10) { + no = OP(next) - BRACE_COMPLEX; + brace_min[no] = OPERAND_MIN(scan); + brace_max[no] = OPERAND_MAX(scan); + brace_count[no] = 0; + } else { + internal_error("BRACE_LIMITS"); + status = RA_FAIL; + } + } + break; + + case BRACE_COMPLEX + 0: + case BRACE_COMPLEX + 1: + case BRACE_COMPLEX + 2: + case BRACE_COMPLEX + 3: + case BRACE_COMPLEX + 4: + case BRACE_COMPLEX + 5: + case BRACE_COMPLEX + 6: + case BRACE_COMPLEX + 7: + case BRACE_COMPLEX + 8: + case BRACE_COMPLEX + 9: + { + no = op - BRACE_COMPLEX; + ++brace_count[no]; + + // If not matched enough times yet, try one more + if (brace_count[no] <= (brace_min[no] <= brace_max[no] + ? brace_min[no] : brace_max[no])) { + rp = regstack_push(RS_BRCPLX_MORE, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = no; + reg_save(&rp->rs_un.regsave, &backpos); + next = OPERAND(scan); + // We continue and handle the result when done. + } + break; + } + + // If matched enough times, may try matching some more + if (brace_min[no] <= brace_max[no]) { + // Range is the normal way around, use longest match + if (brace_count[no] <= brace_max[no]) { + rp = regstack_push(RS_BRCPLX_LONG, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = no; + reg_save(&rp->rs_un.regsave, &backpos); + next = OPERAND(scan); + // We continue and handle the result when done. + } + } + } else { + // Range is backwards, use shortest match first + if (brace_count[no] <= brace_min[no]) { + rp = regstack_push(RS_BRCPLX_SHORT, scan); + if (rp == NULL) + status = RA_FAIL; + else { + reg_save(&rp->rs_un.regsave, &backpos); + // We continue and handle the result when done. + } + } + } + } + break; + + case BRACE_SIMPLE: + case STAR: + case PLUS: + { + regstar_T rst; + + // Lookahead to avoid useless match attempts when we know + // what character comes next. + if (OP(next) == EXACTLY) { + rst.nextb = *OPERAND(next); + if (rex.reg_ic) { + if (mb_isupper(rst.nextb)) { + rst.nextb_ic = mb_tolower(rst.nextb); + } else { + rst.nextb_ic = mb_toupper(rst.nextb); + } + } else { + rst.nextb_ic = rst.nextb; + } + } else { + rst.nextb = NUL; + rst.nextb_ic = NUL; + } + if (op != BRACE_SIMPLE) { + rst.minval = (op == STAR) ? 0 : 1; + rst.maxval = MAX_LIMIT; + } else { + rst.minval = bl_minval; + rst.maxval = bl_maxval; + } + + // When maxval > minval, try matching as much as possible, up + // to maxval. When maxval < minval, try matching at least the + // minimal number (since the range is backwards, that's also + // maxval!). + rst.count = regrepeat(OPERAND(scan), rst.maxval); + if (got_int) { + status = RA_FAIL; + break; + } + if (rst.minval <= rst.maxval + ? rst.count >= rst.minval : rst.count >= rst.maxval) { + // It could match. Prepare for trying to match what + // follows. The code is below. Parameters are stored in + // a regstar_T on the regstack. + if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { + emsg(_(e_maxmempat)); + status = RA_FAIL; + } else { + ga_grow(®stack, sizeof(regstar_T)); + regstack.ga_len += sizeof(regstar_T); + rp = regstack_push(rst.minval <= rst.maxval + ? RS_STAR_LONG : RS_STAR_SHORT, scan); + if (rp == NULL) + status = RA_FAIL; + else { + *(((regstar_T *)rp) - 1) = rst; + status = RA_BREAK; // skip the restore bits + } + } + } else + status = RA_NOMATCH; + + } + break; + + case NOMATCH: + case MATCH: + case SUBPAT: + rp = regstack_push(RS_NOMATCH, scan); + if (rp == NULL) + status = RA_FAIL; + else { + rp->rs_no = op; + reg_save(&rp->rs_un.regsave, &backpos); + next = OPERAND(scan); + // We continue and handle the result when done. + } + break; + + case BEHIND: + case NOBEHIND: + // Need a bit of room to store extra positions. + if ((long)((unsigned)regstack.ga_len >> 10) >= p_mmp) { + emsg(_(e_maxmempat)); + status = RA_FAIL; + } else { + ga_grow(®stack, sizeof(regbehind_T)); + regstack.ga_len += sizeof(regbehind_T); + rp = regstack_push(RS_BEHIND1, scan); + if (rp == NULL) + status = RA_FAIL; + else { + // Need to save the subexpr to be able to restore them + // when there is a match but we don't use it. + save_subexpr(((regbehind_T *)rp) - 1); + + rp->rs_no = op; + reg_save(&rp->rs_un.regsave, &backpos); + // First try if what follows matches. If it does then we + // check the behind match by looping. + } + } + break; + + case BHPOS: + if (REG_MULTI) { + if (behind_pos.rs_u.pos.col != (colnr_T)(rex.input - rex.line) + || behind_pos.rs_u.pos.lnum != rex.lnum) { + status = RA_NOMATCH; + } + } else if (behind_pos.rs_u.ptr != rex.input) { + status = RA_NOMATCH; + } + break; + + case NEWL: + if ((c != NUL || !REG_MULTI || rex.lnum > rex.reg_maxline + || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) { + status = RA_NOMATCH; + } else if (rex.reg_line_lbr) { + ADVANCE_REGINPUT(); + } else { + reg_nextline(); + } + break; + + case END: + status = RA_MATCH; // Success! + break; + + default: + iemsg(_(e_re_corr)); +#ifdef REGEXP_DEBUG + printf("Illegal op code %d\n", op); +#endif + status = RA_FAIL; + break; + } + } + + // If we can't continue sequentially, break the inner loop. + if (status != RA_CONT) + break; + + // Continue in inner loop, advance to next item. + scan = next; + + } // end of inner loop + + // If there is something on the regstack execute the code for the state. + // If the state is popped then loop and use the older state. + while (!GA_EMPTY(®stack) && status != RA_FAIL) { + rp = (regitem_T *)((char *)regstack.ga_data + regstack.ga_len) - 1; + switch (rp->rs_state) { + case RS_NOPEN: + // Result is passed on as-is, simply pop the state. + regstack_pop(&scan); + break; + + case RS_MOPEN: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no], + &rex.reg_startp[rp->rs_no]); + } + regstack_pop(&scan); + break; + + case RS_ZOPEN: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) + restore_se(&rp->rs_un.sesave, ®_startzpos[rp->rs_no], + ®_startzp[rp->rs_no]); + regstack_pop(&scan); + break; + + case RS_MCLOSE: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no], + &rex.reg_endp[rp->rs_no]); + } + regstack_pop(&scan); + break; + + case RS_ZCLOSE: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) + restore_se(&rp->rs_un.sesave, ®_endzpos[rp->rs_no], + ®_endzp[rp->rs_no]); + regstack_pop(&scan); + break; + + case RS_BRANCH: + if (status == RA_MATCH) + // this branch matched, use it + regstack_pop(&scan); + else { + if (status != RA_BREAK) { + // After a non-matching branch: try next one. + reg_restore(&rp->rs_un.regsave, &backpos); + scan = rp->rs_scan; + } + if (scan == NULL || OP(scan) != BRANCH) { + // no more branches, didn't find a match + status = RA_NOMATCH; + regstack_pop(&scan); + } else { + // Prepare to try a branch. + rp->rs_scan = regnext(scan); + reg_save(&rp->rs_un.regsave, &backpos); + scan = OPERAND(scan); + } + } + break; + + case RS_BRCPLX_MORE: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + reg_restore(&rp->rs_un.regsave, &backpos); + --brace_count[rp->rs_no]; // decrement match count + } + regstack_pop(&scan); + break; + + case RS_BRCPLX_LONG: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + // There was no match, but we did find enough matches. + reg_restore(&rp->rs_un.regsave, &backpos); + --brace_count[rp->rs_no]; + // continue with the items after "\{}" + status = RA_CONT; + } + regstack_pop(&scan); + if (status == RA_CONT) + scan = regnext(scan); + break; + + case RS_BRCPLX_SHORT: + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) + // There was no match, try to match one more item. + reg_restore(&rp->rs_un.regsave, &backpos); + regstack_pop(&scan); + if (status == RA_NOMATCH) { + scan = OPERAND(scan); + status = RA_CONT; + } + break; + + case RS_NOMATCH: + // Pop the state. If the operand matches for NOMATCH or + // doesn't match for MATCH/SUBPAT, we fail. Otherwise backup, + // except for SUBPAT, and continue with the next item. + if (status == (rp->rs_no == NOMATCH ? RA_MATCH : RA_NOMATCH)) + status = RA_NOMATCH; + else { + status = RA_CONT; + if (rp->rs_no != SUBPAT) // zero-width + reg_restore(&rp->rs_un.regsave, &backpos); + } + regstack_pop(&scan); + if (status == RA_CONT) + scan = regnext(scan); + break; + + case RS_BEHIND1: + if (status == RA_NOMATCH) { + regstack_pop(&scan); + regstack.ga_len -= sizeof(regbehind_T); + } else { + // The stuff after BEHIND/NOBEHIND matches. Now try if + // the behind part does (not) match before the current + // position in the input. This must be done at every + // position in the input and checking if the match ends at + // the current position. + + // save the position after the found match for next + reg_save(&(((regbehind_T *)rp) - 1)->save_after, &backpos); + + // Start looking for a match with operand at the current + // position. Go back one character until we find the + // result, hitting the start of the line or the previous + // line (for multi-line matching). + // Set behind_pos to where the match should end, BHPOS + // will match it. Save the current value. + (((regbehind_T *)rp) - 1)->save_behind = behind_pos; + behind_pos = rp->rs_un.regsave; + + rp->rs_state = RS_BEHIND2; + + reg_restore(&rp->rs_un.regsave, &backpos); + scan = OPERAND(rp->rs_scan) + 4; + } + break; + + case RS_BEHIND2: + // Looping for BEHIND / NOBEHIND match. + if (status == RA_MATCH && reg_save_equal(&behind_pos)) { + // found a match that ends where "next" started + behind_pos = (((regbehind_T *)rp) - 1)->save_behind; + if (rp->rs_no == BEHIND) + reg_restore(&(((regbehind_T *)rp) - 1)->save_after, + &backpos); + else { + // But we didn't want a match. Need to restore the + // subexpr, because what follows matched, so they have + // been set. + status = RA_NOMATCH; + restore_subexpr(((regbehind_T *)rp) - 1); + } + regstack_pop(&scan); + regstack.ga_len -= sizeof(regbehind_T); + } else { + long limit; + + // No match or a match that doesn't end where we want it: Go + // back one character. May go to previous line once. + no = OK; + limit = OPERAND_MIN(rp->rs_scan); + if (REG_MULTI) { + if (limit > 0 + && ((rp->rs_un.regsave.rs_u.pos.lnum + < behind_pos.rs_u.pos.lnum + ? (colnr_T)STRLEN(rex.line) + : behind_pos.rs_u.pos.col) + - rp->rs_un.regsave.rs_u.pos.col >= limit)) + no = FAIL; + else if (rp->rs_un.regsave.rs_u.pos.col == 0) { + if (rp->rs_un.regsave.rs_u.pos.lnum + < behind_pos.rs_u.pos.lnum + || reg_getline( + --rp->rs_un.regsave.rs_u.pos.lnum) + == NULL) + no = FAIL; + else { + reg_restore(&rp->rs_un.regsave, &backpos); + rp->rs_un.regsave.rs_u.pos.col = + (colnr_T)STRLEN(rex.line); + } + } else { + const char_u *const line = + reg_getline(rp->rs_un.regsave.rs_u.pos.lnum); + + rp->rs_un.regsave.rs_u.pos.col -= + utf_head_off(line, + line + rp->rs_un.regsave.rs_u.pos.col - 1) + + 1; + } + } else { + if (rp->rs_un.regsave.rs_u.ptr == rex.line) { + no = FAIL; + } else { + MB_PTR_BACK(rex.line, rp->rs_un.regsave.rs_u.ptr); + if (limit > 0 + && (behind_pos.rs_u.ptr - rp->rs_un.regsave.rs_u.ptr) > (ptrdiff_t)limit) { + no = FAIL; + } + } + } + if (no == OK) { + // Advanced, prepare for finding match again. + reg_restore(&rp->rs_un.regsave, &backpos); + scan = OPERAND(rp->rs_scan) + 4; + if (status == RA_MATCH) { + // We did match, so subexpr may have been changed, + // need to restore them for the next try. + status = RA_NOMATCH; + restore_subexpr(((regbehind_T *)rp) - 1); + } + } else { + // Can't advance. For NOBEHIND that's a match. + behind_pos = (((regbehind_T *)rp) - 1)->save_behind; + if (rp->rs_no == NOBEHIND) { + reg_restore(&(((regbehind_T *)rp) - 1)->save_after, + &backpos); + status = RA_MATCH; + } else { + // We do want a proper match. Need to restore the + // subexpr if we had a match, because they may have + // been set. + if (status == RA_MATCH) { + status = RA_NOMATCH; + restore_subexpr(((regbehind_T *)rp) - 1); + } + } + regstack_pop(&scan); + regstack.ga_len -= sizeof(regbehind_T); + } + } + break; + + case RS_STAR_LONG: + case RS_STAR_SHORT: + { + regstar_T *rst = ((regstar_T *)rp) - 1; + + if (status == RA_MATCH) { + regstack_pop(&scan); + regstack.ga_len -= sizeof(regstar_T); + break; + } + + // Tried once already, restore input pointers. + if (status != RA_BREAK) + reg_restore(&rp->rs_un.regsave, &backpos); + + // Repeat until we found a position where it could match. + for (;; ) { + if (status != RA_BREAK) { + // Tried first position already, advance. + if (rp->rs_state == RS_STAR_LONG) { + // Trying for longest match, but couldn't or + // didn't match -- back up one char. + if (--rst->count < rst->minval) + break; + if (rex.input == rex.line) { + // backup to last char of previous line + rex.lnum--; + rex.line = reg_getline(rex.lnum); + // Just in case regrepeat() didn't count right. + if (rex.line == NULL) { + break; + } + rex.input = rex.line + STRLEN(rex.line); + fast_breakcheck(); + } else { + MB_PTR_BACK(rex.line, rex.input); + } + } else { + // Range is backwards, use shortest match first. + // Careful: maxval and minval are exchanged! + // Couldn't or didn't match: try advancing one + // char. + if (rst->count == rst->minval + || regrepeat(OPERAND(rp->rs_scan), 1L) == 0) + break; + ++rst->count; + } + if (got_int) + break; + } else + status = RA_NOMATCH; + + // If it could match, try it. + if (rst->nextb == NUL || *rex.input == rst->nextb + || *rex.input == rst->nextb_ic) { + reg_save(&rp->rs_un.regsave, &backpos); + scan = regnext(rp->rs_scan); + status = RA_CONT; + break; + } + } + if (status != RA_CONT) { + // Failed. + regstack_pop(&scan); + regstack.ga_len -= sizeof(regstar_T); + status = RA_NOMATCH; + } + } + break; + } + + // If we want to continue the inner loop or didn't pop a state + // continue matching loop + if (status == RA_CONT || rp == (regitem_T *) + ((char *)regstack.ga_data + regstack.ga_len) - 1) + break; + } + + // May need to continue with the inner loop, starting at "scan". + if (status == RA_CONT) + continue; + + // If the regstack is empty or something failed we are done. + if (GA_EMPTY(®stack) || status == RA_FAIL) { + if (scan == NULL) { + // We get here only if there's trouble -- normally "case END" is + // the terminating point. + iemsg(_(e_re_corr)); +#ifdef REGEXP_DEBUG + printf("Premature EOL\n"); +#endif + } + return status == RA_MATCH; + } + + } // End of loop until the regstack is empty. + + // NOTREACHED +} + +/// Try match of "prog" with at rex.line["col"]. +/// @returns 0 for failure, or number of lines contained in the match. +static long regtry(bt_regprog_T *prog, + colnr_T col, + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL +{ + rex.input = rex.line + col; + rex.need_clear_subexpr = true; + // Clear the external match subpointers if necessaey. + rex.need_clear_zsubexpr = (prog->reghasz == REX_SET); + + if (regmatch(prog->program + 1, tm, timed_out) == 0) { + return 0; + } + + cleanup_subexpr(); + if (REG_MULTI) { + if (rex.reg_startpos[0].lnum < 0) { + rex.reg_startpos[0].lnum = 0; + rex.reg_startpos[0].col = col; + } + if (rex.reg_endpos[0].lnum < 0) { + rex.reg_endpos[0].lnum = rex.lnum; + rex.reg_endpos[0].col = (int)(rex.input - rex.line); + } else { + // Use line number of "\ze". + rex.lnum = rex.reg_endpos[0].lnum; + } + } else { + if (rex.reg_startp[0] == NULL) { + rex.reg_startp[0] = rex.line + col; + } + if (rex.reg_endp[0] == NULL) { + rex.reg_endp[0] = rex.input; + } + } + // Package any found \z(...\) matches for export. Default is none. + unref_extmatch(re_extmatch_out); + re_extmatch_out = NULL; + + if (prog->reghasz == REX_SET) { + int i; + + cleanup_zsubexpr(); + re_extmatch_out = make_extmatch(); + for (i = 0; i < NSUBEXP; i++) { + if (REG_MULTI) { + // Only accept single line matches. + if (reg_startzpos[i].lnum >= 0 + && reg_endzpos[i].lnum == reg_startzpos[i].lnum + && reg_endzpos[i].col >= reg_startzpos[i].col) { + re_extmatch_out->matches[i] = + vim_strnsave(reg_getline(reg_startzpos[i].lnum) + + reg_startzpos[i].col, + reg_endzpos[i].col + - reg_startzpos[i].col); + } + } else { + if (reg_startzp[i] != NULL && reg_endzp[i] != NULL) + re_extmatch_out->matches[i] = + vim_strnsave(reg_startzp[i], reg_endzp[i] - reg_startzp[i]); + } + } + } + return 1 + rex.lnum; +} + +/// Match a regexp against a string ("line" points to the string) or multiple +/// lines (if "line" is NULL, use reg_getline()). +/// @return 0 for failure, or number of lines contained in the match. +static long bt_regexec_both(char_u *line, + colnr_T col, // column to start search + proftime_T *tm, // timeout limit or NULL + int *timed_out) // flag set on timeout or NULL +{ + bt_regprog_T *prog; + char_u *s; + long retval = 0L; + + // Create "regstack" and "backpos" if they are not allocated yet. + // We allocate *_INITIAL amount of bytes first and then set the grow size + // to much bigger value to avoid many malloc calls in case of deep regular + // expressions. + if (regstack.ga_data == NULL) { + // Use an item size of 1 byte, since we push different things + // onto the regstack. + ga_init(®stack, 1, REGSTACK_INITIAL); + ga_grow(®stack, REGSTACK_INITIAL); + ga_set_growsize(®stack, REGSTACK_INITIAL * 8); + } + + if (backpos.ga_data == NULL) { + ga_init(&backpos, sizeof(backpos_T), BACKPOS_INITIAL); + ga_grow(&backpos, BACKPOS_INITIAL); + ga_set_growsize(&backpos, BACKPOS_INITIAL * 8); + } + + if (REG_MULTI) { + prog = (bt_regprog_T *)rex.reg_mmatch->regprog; + line = reg_getline((linenr_T)0); + rex.reg_startpos = rex.reg_mmatch->startpos; + rex.reg_endpos = rex.reg_mmatch->endpos; + } else { + prog = (bt_regprog_T *)rex.reg_match->regprog; + rex.reg_startp = rex.reg_match->startp; + rex.reg_endp = rex.reg_match->endp; + } + + // Be paranoid... + if (prog == NULL || line == NULL) { + iemsg(_(e_null)); + goto theend; + } + + // Check validity of program. + if (prog_magic_wrong()) + goto theend; + + // If the start column is past the maximum column: no need to try. + if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { + goto theend; + } + + // If pattern contains "\c" or "\C": overrule value of rex.reg_ic + if (prog->regflags & RF_ICASE) { + rex.reg_ic = true; + } else if (prog->regflags & RF_NOICASE) { + rex.reg_ic = false; + } + + // If pattern contains "\Z" overrule value of rex.reg_icombine + if (prog->regflags & RF_ICOMBINE) { + rex.reg_icombine = true; + } + + // If there is a "must appear" string, look for it. + if (prog->regmust != NULL) { + int c = utf_ptr2char(prog->regmust); + s = line + col; + + // This is used very often, esp. for ":global". Use two versions of + // the loop to avoid overhead of conditions. + if (!rex.reg_ic) { + while ((s = vim_strchr(s, c)) != NULL) { + if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { + break; // Found it. + } + MB_PTR_ADV(s); + } + } else { + while ((s = cstrchr(s, c)) != NULL) { + if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { + break; // Found it. + } + MB_PTR_ADV(s); + } + } + if (s == NULL) { // Not present. + goto theend; + } + } + + rex.line = line; + rex.lnum = 0; + reg_toolong = false; + + // Simplest case: Anchored match need be tried only once. + if (prog->reganch) { + int c = utf_ptr2char(rex.line + col); + if (prog->regstart == NUL + || prog->regstart == c + || (rex.reg_ic + && (utf_fold(prog->regstart) == utf_fold(c) + || (c < 255 && prog->regstart < 255 + && mb_tolower(prog->regstart) == mb_tolower(c))))) { + retval = regtry(prog, col, tm, timed_out); + } else { + retval = 0; + } + } else { + int tm_count = 0; + // Messy cases: unanchored match. + while (!got_int) { + if (prog->regstart != NUL) { + // Skip until the char we know it must start with. + s = cstrchr(rex.line + col, prog->regstart); + if (s == NULL) { + retval = 0; + break; + } + col = (int)(s - rex.line); + } + + // Check for maximum column to try. + if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { + retval = 0; + break; + } + + retval = regtry(prog, col, tm, timed_out); + if (retval > 0) { + break; + } + + // if not currently on the first line, get it again + if (rex.lnum != 0) { + rex.lnum = 0; + rex.line = reg_getline((linenr_T)0); + } + if (rex.line[col] == NUL) { + break; + } + col += utfc_ptr2len(rex.line + col); + // Check for timeout once in a twenty times to avoid overhead. + if (tm != NULL && ++tm_count == 20) { + tm_count = 0; + if (profile_passed_limit(*tm)) { + if (timed_out != NULL) { + *timed_out = true; + } + break; + } + } + } + } + +theend: + // Free "reg_tofree" when it's a bit big. + // Free regstack and backpos if they are bigger than their initial size. + if (reg_tofreelen > 400) { + XFREE_CLEAR(reg_tofree); + } + if (regstack.ga_maxlen > REGSTACK_INITIAL) + ga_clear(®stack); + if (backpos.ga_maxlen > BACKPOS_INITIAL) + ga_clear(&backpos); + + if (retval > 0) { + // Make sure the end is never before the start. Can happen when \zs + // and \ze are used. + if (REG_MULTI) { + const lpos_T *const start = &rex.reg_mmatch->startpos[0]; + const lpos_T *const end = &rex.reg_mmatch->endpos[0]; + + if (end->lnum < start->lnum + || (end->lnum == start->lnum && end->col < start->col)) { + rex.reg_mmatch->endpos[0] = rex.reg_mmatch->startpos[0]; + } + } else { + if (rex.reg_match->endp[0] < rex.reg_match->startp[0]) { + rex.reg_match->endp[0] = rex.reg_match->startp[0]; + } + } + } + + return retval; +} + +/* + * Match a regexp against a string. + * "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). + * Uses curbuf for line count and 'iskeyword'. + * If "line_lbr" is true, consider a "\n" in "line" to be a line break. + * + * Returns 0 for failure, number of lines contained in the match otherwise. + */ +static int bt_regexec_nl( + regmatch_T *rmp, + char_u *line, // string to match against + colnr_T col, // column to start looking for match + bool line_lbr) +{ + rex.reg_match = rmp; + rex.reg_mmatch = NULL; + rex.reg_maxline = 0; + rex.reg_line_lbr = line_lbr; + rex.reg_buf = curbuf; + rex.reg_win = NULL; + rex.reg_ic = rmp->rm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = 0; + + long r = bt_regexec_both(line, col, NULL, NULL); + assert(r <= INT_MAX); + return (int)r; +} + + +/// Matches a regexp against multiple lines. +/// "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). +/// Uses curbuf for line count and 'iskeyword'. +/// +/// @param win Window in which to search or NULL +/// @param buf Buffer in which to search +/// @param lnum Number of line to start looking for match +/// @param col Column to start looking for match +/// @param tm Timeout limit or NULL +/// +/// @return zero if there is no match and number of lines contained in the match +/// otherwise. +static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, + linenr_T lnum, colnr_T col, + proftime_T *tm, int *timed_out) +{ + rex.reg_match = NULL; + rex.reg_mmatch = rmp; + rex.reg_buf = buf; + rex.reg_win = win; + rex.reg_firstlnum = lnum; + rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum; + rex.reg_line_lbr = false; + rex.reg_ic = rmp->rmm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = rmp->rmm_maxcol; + + return bt_regexec_both(NULL, col, tm, timed_out); +} + +/* + * Compare a number with the operand of RE_LNUM, RE_COL or RE_VCOL. + */ +static int re_num_cmp(uint32_t val, char_u *scan) +{ + uint32_t n = (uint32_t)OPERAND_MIN(scan); + + if (OPERAND_CMP(scan) == '>') + return val > n; + if (OPERAND_CMP(scan) == '<') + return val < n; + return val == n; +} + +#ifdef BT_REGEXP_DUMP + +/* + * regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +static void regdump(char_u *pattern, bt_regprog_T *r) +{ + char_u *s; + int op = EXACTLY; // Arbitrary non-END op. + char_u *next; + char_u *end = NULL; + FILE *f; + +#ifdef BT_REGEXP_LOG + f = fopen("bt_regexp_log.log", "a"); +#else + f = stdout; +#endif + if (f == NULL) + return; + fprintf(f, "-------------------------------------\n\r\nregcomp(%s):\r\n", + pattern); + + s = r->program + 1; + // Loop until we find the END that isn't before a referred next (an END + // can also appear in a NOMATCH operand). + while (op != END || s <= end) { + op = OP(s); + fprintf(f, "%2d%s", (int)(s - r->program), regprop(s)); // Where, what. + next = regnext(s); + if (next == NULL) // Next ptr. + fprintf(f, "(0)"); + else + fprintf(f, "(%d)", (int)((s - r->program) + (next - s))); + if (end < next) + end = next; + if (op == BRACE_LIMITS) { + // Two ints + fprintf(f, " minval %" PRId64 ", maxval %" PRId64, + (int64_t)OPERAND_MIN(s), (int64_t)OPERAND_MAX(s)); + s += 8; + } else if (op == BEHIND || op == NOBEHIND) { + // one int + fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s)); + s += 4; + } else if (op == RE_LNUM || op == RE_COL || op == RE_VCOL) { + // one int plus comparator + fprintf(f, " count %" PRId64, (int64_t)OPERAND_MIN(s)); + s += 5; + } + s += 3; + if (op == ANYOF || op == ANYOF + ADD_NL + || op == ANYBUT || op == ANYBUT + ADD_NL + || op == EXACTLY) { + // Literal string, where present. + fprintf(f, "\nxxxxxxxxx\n"); + while (*s != NUL) + fprintf(f, "%c", *s++); + fprintf(f, "\nxxxxxxxxx\n"); + s++; + } + fprintf(f, "\r\n"); + } + + // Header fields of interest. + if (r->regstart != NUL) + fprintf(f, "start `%s' 0x%x; ", r->regstart < 256 + ? (char *)transchar(r->regstart) + : "multibyte", r->regstart); + if (r->reganch) + fprintf(f, "anchored; "); + if (r->regmust != NULL) + fprintf(f, "must have \"%s\"", r->regmust); + fprintf(f, "\r\n"); + +#ifdef BT_REGEXP_LOG + fclose(f); +#endif +} +#endif // BT_REGEXP_DUMP + +#ifdef REGEXP_DEBUG + +/* + * regprop - printable representation of opcode + */ +static char_u *regprop(char_u *op) +{ + char *p; + static char buf[50]; + + STRCPY(buf, ":"); + + switch ((int)OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case RE_BOF: + p = "BOF"; + break; + case RE_EOF: + p = "EOF"; + break; + case CURSOR: + p = "CURSOR"; + break; + case RE_VISUAL: + p = "RE_VISUAL"; + break; + case RE_LNUM: + p = "RE_LNUM"; + break; + case RE_MARK: + p = "RE_MARK"; + break; + case RE_COL: + p = "RE_COL"; + break; + case RE_VCOL: + p = "RE_VCOL"; + break; + case BOW: + p = "BOW"; + break; + case EOW: + p = "EOW"; + break; + case ANY: + p = "ANY"; + break; + case ANY + ADD_NL: + p = "ANY+NL"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYOF + ADD_NL: + p = "ANYOF+NL"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case ANYBUT + ADD_NL: + p = "ANYBUT+NL"; + break; + case IDENT: + p = "IDENT"; + break; + case IDENT + ADD_NL: + p = "IDENT+NL"; + break; + case SIDENT: + p = "SIDENT"; + break; + case SIDENT + ADD_NL: + p = "SIDENT+NL"; + break; + case KWORD: + p = "KWORD"; + break; + case KWORD + ADD_NL: + p = "KWORD+NL"; + break; + case SKWORD: + p = "SKWORD"; + break; + case SKWORD + ADD_NL: + p = "SKWORD+NL"; + break; + case FNAME: + p = "FNAME"; + break; + case FNAME + ADD_NL: + p = "FNAME+NL"; + break; + case SFNAME: + p = "SFNAME"; + break; + case SFNAME + ADD_NL: + p = "SFNAME+NL"; + break; + case PRINT: + p = "PRINT"; + break; + case PRINT + ADD_NL: + p = "PRINT+NL"; + break; + case SPRINT: + p = "SPRINT"; + break; + case SPRINT + ADD_NL: + p = "SPRINT+NL"; + break; + case WHITE: + p = "WHITE"; + break; + case WHITE + ADD_NL: + p = "WHITE+NL"; + break; + case NWHITE: + p = "NWHITE"; + break; + case NWHITE + ADD_NL: + p = "NWHITE+NL"; + break; + case DIGIT: + p = "DIGIT"; + break; + case DIGIT + ADD_NL: + p = "DIGIT+NL"; + break; + case NDIGIT: + p = "NDIGIT"; + break; + case NDIGIT + ADD_NL: + p = "NDIGIT+NL"; + break; + case HEX: + p = "HEX"; + break; + case HEX + ADD_NL: + p = "HEX+NL"; + break; + case NHEX: + p = "NHEX"; + break; + case NHEX + ADD_NL: + p = "NHEX+NL"; + break; + case OCTAL: + p = "OCTAL"; + break; + case OCTAL + ADD_NL: + p = "OCTAL+NL"; + break; + case NOCTAL: + p = "NOCTAL"; + break; + case NOCTAL + ADD_NL: + p = "NOCTAL+NL"; + break; + case WORD: + p = "WORD"; + break; + case WORD + ADD_NL: + p = "WORD+NL"; + break; + case NWORD: + p = "NWORD"; + break; + case NWORD + ADD_NL: + p = "NWORD+NL"; + break; + case HEAD: + p = "HEAD"; + break; + case HEAD + ADD_NL: + p = "HEAD+NL"; + break; + case NHEAD: + p = "NHEAD"; + break; + case NHEAD + ADD_NL: + p = "NHEAD+NL"; + break; + case ALPHA: + p = "ALPHA"; + break; + case ALPHA + ADD_NL: + p = "ALPHA+NL"; + break; + case NALPHA: + p = "NALPHA"; + break; + case NALPHA + ADD_NL: + p = "NALPHA+NL"; + break; + case LOWER: + p = "LOWER"; + break; + case LOWER + ADD_NL: + p = "LOWER+NL"; + break; + case NLOWER: + p = "NLOWER"; + break; + case NLOWER + ADD_NL: + p = "NLOWER+NL"; + break; + case UPPER: + p = "UPPER"; + break; + case UPPER + ADD_NL: + p = "UPPER+NL"; + break; + case NUPPER: + p = "NUPPER"; + break; + case NUPPER + ADD_NL: + p = "NUPPER+NL"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case MOPEN + 0: + p = "MATCH START"; + break; + case MOPEN + 1: + case MOPEN + 2: + case MOPEN + 3: + case MOPEN + 4: + case MOPEN + 5: + case MOPEN + 6: + case MOPEN + 7: + case MOPEN + 8: + case MOPEN + 9: + sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN); + p = NULL; + break; + case MCLOSE + 0: + p = "MATCH END"; + break; + case MCLOSE + 1: + case MCLOSE + 2: + case MCLOSE + 3: + case MCLOSE + 4: + case MCLOSE + 5: + case MCLOSE + 6: + case MCLOSE + 7: + case MCLOSE + 8: + case MCLOSE + 9: + sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE); + p = NULL; + break; + case BACKREF + 1: + case BACKREF + 2: + case BACKREF + 3: + case BACKREF + 4: + case BACKREF + 5: + case BACKREF + 6: + case BACKREF + 7: + case BACKREF + 8: + case BACKREF + 9: + sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF); + p = NULL; + break; + case NOPEN: + p = "NOPEN"; + break; + case NCLOSE: + p = "NCLOSE"; + break; + case ZOPEN + 1: + case ZOPEN + 2: + case ZOPEN + 3: + case ZOPEN + 4: + case ZOPEN + 5: + case ZOPEN + 6: + case ZOPEN + 7: + case ZOPEN + 8: + case ZOPEN + 9: + sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN); + p = NULL; + break; + case ZCLOSE + 1: + case ZCLOSE + 2: + case ZCLOSE + 3: + case ZCLOSE + 4: + case ZCLOSE + 5: + case ZCLOSE + 6: + case ZCLOSE + 7: + case ZCLOSE + 8: + case ZCLOSE + 9: + sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE); + p = NULL; + break; + case ZREF + 1: + case ZREF + 2: + case ZREF + 3: + case ZREF + 4: + case ZREF + 5: + case ZREF + 6: + case ZREF + 7: + case ZREF + 8: + case ZREF + 9: + sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + case NOMATCH: + p = "NOMATCH"; + break; + case MATCH: + p = "MATCH"; + break; + case BEHIND: + p = "BEHIND"; + break; + case NOBEHIND: + p = "NOBEHIND"; + break; + case SUBPAT: + p = "SUBPAT"; + break; + case BRACE_LIMITS: + p = "BRACE_LIMITS"; + break; + case BRACE_SIMPLE: + p = "BRACE_SIMPLE"; + break; + case BRACE_COMPLEX + 0: + case BRACE_COMPLEX + 1: + case BRACE_COMPLEX + 2: + case BRACE_COMPLEX + 3: + case BRACE_COMPLEX + 4: + case BRACE_COMPLEX + 5: + case BRACE_COMPLEX + 6: + case BRACE_COMPLEX + 7: + case BRACE_COMPLEX + 8: + case BRACE_COMPLEX + 9: + sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX); + p = NULL; + break; + case MULTIBYTECODE: + p = "MULTIBYTECODE"; + break; + case NEWL: + p = "NEWL"; + break; + default: + sprintf(buf + STRLEN(buf), "corrupt %d", OP(op)); + p = NULL; + break; + } + if (p != NULL) + STRCAT(buf, p); + return (char_u *)buf; +} +#endif // REGEXP_DEBUG -- cgit From 487f52c9d2580b9a10010071f9263d19bc069bd1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Mar 2022 13:53:57 +0800 Subject: chore(regexp.c): correctly align META_flags (#17668) --- src/nvim/regexp.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 0fc49cd1bf..412cdac21b 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -320,20 +320,20 @@ static int reg_strict; // "[abc" is illegal // META[] is used often enough to justify turning it into a table. static char_u META_flags[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* % & ( ) * + . */ - 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, - /* 1 2 3 4 5 6 7 8 9 < = > ? */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, - /* @ A C D F H I K L M O */ - 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, - /* P S U V W X Z [ _ */ - 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, - /* a c d f h i k l m n o */ - 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, - /* p s u v w x z { | ~ */ - 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// % & ( ) * + . + 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, +// 1 2 3 4 5 6 7 8 9 < = > ? + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, +// @ A C D F H I K L M O + 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, +// P S U V W X Z [ _ + 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, +// a c d f h i k l m n o + 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, +// p s u v w x z { | ~ + 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 }; static int curchr; // currently parsed character -- cgit From a7b1c8893c602196541e94b8b24c4c70c32c25b0 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 10 Mar 2022 07:34:55 +0100 Subject: chore: fix typos (#17331) Co-authored-by: Hongyi Lyu Co-authored-by: Gregory Anders Co-authored-by: notomo Co-authored-by: zeertzjq --- src/nvim/api/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 3dd647e76f..3bddbe8fe6 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -133,7 +133,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - buffer handle /// - on_reload: Lua callback invoked on reload. The entire buffer /// content should be considered changed. Args: -/// - the string "detach" +/// - the string "reload" /// - buffer handle /// - utf_sizes: include UTF-32 and UTF-16 size of the replaced /// region, as args to `on_lines`. -- cgit From d0cb8744d84822209edd0ac242f2400c784e6dc5 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Wed, 9 Mar 2022 21:00:39 +0100 Subject: refactor(uncrustify): disable uncrustify for misformatted code sections Uncrustify version 0.74 has a bug that deindents and misformats the entire fileio.c. --- src/nvim/eval/funcs.c | 2 ++ src/nvim/fileio.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d81a408663..85c49c20e7 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -7066,10 +7066,12 @@ static void init_srand(uint32_t *const x) if (dev_urandom_state != OK) { // Reading /dev/urandom doesn't work, fall back to time(). #endif + // uncrustify:off *x = time(NULL); #ifndef MSWIN } #endif + // uncrustify:on } static inline uint32_t splitmix32(uint32_t *const x) diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 971ae9abae..41e67e5b3b 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3,6 +3,8 @@ // fileio.c: read from and write to a file +// uncrustify:off + #include #include #include -- cgit From 7e3bdc75e44b9139d8afaea4381b53ae78b15746 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Wed, 9 Mar 2022 21:19:37 +0100 Subject: refactor(uncrustify): format all c files --- src/nvim/api/autocmd.c | 122 ++++++++++++++++++++--------------------- src/nvim/api/buffer.c | 7 +-- src/nvim/api/vim.c | 18 +++--- src/nvim/buffer.c | 4 +- src/nvim/decoration.h | 8 +-- src/nvim/eval.c | 2 +- src/nvim/eval/typval.c | 8 +-- src/nvim/event/rstream.c | 2 +- src/nvim/ex_docmd.h | 2 +- src/nvim/extmark.c | 2 +- src/nvim/extmark.h | 2 +- src/nvim/highlight.c | 7 +-- src/nvim/input.c | 2 +- src/nvim/lua/executor.c | 47 ++++++++-------- src/nvim/lua/executor.h | 8 +-- src/nvim/lua/spell.c | 8 +-- src/nvim/lua/stdlib.c | 4 +- src/nvim/marktree.c | 4 +- src/nvim/marktree.h | 4 +- src/nvim/os/os_defs.h | 2 +- src/nvim/os/pty_process_unix.c | 21 ++++--- src/nvim/os/shell.c | 4 +- src/nvim/quickfix.c | 5 +- src/nvim/screen.c | 6 +- src/nvim/search.c | 6 +- src/nvim/vim.h | 4 +- 26 files changed, 151 insertions(+), 158 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 5ede0e5265..8a7dd00b2a 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -63,26 +63,26 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) int group = 0; switch (opts->group.type) { - case kObjectTypeNil: - break; - case kObjectTypeString: - group = augroup_find(opts->group.data.string.data); - if (group < 0) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); - goto cleanup; - } - break; - case kObjectTypeInteger: - group = (int)opts->group.data.integer; - char *name = augroup_name(group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); - goto cleanup; - } - break; - default: - api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); + case kObjectTypeNil: + break; + case kObjectTypeString: + group = augroup_find(opts->group.data.string.data); + if (group < 0) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); goto cleanup; + } + break; + case kObjectTypeInteger: + group = (int)opts->group.data.integer; + char *name = augroup_name(group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup passed."); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "group must be a string or an integer."); + goto cleanup; } if (opts->event.type != kObjectTypeNil) { @@ -415,28 +415,28 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); switch (opts->group.type) { - case kObjectTypeNil: - break; - case kObjectTypeString: - au_group = augroup_find(opts->group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", opts->group.data.string.data); - goto cleanup; - } - break; - case kObjectTypeInteger: - au_group = (int)opts->group.data.integer; - char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); - goto cleanup; - } - break; - default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + goto cleanup; } if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { @@ -659,28 +659,28 @@ void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) } switch (opts->group.type) { - case kObjectTypeNil: - break; - case kObjectTypeString: - au_group = augroup_find(opts->group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", opts->group.data.string.data); - goto cleanup; - } - break; - case kObjectTypeInteger: - au_group = (int)opts->group.data.integer; - char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); - goto cleanup; - } - break; - default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + case kObjectTypeNil: + break; + case kObjectTypeString: + au_group = augroup_find(opts->group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", opts->group.data.string.data); goto cleanup; + } + break; + case kObjectTypeInteger: + au_group = (int)opts->group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + goto cleanup; + } + break; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + goto cleanup; } if (opts->buffer.type != kObjectTypeNil) { diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 3bddbe8fe6..a3af51008f 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -965,8 +965,7 @@ void nvim_buf_set_keymap(uint64_t channel_id, Buffer buffer, String mode, String /// @see |nvim_del_keymap()| /// /// @param buffer Buffer handle, or 0 for current buffer -void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, - String lhs, Error *err) +void nvim_buf_del_keymap(uint64_t channel_id, Buffer buffer, String mode, String lhs, Error *err) FUNC_API_SINCE(6) { String rhs = { .data = "", .size = 0 }; @@ -1380,8 +1379,8 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) /// @param buffer Buffer handle, or 0 for current buffer. /// @param[out] err Error details, if any. /// @see nvim_add_user_command -void nvim_buf_add_user_command(Buffer buffer, String name, Object command, - Dict(user_command) *opts, Error *err) +void nvim_buf_add_user_command(Buffer buffer, String name, Object command, Dict(user_command) *opts, + Error *err) FUNC_API_SINCE(9) { buf_T *target_buf = find_buffer_by_handle(buffer, err); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1a324bfaa5..b2d866ae0e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -722,15 +722,15 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) break; case 2: switch (numval) { - case 0: - case 1: - rv = BOOLEAN_OBJ(numval); - break; - default: - // Boolean options that return something other than 0 or 1 should return nil. Currently this - // only applies to 'autoread' which uses -1 as a local value to indicate "unset" - rv = NIL; - break; + case 0: + case 1: + rv = BOOLEAN_OBJ(numval); + break; + default: + // Boolean options that return something other than 0 or 1 should return nil. Currently this + // only applies to 'autoread' which uses -1 as a local value to indicate "unset" + rv = NIL; + break; } break; default: diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2bd14e2103..ecb334c895 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -34,9 +34,9 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/digraph.h" -#include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -4508,7 +4508,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use char_u *start = stl_items[stl_separator_locations[i]].start; char_u *seploc = start + dislocation; STRMOVE(seploc, start); - for (char_u *s = start; s < seploc; ) { + for (char_u *s = start; s < seploc;) { MB_CHAR2BYTES(fillchar, s); } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 2277a0ef1c..c186a1b1fd 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -106,10 +106,10 @@ EXTERN bool provider_active INIT(= false); static inline bool decor_has_sign(Decoration *decor) { return decor->sign_text - || decor->sign_hl_id - || decor->number_hl_id - || decor->line_hl_id - || decor->cursorline_hl_id; + || decor->sign_hl_id + || decor->number_hl_id + || decor->line_hl_id + || decor->cursorline_hl_id; } #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8cdb03c341..83223cdd5c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2710,7 +2710,7 @@ bool next_for_item(void *fi_void, char_u *arg) tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len); fi->fi_byte_idx += len; const int result - = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; xfree(tv.vval.v_string); return result; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index d492c67877..2a432ecb47 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1171,10 +1171,10 @@ void callback_put(Callback *cb, typval_T *tv) func_ref(cb->data.funcref); break; case kCallbackLua: - // TODO(tjdevries): Unified Callback. - // At this point this isn't possible, but it'd be nice to put - // these handled more neatly in one place. - // So instead, we just do the default and put nil + // TODO(tjdevries): Unified Callback. + // At this point this isn't possible, but it'd be nice to put + // these handled more neatly in one place. + // So instead, we just do the default and put nil default: tv->v_type = VAR_SPECIAL; tv->vval.v_special = kSpecialVarNull; diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 3c43d1f98d..e51689543f 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -11,8 +11,8 @@ #include "nvim/event/loop.h" #include "nvim/event/rstream.h" #include "nvim/log.h" -#include "nvim/memory.h" #include "nvim/main.h" +#include "nvim/memory.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index abf6ec347b..be9f97e27d 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -1,8 +1,8 @@ #ifndef NVIM_EX_DOCMD_H #define NVIM_EX_DOCMD_H -#include "nvim/ex_cmds_defs.h" #include "nvim/eval/funcs.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/globals.h" // flags for do_cmdline() diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 2916e3e978..6b879f5139 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -118,7 +118,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col } mtkey_t mark = { { row, col }, ns_id, id, 0, - mt_flags(right_gravity, decor_level), 0, NULL }; + mt_flags(right_gravity, decor_level), 0, NULL }; if (decor_full) { mark.decor_full = decor; } else if (decor) { diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h index af9526cd43..b856a1148f 100644 --- a/src/nvim/extmark.h +++ b/src/nvim/extmark.h @@ -2,8 +2,8 @@ #define NVIM_EXTMARK_H #include "nvim/buffer_defs.h" -#include "nvim/extmark_defs.h" #include "nvim/decoration.h" +#include "nvim/extmark_defs.h" #include "nvim/marktree.h" #include "nvim/pos.h" diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index a1fd0d0d66..3aa9190db6 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -819,9 +819,9 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e bool cterm_mask_provided = false; #define CHECK_FLAG(d, m, name, extra, flag) \ - if (api_object_to_bool(d->name ## extra, #name, false, err)) { \ - m = m | flag; \ - } + if (api_object_to_bool(d->name##extra, #name, false, err)) { \ + m = m | flag; \ + } CHECK_FLAG(dict, mask, bold, , HL_BOLD); CHECK_FLAG(dict, mask, standout, , HL_STANDOUT); @@ -906,7 +906,6 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e CHECK_FLAG(cterm, cterm_mask, reverse, , HL_INVERSE); CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH); CHECK_FLAG(cterm, cterm_mask, nocombine, , HL_NOCOMBINE); - } else if (dict->cterm.type == kObjectTypeArray && dict->cterm.data.array.size == 0) { // empty list from Lua API should clear all cterm attributes // TODO(clason): handle via gen_api_dispatch diff --git a/src/nvim/input.c b/src/nvim/input.c index 5fa9b8b343..ff6b559710 100644 --- a/src/nvim/input.c +++ b/src/nvim/input.c @@ -9,9 +9,9 @@ #include "nvim/func_attr.h" #include "nvim/getchar.h" +#include "nvim/input.h" #include "nvim/mbyte.h" #include "nvim/memory.h" -#include "nvim/input.h" #include "nvim/mouse.h" #include "nvim/os/input.h" #include "nvim/ui.h" diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 6aaff100ca..054f6c9483 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -15,8 +15,8 @@ #include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/cursor.h" -#include "nvim/eval/userfunc.h" #include "nvim/eval/typval.h" +#include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" #include "nvim/event/time.h" #include "nvim/ex_cmds2.h" @@ -139,17 +139,17 @@ static void nlua_luv_error_event(void **argv) luv_err_t type = (luv_err_t)(intptr_t)argv[1]; msg_ext_set_kind("lua_error"); switch (type) { - case kCallback: - semsg_multiline("Error executing luv callback:\n%s", error); - break; - case kThread: - semsg_multiline("Error in luv thread:\n%s", error); - break; - case kThreadCallback: - semsg_multiline("Error in luv callback, thread:\n%s", error); - break; - default: - break; + case kCallback: + semsg_multiline("Error executing luv callback:\n%s", error); + break; + case kThread: + semsg_multiline("Error in luv thread:\n%s", error); + break; + case kThreadCallback: + semsg_multiline("Error in luv callback, thread:\n%s", error); + break; + default: + break; } xfree(error); } @@ -189,21 +189,18 @@ static int nlua_luv_cfpcall(lua_State *lstate, int nargs, int nresult, int flags return retval; } -static int nlua_luv_thread_cb_cfpcall(lua_State *lstate, int nargs, int nresult, - int flags) +static int nlua_luv_thread_cb_cfpcall(lua_State *lstate, int nargs, int nresult, int flags) { return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, true); } -static int nlua_luv_thread_cfpcall(lua_State *lstate, int nargs, int nresult, - int flags) +static int nlua_luv_thread_cfpcall(lua_State *lstate, int nargs, int nresult, int flags) FUNC_ATTR_NONNULL_ALL { return nlua_luv_thread_common_cfpcall(lstate, nargs, nresult, flags, false); } -static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func, - void *ud, int flags) +static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func, void *ud, int flags) FUNC_ATTR_NONNULL_ARG(1, 2) { lua_pushcfunction(lstate, func); @@ -212,8 +209,8 @@ static int nlua_luv_thread_cfcpcall(lua_State *lstate, lua_CFunction func, return retval; } -static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nresult, - int flags, bool is_callback) +static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nresult, int flags, + bool is_callback) FUNC_ATTR_NONNULL_ALL { int retval; @@ -228,9 +225,9 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres mch_errmsg("\n"); lua_close(lstate); #ifdef WIN32 - ExitThread(0); + ExitThread(0); #else - pthread_exit(0); + pthread_exit(0); #endif } const char *error = lua_tostring(lstate, -1); @@ -565,9 +562,9 @@ static bool nlua_init_packages(lua_State *lstate) lua_getglobal(lstate, "require"); lua_pushstring(lstate, "vim._init_packages"); if (nlua_pcall(lstate, 1, 0)) { - mch_errmsg(lua_tostring(lstate, -1)); - mch_errmsg("\n"); - return false; + mch_errmsg(lua_tostring(lstate, -1)); + mch_errmsg("\n"); + return false; } return true; diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h index d978dc55d3..e96494ec5a 100644 --- a/src/nvim/lua/executor.h +++ b/src/nvim/lua/executor.h @@ -4,8 +4,8 @@ #include #include -#include "nvim/assert.h" #include "nvim/api/private/defs.h" +#include "nvim/assert.h" #include "nvim/eval/typval.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" @@ -16,9 +16,9 @@ void nlua_add_api_functions(lua_State *lstate) REAL_FATTR_NONNULL_ALL; typedef struct { - LuaRef nil_ref; - LuaRef empty_dict_ref; - int ref_count; + LuaRef nil_ref; + LuaRef empty_dict_ref; + int ref_count; #if __has_feature(address_sanitizer) PMap(handle_T) ref_markers; #endif diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c index 3a63f61200..31a2b2d19f 100644 --- a/src/nvim/lua/spell.c +++ b/src/nvim/lua/spell.c @@ -1,12 +1,12 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include #include +#include +#include "nvim/lua/spell.h" #include "nvim/spell.h" #include "nvim/vim.h" -#include "nvim/lua/spell.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "lua/spell.c.generated.h" @@ -45,7 +45,7 @@ int nlua_spell_check(lua_State *lstate) size_t pos = 0; int capcol = -1; int no_res = 0; - const char * result; + const char *result; lua_createtable(lstate, 0, 0); @@ -90,7 +90,7 @@ int nlua_spell_check(lua_State *lstate) static const luaL_Reg spell_functions[] = { { "check", nlua_spell_check }, - { NULL , NULL } + { NULL, NULL } }; int luaopen_spell(lua_State *L) diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c index c2ce899a74..e94c61b37c 100644 --- a/src/nvim/lua/stdlib.c +++ b/src/nvim/lua/stdlib.c @@ -28,10 +28,10 @@ #include "nvim/globals.h" #include "nvim/lua/converter.h" #include "nvim/lua/executor.h" +#include "nvim/lua/spell.h" #include "nvim/lua/stdlib.h" #include "nvim/lua/treesitter.h" #include "nvim/lua/xdiff.h" -#include "nvim/lua/spell.h" #include "nvim/macros.h" #include "nvim/map.h" #include "nvim/memline.h" @@ -411,7 +411,7 @@ int nlua_getvar(lua_State *lstate) dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len); if (di == NULL && dict == &globvardict) { // try to autoload script if (!script_autoload(name, len, false) || aborting()) { - return 0; // nil + return 0; // nil } di = tv_dict_find(dict, name, (ptrdiff_t)len); } diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c index 4456b28293..937582572b 100644 --- a/src/nvim/marktree.c +++ b/src/nvim/marktree.c @@ -558,7 +558,7 @@ void marktree_revise(MarkTree *b, MarkTreeIter *itr, uint8_t decor_level, mtkey_ { // TODO(bfredl): clean up this mess and re-instantiate &= and |= forms // once we upgrade to a non-broken version of gcc in functionaltest-lua CI - rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t)~MT_FLAG_DECOR_MASK); + rawkey(itr).flags = (uint16_t)(rawkey(itr).flags & (uint16_t) ~MT_FLAG_DECOR_MASK); rawkey(itr).flags = (uint16_t)(rawkey(itr).flags | (uint16_t)(decor_level << MT_FLAG_DECOR_OFFSET) | (uint16_t)(key.flags & MT_FLAG_DECOR_MASK)); @@ -1111,7 +1111,7 @@ static void marktree_itr_fix_pos(MarkTree *b, MarkTreeIter *itr) void marktree_put_test(MarkTree *b, uint32_t id, int row, int col, bool right_gravity) { mtkey_t key = { { row, col }, UINT32_MAX, id, 0, - mt_flags(right_gravity, 0), 0, NULL }; + mt_flags(right_gravity, 0), 0, NULL }; marktree_put(b, key, -1, -1, false); } diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h index 440816930b..ae6da92106 100644 --- a/src/nvim/marktree.h +++ b/src/nvim/marktree.h @@ -1,14 +1,14 @@ #ifndef NVIM_MARKTREE_H #define NVIM_MARKTREE_H -#include #include +#include #include "nvim/assert.h" #include "nvim/garray.h" #include "nvim/map.h" -#include "nvim/types.h" #include "nvim/pos.h" +#include "nvim/types.h" #define MT_MAX_DEPTH 20 #define MT_BRANCH_FACTOR 10 diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index dce4b0c187..a4361859ec 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -14,7 +14,7 @@ #endif #if !defined(NAME_MAX) && defined(_XOPEN_NAME_MAX) -#define NAME_MAX _XOPEN_NAME_MAX +# define NAME_MAX _XOPEN_NAME_MAX #endif #define BASENAMELEN (NAME_MAX - 5) diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 3459646bad..58cb1b8f84 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -16,11 +16,11 @@ #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include #elif defined(__sun) -# include -# include -# include -# include -# include +# include +# include +# include +# include +# include #else # include #endif @@ -49,10 +49,10 @@ // this header defines STR, just as nvim.h, but it is defined as ('S'<<8), // to avoid #undef STR, #undef STR, #define STR ('S'<<8) just delay the // inclusion of the header even though it gets include out of order. -#include +# include -static int openpty(int *amaster, int *aslave, char *name, - struct termios *termp, struct winsize *winp) +static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, + struct winsize *winp) { int slave = -1; int master = open("/dev/ptmx", O_RDWR); @@ -63,7 +63,7 @@ static int openpty(int *amaster, int *aslave, char *name, // grantpt will invoke a setuid program to change permissions // and might fail if SIGCHLD handler is set, temporarily reset // while running - void(*sig_saved)(int) = signal(SIGCHLD, SIG_DFL); + void (*sig_saved)(int) = signal(SIGCHLD, SIG_DFL); int res = grantpt(master); signal(SIGCHLD, sig_saved); @@ -129,8 +129,7 @@ static int login_tty(int fd) return 0; } -static pid_t forkpty(int *amaster, char *name, - struct termios *termp, struct winsize *winp) +static pid_t forkpty(int *amaster, char *name, struct termios *termp, struct winsize *winp) { int master, slave; if (openpty(&master, &slave, name, termp, winp) == -1) { diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index e618b2788b..656e059303 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -9,10 +9,10 @@ #include "nvim/ascii.h" #include "nvim/charset.h" +#include "nvim/eval.h" #include "nvim/event/libuv_process.h" #include "nvim/event/loop.h" #include "nvim/event/rstream.h" -#include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/fileio.h" #include "nvim/lib/kvec.h" @@ -27,8 +27,8 @@ #include "nvim/path.h" #include "nvim/screen.h" #include "nvim/strings.h" -#include "nvim/types.h" #include "nvim/tag.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7e29aed51b..bddbd04af9 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -5421,9 +5421,8 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args) /// Search for a pattern in a list of files and populate the quickfix list with /// the matches. -static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, - bool *redraw_for_dummy, buf_T **first_match_buf, - char_u **target_dir) +static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, bool *redraw_for_dummy, + buf_T **first_match_buf, char_u **target_dir) { int status = FAIL; unsigned save_qfid = qf_get_curlist(qi)->qf_id; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3a87cba7f9..c8092c3503 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4680,9 +4680,9 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) // @param[in, out] sign_idxp Index of the displayed sign static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[], int row, int startrow, int filler_lines, int filler_todo, - int *c_extrap, int *c_finalp, char_u *extra, - size_t extra_size, char_u **pp_extra, int *n_extrap, - int *char_attrp, int *draw_statep, int *sign_idxp) + int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, + char_u **pp_extra, int *n_extrap, int *char_attrp, + int *draw_statep, int *sign_idxp) { int count = wp->w_scwidth; // Draw cells with the sign value or blank. diff --git a/src/nvim/search.c b/src/nvim/search.c index e6b47e75b2..cc7c2ecf06 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -13,8 +13,8 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" -#include "nvim/charset.h" #include "nvim/change.h" +#include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -5048,8 +5048,8 @@ bool fuzzy_match(char_u *const str, const char_u *const pat_arg, const bool matc int score = 0; int recursionCount = 0; const int matchCount - = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches, - maxMatches - numMatches, 0, &recursionCount); + = fuzzy_match_recursive(pat, str, 0, &score, str, len, NULL, matches + numMatches, + maxMatches - numMatches, 0, &recursionCount); if (matchCount == 0) { numMatches = 0; break; diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 6e0e9922a6..5d632b1b25 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -217,9 +217,9 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext() #define STRLEN(s) strlen((char *)(s)) #ifdef HAVE_STRNLEN -# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n)) +# define STRNLEN(s, n) strnlen((char *)(s), (size_t)(n)) #else -# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n)) +# define STRNLEN(s, n) xstrnlen((char *)(s), (size_t)(n)) #endif #define STRCPY(d, s) strcpy((char *)(d), (char *)(s)) #define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n)) -- cgit From d2d3be0a4a65f623e95b2eac1abf215233ba9f00 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Mar 2022 16:40:45 +0800 Subject: vim-patch:8.2.3949: using freed memory with /\%V Problem: Using freed memory with /\%V. Solution: Get the line again after getvvcol(). https://github.com/vim/vim/commit/4c13e5e6763c6eb36a343a2b8235ea227202e952 --- src/nvim/regexp.c | 10 +++++++--- src/nvim/testdir/test_regexp_latin.vim | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 412cdac21b..009a26d4e0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -1136,8 +1136,8 @@ static bool reg_match_visual(void) return false; } + col = (colnr_T)(rex.input - rex.line); if (mode == 'v') { - col = (colnr_T)(rex.input - rex.line); if ((lnum == top.lnum && col < top.col) || (lnum == bot.lnum && col >= bot.col + (*p_sel != 'e'))) { return false; @@ -1152,8 +1152,12 @@ static bool reg_match_visual(void) if (top.col == MAXCOL || bot.col == MAXCOL || curswant == MAXCOL) { end = MAXCOL; } - unsigned int cols_u = win_linetabsize(wp, rex.line, - (colnr_T)(rex.input - rex.line)); + + // getvvcol() flushes rex.line, need to get it again + rex.line = reg_getline(rex.lnum); + rex.input = rex.line + col; + + unsigned int cols_u = win_linetabsize(wp, rex.line, col); assert(cols_u <= MAXCOL); colnr_T cols = (colnr_T)cols_u; if (cols < start || cols > end - (*p_sel == 'e')) { diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index a92f7e1192..538b078999 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -795,4 +795,12 @@ func Test_using_mark_position() bwipe! endfunc +func Test_using_visual_position() + " this was using freed memory + new + exe "norm 0o\\k\o0" + /\%V + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From c3208feb72234840643a04d2282c37276fd53cda Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 10 Mar 2022 16:43:32 +0800 Subject: vim-patch:8.2.3950: going beyond the end of the line with /\%V Problem: Going beyond the end of the line with /\%V. Solution: Check for valid column in getvcol(). https://github.com/vim/vim/commit/94f3192b03ed27474db80b4d3a409e107140738b --- src/nvim/charset.c | 10 ++++++---- src/nvim/testdir/test_regexp_latin.vim | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 97aac67627..3383dd2a76 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -928,10 +928,12 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // continue until the NUL posptr = NULL; } else { - // Special check for an empty line, which can happen on exit, when - // ml_get_buf() always returns an empty string. - if (*ptr == NUL) { - pos->col = 0; + // In a few cases the position can be beyond the end of the line. + for (colnr_T i = 0; i < pos->col; i++) { + if (ptr[i] == NUL) { + pos->col = i; + break; + } } posptr = ptr + pos->col; posptr -= utf_head_off(line, posptr); diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 538b078999..a0f5ebfb9f 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -803,4 +803,12 @@ func Test_using_visual_position() bwipe! endfunc +func Test_using_invalid_visual_position() + " this was going beyond the end of the line + new + exe "norm 0o000\0\$s0" + /\%V + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 1b054119ec1a7208b49feeaa496c2e1d55252989 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 10 Mar 2022 15:17:06 +0000 Subject: refactor(decorations): move provider code Move decoration provider code to a separate file. --- src/nvim/api/extmark.c | 1 + src/nvim/api/vim.c | 1 + src/nvim/decoration.c | 53 ---------- src/nvim/decoration.h | 19 ---- src/nvim/decoration_provider.c | 224 +++++++++++++++++++++++++++++++++++++++++ src/nvim/decoration_provider.h | 33 ++++++ src/nvim/highlight.c | 1 + src/nvim/main.c | 1 + src/nvim/memory.c | 2 +- src/nvim/screen.c | 148 +++++---------------------- 10 files changed, 285 insertions(+), 198 deletions(-) create mode 100644 src/nvim/decoration_provider.c create mode 100644 src/nvim/decoration_provider.h (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 0ee8134ec4..e355f82f4d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -8,6 +8,7 @@ #include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/decoration_provider.h" #include "nvim/extmark.h" #include "nvim/lua/executor.h" #include "nvim/memline.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 1a324bfaa5..1573c6e0e3 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -22,6 +22,7 @@ #include "nvim/charset.h" #include "nvim/context.h" #include "nvim/decoration.h" +#include "nvim/decoration_provider.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index b533f4c46f..94bf1feeee 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -523,59 +523,6 @@ void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col, } -DecorProvider *get_decor_provider(NS ns_id, bool force) -{ - size_t i; - size_t len = kv_size(decor_providers); - for (i = 0; i < len; i++) { - DecorProvider *item = &kv_A(decor_providers, i); - if (item->ns_id == ns_id) { - return item; - } else if (item->ns_id > ns_id) { - break; - } - } - - if (!force) { - return NULL; - } - - // Adding a new provider, so allocate room in the vector - (void)kv_a(decor_providers, len); - if (i < len) { - // New ns_id needs to be inserted between existing providers to maintain - // ordering, so shift other providers with larger ns_id - memmove(&kv_A(decor_providers, i + 1), - &kv_A(decor_providers, i), - (len - i) * sizeof(kv_a(decor_providers, i))); - } - DecorProvider *item = &kv_a(decor_providers, i); - *item = DECORATION_PROVIDER_INIT(ns_id); - - return item; -} - -void decor_provider_clear(DecorProvider *p) -{ - if (p == NULL) { - return; - } - NLUA_CLEAR_REF(p->redraw_start); - NLUA_CLEAR_REF(p->redraw_buf); - NLUA_CLEAR_REF(p->redraw_win); - NLUA_CLEAR_REF(p->redraw_line); - NLUA_CLEAR_REF(p->redraw_end); - p->active = false; -} - -void decor_free_all_mem(void) -{ - for (size_t i = 0; i < kv_size(decor_providers); i++) { - decor_provider_clear(&kv_A(decor_providers, i)); - } - kv_destroy(decor_providers); -} - int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines) { diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 2277a0ef1c..ca41a8f360 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -82,26 +82,7 @@ typedef struct { int eol_col; } DecorState; -typedef struct { - NS ns_id; - bool active; - LuaRef redraw_start; - LuaRef redraw_buf; - LuaRef redraw_win; - LuaRef redraw_line; - LuaRef redraw_end; - LuaRef hl_def; - int hl_valid; -} DecorProvider; - -EXTERN kvec_t(DecorProvider) decor_providers INIT(= KV_INITIAL_VALUE); EXTERN DecorState decor_state INIT(= { 0 }); -EXTERN bool provider_active INIT(= false); - -#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ - { ns_id, false, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, -1 } static inline bool decor_has_sign(Decoration *decor) { diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c new file mode 100644 index 0000000000..e72e1e1a5a --- /dev/null +++ b/src/nvim/decoration_provider.c @@ -0,0 +1,224 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/api/extmark.h" +#include "nvim/api/private/helpers.h" +#include "nvim/buffer.h" +#include "nvim/decoration.h" +#include "nvim/decoration_provider.h" +#include "nvim/highlight.h" +#include "nvim/lua/executor.h" + +static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, + Array args, bool default_true, char **perr) +{ + Error err = ERROR_INIT; + + textlock++; + provider_active = true; + Object ret = nlua_call_ref(ref, name, args, true, &err); + provider_active = false; + textlock--; + + if (!ERROR_SET(&err) + && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { + return true; + } + + if (ERROR_SET(&err)) { + const char *ns_name = describe_ns(ns_id); + ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); + bool verbose_errs = true; // TODO(bfredl): + if (verbose_errs && perr && *perr == NULL) { + static char errbuf[IOSIZE]; + snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); + *perr = xstrdup(errbuf); + } + } + + api_free_object(ret); + return false; +} + +/// For each provider invoke the 'start' callback +/// +/// @param[out] providers Decoration providers +/// @param[out] err Provider err +void decor_providers_start(DecorProviders *providers, int type, char **err) +{ + kvi_init(*providers); + + for (size_t i = 0; i < kv_size(decor_providers); i++) { + DecorProvider *p = &kv_A(decor_providers, i); + if (!p->active) { + continue; + } + + bool active; + if (p->redraw_start != LUA_NOREF) { + FIXED_TEMP_ARRAY(args, 2); + args.items[0] = INTEGER_OBJ((int)display_tick); + args.items[1] = INTEGER_OBJ(type); + active = decor_provider_invoke(p->ns_id, "start", p->redraw_start, args, true, err); + } else { + active = true; + } + + if (active) { + kvi_push(*providers, p); + } + } +} + +/// For each provider run 'win'. If result is not false, then collect the +/// 'on_line' callback to call inside win_line +/// +/// @param wp Window +/// @param providers Decoration providers +/// @param[out] line_providers Enabled line providers to invoke in win_line +/// @param[out] err Provider error +void decor_providers_invoke_win(win_T *wp, DecorProviders *providers, + DecorProviders *line_providers, char **err) +{ + kvi_init(*line_providers); + + linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE) + ? wp->w_botline + : (wp->w_topline + wp->w_height_inner)); + + for (size_t k = 0; k < kv_size(*providers); k++) { + DecorProvider *p = kv_A(*providers, k); + if (p && p->redraw_win != LUA_NOREF) { + FIXED_TEMP_ARRAY(args, 4); + args.items[0] = WINDOW_OBJ(wp->handle); + args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); + // TODO(bfredl): we are not using this, but should be first drawn line? + args.items[2] = INTEGER_OBJ(wp->w_topline-1); + args.items[3] = INTEGER_OBJ(knownmax); + if (decor_provider_invoke(p->ns_id, "win", p->redraw_win, args, true, err)) { + kvi_push(*line_providers, p); + } + } + } + + win_check_ns_hl(wp); +} + +/// For each provider invoke the 'line' callback for a given window row. +/// +/// @param wp Window +/// @param providers Decoration providers +/// @param row Row to invoke line callback for +/// @param[out] has_decor Set when at least one provider invokes a line callback +/// @param[out] err Provider error +void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *has_decor, + char **err) +{ + for (size_t k = 0; k < kv_size(*providers); k++) { + DecorProvider *p = kv_A(*providers, k); + if (p && p->redraw_line != LUA_NOREF) { + FIXED_TEMP_ARRAY(args, 3); + args.items[0] = WINDOW_OBJ(wp->handle); + args.items[1] = BUFFER_OBJ(wp->w_buffer->handle); + args.items[2] = INTEGER_OBJ(row); + if (decor_provider_invoke(p->ns_id, "line", p->redraw_line, args, true, err)) { + *has_decor = true; + } else { + // return 'false' or error: skip rest of this window + kv_A(*providers, k) = NULL; + } + + win_check_ns_hl(wp); + } + } +} + + +/// For each provider invoke the 'buf' callback for a given buffer. +/// +/// @param buf Buffer +/// @param providers Decoration providers +/// @param[out] err Provider error +void decor_providers_invoke_buf(buf_T *buf, DecorProviders *providers, char **err) +{ + for (size_t i = 0; i < kv_size(*providers); i++) { + DecorProvider *p = kv_A(*providers, i); + if (p && p->redraw_buf != LUA_NOREF) { + FIXED_TEMP_ARRAY(args, 1); + args.items[0] = BUFFER_OBJ(buf->handle); + decor_provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true, err); + } + } +} + + +/// For each provider invoke the 'end' callback +/// +/// @param providers Decoration providers +/// @param displaytick Display tick +/// @param[out] err Provider error +void decor_providers_invoke_end(DecorProviders *providers, char **err) +{ + for (size_t i = 0; i < kv_size(*providers); i++) { + DecorProvider *p = kv_A(*providers, i); + if (p && p->active && p->redraw_end != LUA_NOREF) { + FIXED_TEMP_ARRAY(args, 1); + args.items[0] = INTEGER_OBJ((int)display_tick); + decor_provider_invoke(p->ns_id, "end", p->redraw_end, args, true, err); + } + } +} + +DecorProvider *get_decor_provider(NS ns_id, bool force) +{ + size_t i; + size_t len = kv_size(decor_providers); + for (i = 0; i < len; i++) { + DecorProvider *item = &kv_A(decor_providers, i); + if (item->ns_id == ns_id) { + return item; + } else if (item->ns_id > ns_id) { + break; + } + } + + if (!force) { + return NULL; + } + + // Adding a new provider, so allocate room in the vector + (void)kv_a(decor_providers, len); + if (i < len) { + // New ns_id needs to be inserted between existing providers to maintain + // ordering, so shift other providers with larger ns_id + memmove(&kv_A(decor_providers, i + 1), + &kv_A(decor_providers, i), + (len - i) * sizeof(kv_a(decor_providers, i))); + } + DecorProvider *item = &kv_a(decor_providers, i); + *item = DECORATION_PROVIDER_INIT(ns_id); + + return item; +} + +void decor_provider_clear(DecorProvider *p) +{ + if (p == NULL) { + return; + } + NLUA_CLEAR_REF(p->redraw_start); + NLUA_CLEAR_REF(p->redraw_buf); + NLUA_CLEAR_REF(p->redraw_win); + NLUA_CLEAR_REF(p->redraw_line); + NLUA_CLEAR_REF(p->redraw_end); + p->active = false; +} + +void decor_free_all_mem(void) +{ + for (size_t i = 0; i < kv_size(decor_providers); i++) { + decor_provider_clear(&kv_A(decor_providers, i)); + } + kv_destroy(decor_providers); +} + diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h new file mode 100644 index 0000000000..f97beb346b --- /dev/null +++ b/src/nvim/decoration_provider.h @@ -0,0 +1,33 @@ +#ifndef NVIM_DECORATION_PROVIDER_H +#define NVIM_DECORATION_PROVIDER_H + +#include "nvim/buffer_defs.h" +#include "nvim/extmark_defs.h" + +typedef struct { + NS ns_id; + bool active; + LuaRef redraw_start; + LuaRef redraw_buf; + LuaRef redraw_win; + LuaRef redraw_line; + LuaRef redraw_end; + LuaRef hl_def; + int hl_valid; +} DecorProvider; + +#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ + { ns_id, false, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, -1 } + +typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders; + +EXTERN kvec_t(DecorProvider) decor_providers INIT(= KV_INITIAL_VALUE); +EXTERN bool provider_active INIT(= false); + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "decoration_provider.h.generated.h" +#endif + +#endif // NVIM_DECORATION_PROVIDER_H diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index a1fd0d0d66..2e6947d2e0 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -5,6 +5,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/decoration_provider.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/main.c b/src/nvim/main.c index 7281809c06..a575aba50a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -14,6 +14,7 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 677ff8f522..d68ca6b62e 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -10,7 +10,7 @@ #include "nvim/api/extmark.h" #include "nvim/context.h" -#include "nvim/decoration.h" +#include "nvim/decoration_provider.h" #include "nvim/eval.h" #include "nvim/highlight.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3a87cba7f9..4c440db124 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -75,6 +75,7 @@ #include "nvim/cursor.h" #include "nvim/cursor_shape.h" #include "nvim/decoration.h" +#include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -126,8 +127,6 @@ #define MB_FILLER_CHAR '<' /* character used when a double-width character * doesn't fit. */ -typedef kvec_withinit_t(DecorProvider *, 4) Providers; - // temporary buffer for rendering a single screenline, so it can be // compared with previous contents to calculate smallest delta. // Per-cell attributes @@ -168,36 +167,6 @@ static bool resizing = false; static char *provider_err = NULL; -static bool provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true) -{ - Error err = ERROR_INIT; - - textlock++; - provider_active = true; - Object ret = nlua_call_ref(ref, name, args, true, &err); - provider_active = false; - textlock--; - - if (!ERROR_SET(&err) - && api_object_to_bool(ret, "provider %s retval", default_true, &err)) { - return true; - } - - if (ERROR_SET(&err)) { - const char *ns_name = describe_ns(ns_id); - ELOG("error in provider %s:%s: %s", ns_name, name, err.msg); - bool verbose_errs = true; // TODO(bfredl): - if (verbose_errs && provider_err == NULL) { - static char errbuf[IOSIZE]; - snprintf(errbuf, sizeof errbuf, "%s: %s", ns_name, err.msg); - provider_err = xstrdup(errbuf); - } - } - - api_free_object(ret); - return false; -} - /// Redraw a window later, with update_screen(type). /// /// Set must_redraw only if not already set to a higher value. @@ -501,28 +470,8 @@ int update_screen(int type) ui_comp_set_screen_valid(true); - Providers providers; - kvi_init(providers); - for (size_t i = 0; i < kv_size(decor_providers); i++) { - DecorProvider *p = &kv_A(decor_providers, i); - if (!p->active) { - continue; - } - - bool active; - if (p->redraw_start != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 2); - args.items[0] = INTEGER_OBJ(display_tick); - args.items[1] = INTEGER_OBJ(type); - active = provider_invoke(p->ns_id, "start", p->redraw_start, args, true); - } else { - active = true; - } - - if (active) { - kvi_push(providers, p); - } - } + DecorProviders providers; + decor_providers_start(&providers, type, &provider_err); // "start" callback could have changed highlights for global elements if (win_check_ns_hl(NULL)) { @@ -591,14 +540,7 @@ int update_screen(int type) } if (buf->b_mod_tick_decor < display_tick) { - for (size_t i = 0; i < kv_size(providers); i++) { - DecorProvider *p = kv_A(providers, i); - if (p && p->redraw_buf != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = BUFFER_OBJ(buf->handle); - provider_invoke(p->ns_id, "buf", p->redraw_buf, args, true); - } - } + decor_providers_invoke_buf(buf, &providers, &provider_err); buf->b_mod_tick_decor = display_tick; } } @@ -668,18 +610,7 @@ int update_screen(int type) } did_intro = true; - for (size_t i = 0; i < kv_size(providers); i++) { - DecorProvider *p = kv_A(providers, i); - if (!p->active) { - continue; - } - - if (p->redraw_end != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 1); - args.items[0] = INTEGER_OBJ(display_tick); - provider_invoke(p->ns_id, "end", p->redraw_end, args, true); - } - } + decor_providers_invoke_end(&providers, &provider_err); kvi_destroy(providers); @@ -766,7 +697,7 @@ bool win_cursorline_standout(const win_T *wp) * mid: from mid_start to mid_end (update inversion or changed text) * bot: from bot_start to last row (when scrolled up) */ -static void win_update(win_T *wp, Providers *providers) +static void win_update(win_T *wp, DecorProviders *providers) { buf_T *buf = wp->w_buffer; int type; @@ -1377,30 +1308,8 @@ static void win_update(win_T *wp, Providers *providers) decor_redraw_reset(buf, &decor_state); - Providers line_providers; - kvi_init(line_providers); - - linenr_T knownmax = ((wp->w_valid & VALID_BOTLINE) - ? wp->w_botline - : (wp->w_topline + wp->w_height_inner)); - - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_win != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 4); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(buf->handle); - // TODO(bfredl): we are not using this, but should be first drawn line? - args.items[2] = INTEGER_OBJ(wp->w_topline-1); - args.items[3] = INTEGER_OBJ(knownmax); - if (provider_invoke(p->ns_id, "win", p->redraw_win, args, true)) { - kvi_push(line_providers, p); - } - } - } - - win_check_ns_hl(wp); - + DecorProviders line_providers; + decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); for (;;) { /* stop updating when reached the end of the window (check for _past_ @@ -2028,6 +1937,17 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ return MAX(char_counter + (fdc-i), (size_t)fdc); } +static inline void provider_err_virt_text(linenr_T lnum, char *err) +{ + Decoration err_decor = DECORATION_INIT; + int hl_err = syn_check_group(S_LEN("ErrorMsg")); + kv_push(err_decor.virt_text, + ((VirtTextChunk){ .text = provider_err, + .hl_id = hl_err })); + err_decor.virt_text_width = mb_string2cells((char_u *)err); + decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); +} + /// Display line "lnum" of window 'wp' on the screen. /// wp->w_virtcol needs to be valid. /// @@ -2043,7 +1963,7 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_ /// /// @return the number of last row the line occupies. static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, - bool number_only, foldinfo_T foldinfo, Providers *providers) + bool number_only, foldinfo_T foldinfo, DecorProviders *providers) { int c = 0; // init for GCC long vcol = 0; // virtual column (for tabs) @@ -2231,34 +2151,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc has_decor = decor_redraw_line(buf, lnum-1, &decor_state); - for (size_t k = 0; k < kv_size(*providers); k++) { - DecorProvider *p = kv_A(*providers, k); - if (p && p->redraw_line != LUA_NOREF) { - FIXED_TEMP_ARRAY(args, 3); - args.items[0] = WINDOW_OBJ(wp->handle); - args.items[1] = BUFFER_OBJ(buf->handle); - args.items[2] = INTEGER_OBJ(lnum-1); - if (provider_invoke(p->ns_id, "line", p->redraw_line, args, true)) { - has_decor = true; - } else { - // return 'false' or error: skip rest of this window - kv_A(*providers, k) = NULL; - } - - win_check_ns_hl(wp); - } - } + providers_invoke_line(wp, providers, lnum-1, &has_decor, &provider_err); if (provider_err) { - Decoration err_decor = DECORATION_INIT; - int hl_err = syn_check_group(S_LEN("ErrorMsg")); - kv_push(err_decor.virt_text, - ((VirtTextChunk){ .text = provider_err, - .hl_id = hl_err })); - err_decor.virt_text_width = mb_string2cells((char_u *)provider_err); - decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); - provider_err = NULL; + provider_err_virt_text(lnum, provider_err); has_decor = true; + provider_err = NULL; } if (has_decor) { -- cgit From 4be92ba90d96fa7b77e18bba5e1f1530afef9651 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 10 Mar 2022 22:26:41 +0000 Subject: refactor(decorations): unglobal decor_providers Now all the decoration provider code is in one place, decor_providers no longer need to be global. --- src/nvim/decoration_provider.c | 7 +++++++ src/nvim/decoration_provider.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c index e72e1e1a5a..6efcb08b9d 100644 --- a/src/nvim/decoration_provider.c +++ b/src/nvim/decoration_provider.c @@ -9,6 +9,13 @@ #include "nvim/highlight.h" #include "nvim/lua/executor.h" +static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE; + +#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ + { ns_id, false, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, LUA_NOREF, LUA_NOREF, \ + LUA_NOREF, -1 } + static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args, bool default_true, char **perr) { diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h index f97beb346b..3ec7c80357 100644 --- a/src/nvim/decoration_provider.h +++ b/src/nvim/decoration_provider.h @@ -2,7 +2,6 @@ #define NVIM_DECORATION_PROVIDER_H #include "nvim/buffer_defs.h" -#include "nvim/extmark_defs.h" typedef struct { NS ns_id; @@ -16,14 +15,8 @@ typedef struct { int hl_valid; } DecorProvider; -#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \ - { ns_id, false, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, -1 } - typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders; -EXTERN kvec_t(DecorProvider) decor_providers INIT(= KV_INITIAL_VALUE); EXTERN bool provider_active INIT(= false); #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 9ff4acbb36c8a914b48b85e345ddb11cc01277ac Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Thu, 10 Mar 2022 17:34:41 +0100 Subject: refactor: fix all clint warnings from buffer.c --- src/nvim/buffer.c | 437 ++++++++++++++++++++++-------------------------------- 1 file changed, 179 insertions(+), 258 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2bd14e2103..2e2459aecf 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -34,9 +34,9 @@ #include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/digraph.h" -#include "nvim/decoration.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" @@ -223,7 +223,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags) || (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname)) # endif - )) { + )) { // NOLINT(whitespace/parens) read_fifo = true; } if (read_fifo) { @@ -401,7 +401,7 @@ bool buf_valid(buf_T *buf) /// there to be only one window with this buffer. e.g. when /// ":quit" is supposed to close the window but autocommands /// close all other windows. -/// @returns true when we got to the end and b_nwindows was decremented. +/// @return true when we got to the end and b_nwindows was decremented. bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) { bool unload_buf = (action != 0); @@ -587,9 +587,7 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) // No need to check `unload_buf`: in that case the function returned above. buf_updates_unload(buf, false); - /* - * Remove the buffer from the list. - */ + // Remove the buffer from the list. if (wipe_buf) { if (buf->b_sfname != buf->b_ffname) { XFREE_CLEAR(buf->b_sfname); @@ -737,10 +735,8 @@ void buf_freeall(buf_T *buf, int flags) buf->b_flags &= ~BF_READERR; // a read error is no longer relevant } -/* - * Free a buffer structure and the things it contains related to the buffer - * itself (not the file, that must have been done already). - */ +/// Free a buffer structure and the things it contains related to the buffer +/// itself (not the file, that must have been done already). static void free_buffer(buf_T *buf) { pmap_del(handle_T)(&buffer_handles, buf->b_fnum); @@ -813,9 +809,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) buf_updates_unload(buf, false); } -/* - * Free the b_wininfo list for buffer "buf". - */ +/// Free the b_wininfo list for buffer "buf". static void clear_wininfo(buf_T *buf) { wininfo_T *wip; @@ -827,9 +821,7 @@ static void clear_wininfo(buf_T *buf) } } -/* - * Go to another buffer. Handles the result of the ATTENTION dialog. - */ +/// Go to another buffer. Handles the result of the ATTENTION dialog. void goto_buffer(exarg_T *eap, int start, int dir, int count) { bufref_T old_curbuf; @@ -1033,10 +1025,8 @@ char *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end } -/* - * Make the current buffer empty. - * Used when it is wiped out and it's the last buffer. - */ +/// Make the current buffer empty. +/// Used when it is wiped out and it's the last buffer. static int empty_curbuf(int close_others, int forceit, int action) { int retval; @@ -1410,15 +1400,15 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } -/* - * Set current buffer to "buf". Executes autocommands and closes current - * buffer. "action" tells how to close the current buffer: - * DOBUF_GOTO free or hide it - * DOBUF_SPLIT nothing - * DOBUF_UNLOAD unload it - * DOBUF_DEL delete it - * DOBUF_WIPE wipe it out - */ +/// Set current buffer to "buf". Executes autocommands and closes current +/// buffer. +/// +/// @param action tells how to close the current buffer: +/// DOBUF_GOTO free or hide it +/// DOBUF_SPLIT nothing +/// DOBUF_UNLOAD unload it +/// DOBUF_DEL delete it +/// DOBUF_WIPE wipe it out void set_curbuf(buf_T *buf, int action) { buf_T *prevbuf; @@ -1478,9 +1468,7 @@ void set_curbuf(buf_T *buf, int action) // An autocommand may have deleted "buf", already entered it (e.g., when // it did ":bunload") or aborted the script processing! // If curwin->w_buffer is null, enter_buffer() will make it valid again - if ((buf_valid(buf) && buf != curbuf - && !aborting() - ) || curwin->w_buffer == NULL) { + if ((buf_valid(buf) && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) { enter_buffer(buf); if (old_tw != curbuf->b_p_tw) { check_colorcolumn(curwin); @@ -1492,11 +1480,9 @@ void set_curbuf(buf_T *buf, int action) } } -/* - * Enter a new current buffer. - * Old curbuf must have been abandoned already! This also means "curbuf" may - * be pointing to freed memory. - */ +/// Enter a new current buffer. +/// Old curbuf must have been abandoned already! This also means "curbuf" may +/// be pointing to freed memory. void enter_buffer(buf_T *buf) { // Get the buffer in the current window. @@ -1583,8 +1569,8 @@ void enter_buffer(buf_T *buf) redraw_later(curwin, NOT_VALID); } -// Change to the directory of the current buffer. -// Don't do this while still starting up. +/// Change to the directory of the current buffer. +/// Don't do this while still starting up. void do_autochdir(void) { if (p_acd) { @@ -1661,7 +1647,7 @@ static inline void buf_init_changedtick(buf_T *const buf) /// @param flags BLN_ defines /// @param bufnr /// -/// @return pointer to the buffer +/// @return pointer to the buffer buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int flags) { char_u *ffname = ffname_arg; @@ -1703,14 +1689,12 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl return buf; } - /* - * If the current buffer has no name and no contents, use the current - * buffer. Otherwise: Need to allocate a new buffer structure. - * - * This is the ONLY place where a new buffer structure is allocated! - * (A spell file buffer is allocated in spell.c, but that's not a normal - * buffer.) - */ + // If the current buffer has no name and no contents, use the current + // buffer. Otherwise: Need to allocate a new buffer structure. + // + // This is the ONLY place where a new buffer structure is allocated! + // (A spell file buffer is allocated in spell.c, but that's not a normal + // buffer.) buf = NULL; if ((flags & BLN_CURBUF) && curbuf_reusable()) { assert(curbuf != NULL); @@ -1781,9 +1765,7 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl // need to reload lmaps and set b:keymap_name curbuf->b_kmap_state |= KEYMAP_INIT; } else { - /* - * put new buffer at the end of the buffer list - */ + // put new buffer at the end of the buffer list buf->b_next = NULL; if (firstbuf == NULL) { // buffer list is empty buf->b_prev = NULL; @@ -1875,11 +1857,9 @@ bool curbuf_reusable(void) && !curbufIsChanged()); } -/* - * Free the memory for the options of a buffer. - * If "free_p_ff" is true also free 'fileformat', 'buftype' and - * 'fileencoding'. - */ +/// Free the memory for the options of a buffer. +/// If "free_p_ff" is true also free 'fileformat', 'buftype' and +/// 'fileencoding'. void free_buf_options(buf_T *buf, int free_p_ff) { if (free_p_ff) { @@ -2039,7 +2019,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) return FAIL; } -// Go to the last known line number for the current buffer. +/// Go to the last known line number for the current buffer. void buflist_getfpos(void) { pos_T *fpos; @@ -2059,10 +2039,9 @@ void buflist_getfpos(void) } } -/* - * Find file in buffer list by name (it has to be for the current window). - * Returns NULL if not found. - */ +/// Find file in buffer list by name (it has to be for the current window). +/// +/// @return buffer or NULL if not found buf_T *buflist_findname_exp(char_u *fname) { char_u *ffname; @@ -2076,7 +2055,7 @@ buf_T *buflist_findname_exp(char_u *fname) #else false #endif - ); + ); // NOLINT(whitespace/parens) if (ffname != NULL) { buf = buflist_findname(ffname); xfree(ffname); @@ -2084,12 +2063,11 @@ buf_T *buflist_findname_exp(char_u *fname) return buf; } -/* - * Find file in buffer list by name (it has to be for the current window). - * "ffname" must have a full path. - * Skips dummy buffers. - * Returns NULL if not found. - */ +/// Find file in buffer list by name (it has to be for the current window). +/// "ffname" must have a full path. +/// Skips dummy buffers. +/// +/// @return buffer or NULL if not found buf_T *buflist_findname(char_u *ffname) { FileID file_id; @@ -2097,11 +2075,10 @@ buf_T *buflist_findname(char_u *ffname) return buflist_findname_file_id(ffname, &file_id, file_id_valid); } -/* - * Same as buflist_findname(), but pass the FileID structure to avoid - * getting it twice for the same file. - * Returns NULL if not found. - */ +/// Same as buflist_findname(), but pass the FileID structure to avoid +/// getting it twice for the same file. +/// +/// @return buffer or NULL if not found static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool file_id_valid) { // Start at the last buffer, expect to find a match sooner. @@ -2115,13 +2092,13 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool fil } /// Find file in buffer list by a regexp pattern. -/// Return fnum of the found buffer. -/// Return < 0 for error. /// /// @param pattern_end pointer to first char after pattern /// @param unlisted find unlisted buffers /// @param diffmode find diff-mode buffers only /// @param curtab_only find buffers in current tab only +/// +/// @return fnum of the found buffer or < 0 for error. int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlisted, bool diffmode, bool curtab_only) FUNC_ATTR_NONNULL_ARG(1) @@ -2145,7 +2122,6 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis match = -1; } } else { - // // Try four ways of matching a listed buffer: // attempt == 0: without '^' or '$' (at any position) // attempt == 1: with '^' at start (only at position 0) @@ -2153,7 +2129,6 @@ int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlis // attempt == 3: with '^' at start and '$' at end (only full match) // Repeat this for finding an unlisted buffer if there was no matching // listed buffer. - // pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, false); if (pat == NULL) { @@ -2251,11 +2226,10 @@ static int buf_time_compare(const void *s1, const void *s2) return buf1->b_last_used > buf2->b_last_used ? -1 : 1; } -/* - * Find all buffer names that match. - * For command line expansion of ":buf" and ":sbuf". - * Return OK if matches found, FAIL otherwise. - */ +/// Find all buffer names that match. +/// For command line expansion of ":buf" and ":sbuf". +/// +/// @return OK if matches found, FAIL otherwise. int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) { int count = 0; @@ -2379,7 +2353,7 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) /// Check for a match on the file name for buffer "buf" with regprog "prog". /// -/// @param ignore_case When true, ignore case. Use 'fic' otherwise. +/// @param ignore_case When true, ignore case. Use 'fic' otherwise. static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case) { // First try the short file name, then the long file name. @@ -2392,8 +2366,9 @@ static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, bool ignore_case) /// Try matching the regexp in "prog" with file name "name". /// -/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise. -/// @return "name" when there is a match, NULL when not. +/// @param ignore_case When true, ignore case. Use 'fileignorecase' otherwise. +/// +/// @return "name" when there is a match, NULL when not. static char_u *fname_match(regmatch_T *rmp, char_u *name, bool ignore_case) { char_u *match = NULL; @@ -2528,12 +2503,13 @@ static bool wininfo_other_tab_diff(wininfo_T *wip) return false; } -// Find info for the current window in buffer "buf". -// If not found, return the info for the most recently used window. -// When "need_options" is true skip entries where wi_optset is false. -// When "skip_diff_buffer" is true avoid windows with 'diff' set that is in -// another tab page. -// Returns NULL when there isn't any info. +/// Find info for the current window in buffer "buf". +/// If not found, return the info for the most recently used window. +/// +/// @param need_options when true, skip entries where wi_optset is false. +/// @param skip_diff_buffer when true, avoid windows with 'diff' set that is in another tab page. +/// +/// @return NULL when there isn't any info. static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer) FUNC_ATTR_NONNULL_ALL { @@ -2570,12 +2546,10 @@ static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buf return wip; } -/* - * Reset the local window options to the values last used in this window. - * If the buffer wasn't used in this window before, use the values from - * the most recently used window. If the values were never set, use the - * global values for the window. - */ +/// Reset the local window options to the values last used in this window. +/// If the buffer wasn't used in this window before, use the values from +/// the most recently used window. If the values were never set, use the +/// global values for the window. void get_winopts(buf_T *buf) { clear_winopt(&curwin->w_onebuf_opt); @@ -2610,11 +2584,10 @@ void get_winopts(buf_T *buf) didset_window_options(curwin); } -/* - * Find the position (lnum and col) for the buffer 'buf' for the current - * window. - * Returns a pointer to no_position if no position is found. - */ +/// Find the position (lnum and col) for the buffer 'buf' for the current +/// window. +/// +/// @return a pointer to no_position if no position is found. pos_T *buflist_findfpos(buf_T *buf) { static pos_T no_position = { 1, 0, 0 }; @@ -2623,15 +2596,13 @@ pos_T *buflist_findfpos(buf_T *buf) return (wip == NULL) ? &no_position : &(wip->wi_fpos); } -/* - * Find the lnum for the buffer 'buf' for the current window. - */ +/// Find the lnum for the buffer 'buf' for the current window. linenr_T buflist_findlnum(buf_T *buf) { return buflist_findfpos(buf)->lnum; } -// List all known file names (for :files and :buffers command). +/// List all known file names (for :files and :buffers command). void buflist_list(exarg_T *eap) { buf_T *buf = firstbuf; @@ -2659,8 +2630,7 @@ void buflist_list(exarg_T *eap) for (; buf != NULL && !got_int; buf = buflist_data != NULL - ? (++p < buflist_data + buflist.ga_len ? *p : NULL) - : buf->b_next) { + ? (++p < buflist_data + buflist.ga_len ? *p : NULL) : buf->b_next) { const bool is_terminal = buf->terminal; const bool job_running = buf->terminal && terminal_running(buf->terminal); @@ -2723,10 +2693,8 @@ void buflist_list(exarg_T *eap) if (vim_strchr(eap->arg, 't') && buf->b_last_used) { undo_fmt_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used); } else { - vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), - _("line %" PRId64), - buf == curbuf ? (int64_t)curwin->w_cursor.lnum - : (int64_t)buflist_findlnum(buf)); + vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), _("line %" PRId64), + buf == curbuf ? (int64_t)curwin->w_cursor.lnum : (int64_t)buflist_findlnum(buf)); } msg_outtrans(IObuff); @@ -2738,12 +2706,11 @@ void buflist_list(exarg_T *eap) } } -/* - * Get file name and line number for file 'fnum'. - * Used by DoOneCmd() for translating '%' and '#'. - * Used by insert_reg() and cmdline_paste() for '#' register. - * Return FAIL if not found, OK for success. - */ +/// Get file name and line number for file 'fnum'. +/// Used by DoOneCmd() for translating '%' and '#'. +/// Used by insert_reg() and cmdline_paste() for '#' register. +/// +/// @return FAIL if not found, OK for success. int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum) { buf_T *buf; @@ -2829,10 +2796,8 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message) return OK; } -/* - * Crude way of changing the name of a buffer. Use with care! - * The name should be relative to the current directory. - */ +/// Crude way of changing the name of a buffer. Use with care! +/// The name should be relative to the current directory. void buf_set_name(int fnum, char_u *name) { buf_T *buf; @@ -2852,15 +2817,10 @@ void buf_set_name(int fnum, char_u *name) } } -/* - * Take care of what needs to be done when the name of buffer "buf" has - * changed. - */ +/// Take care of what needs to be done when the name of buffer "buf" has changed. void buf_name_changed(buf_T *buf) { - /* - * If the file name changed, also change the name of the swapfile - */ + // If the file name changed, also change the name of the swapfile if (buf->b_ml.ml_mfp != NULL) { ml_setname(buf); } @@ -2874,12 +2834,11 @@ void buf_name_changed(buf_T *buf) ml_timestamp(buf); // reset timestamp } -/* - * set alternate file name for current window - * - * Used by do_one_cmd(), do_write() and do_ecmd(). - * Return the buffer. - */ +/// Set alternate file name for current window +/// +/// Used by do_one_cmd(), do_write() and do_ecmd(). +/// +/// @return the buffer. buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) { buf_T *buf; @@ -2910,12 +2869,10 @@ char_u *getaltfname(bool errmsg) return fname; } -/* - * Add a file name to the buflist and return its number. - * Uses same flags as buflist_new(), except BLN_DUMMY. - * - * used by qf_init(), main() and doarglist() - */ +/// Add a file name to the buflist and return its number. +/// Uses same flags as buflist_new(), except BLN_DUMMY. +/// +/// Used by qf_init(), main() and doarglist() int buflist_add(char_u *fname, int flags) { buf_T *buf; @@ -2928,9 +2885,7 @@ int buflist_add(char_u *fname, int flags) } #if defined(BACKSLASH_IN_FILENAME) -/* - * Adjust slashes in file names. Called after 'shellslash' was set. - */ +/// Adjust slashes in file names. Called after 'shellslash' was set. void buflist_slash_adjust(void) { FOR_ALL_BUFFERS(bp) { @@ -2945,10 +2900,8 @@ void buflist_slash_adjust(void) #endif -/* - * Set alternate cursor position for the current buffer and window "win". - * Also save the local window option values. - */ +/// Set alternate cursor position for the current buffer and window "win". +/// Also save the local window option values. void buflist_altfpos(win_T *win) { buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, true); @@ -3011,8 +2964,8 @@ static bool otherfile_buf(buf_T *buf, char_u *ffname, FileID *file_id_p, bool fi return true; } -// Set file_id for a buffer. -// Must always be called when b_fname is changed! +/// Set file_id for a buffer. +/// Must always be called when b_fname is changed! void buf_set_file_id(buf_T *buf) { FileID file_id; @@ -3152,7 +3105,7 @@ static char_u *lasttitle = NULL; static char_u *lasticon = NULL; -// Put the title name in the title bar and icon of the window. +/// Put the title name in the title bar and icon of the window. void maketitle(void) { char_u *title_str = NULL; @@ -3350,8 +3303,8 @@ void maketitle(void) /// /// @param str desired title string /// @param[in,out] last current title string -// -/// @return true if resettitle() is to be called. +/// +/// @return true if resettitle() is to be called. static bool value_change(char_u *str, char_u **last) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -3408,18 +3361,18 @@ typedef enum { /// If maxwidth is not zero, the string will be filled at any middle marker /// or truncated if too long, fillchar is used for all whitespace. /// -/// @param wp The window to build a statusline for -/// @param out The output buffer to write the statusline to -/// Note: This should not be NameBuff -/// @param outlen The length of the output buffer -/// @param fmt The statusline format string -/// @param use_sandbox Use a sandboxed environment when evaluating fmt -/// @param fillchar Character to use when filling empty space in the statusline -/// @param maxwidth The maximum width to make the statusline -/// @param hltab HL attributes (can be NULL) -/// @param tabtab Tab clicks definition (can be NULL). +/// @param wp The window to build a statusline for +/// @param out The output buffer to write the statusline to +/// Note: This should not be NameBuff +/// @param outlen The length of the output buffer +/// @param fmt The statusline format string +/// @param use_sandbox Use a sandboxed environment when evaluating fmt +/// @param fillchar Character to use when filling empty space in the statusline +/// @param maxwidth The maximum width to make the statusline +/// @param hltab HL attributes (can be NULL) +/// @param tabtab Tab clicks definition (can be NULL). /// -/// @return The final width of the statusline +/// @return The final width of the statusline int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use_sandbox, int fillchar, int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab) { @@ -3681,8 +3634,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use } } // If the group is shorter than the minimum width, add padding characters. - } else if ( - abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) { + } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) { long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid; // If the group is left-aligned, add characters to the right. if (min_group_width < 0) { @@ -4508,7 +4460,7 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use char_u *start = stl_items[stl_separator_locations[i]].start; char_u *seploc = start + dislocation; STRMOVE(seploc, start); - for (char_u *s = start; s < seploc; ) { + for (char_u *s = start; s < seploc;) { MB_CHAR2BYTES(fillchar, s); } @@ -4582,12 +4534,10 @@ int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, int use } return width; -} +} // NOLINT(readability/fn_size) -/* - * Get relative cursor position in window into "buf[buflen]", in the form 99%, - * using "Top", "Bot" or "All" when appropriate. - */ +/// Get relative cursor position in window into "buf[buflen]", in the form 99%, +/// using "Top", "Bot" or "All" when appropriate. void get_rel_pos(win_T *wp, char_u *buf, int buflen) { // Need at least 3 chars for writing. @@ -4624,7 +4574,7 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) /// @param buflen length of the string buffer /// @param add_file if true, add "file" before the arg number /// -/// @return true if it was appended. +/// @return true if it was appended. static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) FUNC_ATTR_NONNULL_ALL { @@ -4653,12 +4603,12 @@ static bool append_arg_number(win_T *wp, char_u *buf, int buflen, bool add_file) return true; } -// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. -// "*ffname" becomes a pointer to allocated memory (or NULL). -// When resolving a link both "*sfname" and "*ffname" will point to the same -// allocated memory. -// The "*ffname" and "*sfname" pointer values on call will not be freed. -// Note that the resulting "*ffname" pointer should be considered not allocated. +/// Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL. +/// "*ffname" becomes a pointer to allocated memory (or NULL). +/// When resolving a link both "*sfname" and "*ffname" will point to the same +/// allocated memory. +/// The "*ffname" and "*sfname" pointer values on call will not be freed. +/// Note that the resulting "*ffname" pointer should be considered not allocated. void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) { if (*ffname == NULL) { // no file name given, nothing to do @@ -4682,9 +4632,7 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) #endif } -/* - * Get the file name for an argument list entry. - */ +/// Get the file name for an argument list entry. char_u *alist_name(aentry_T *aep) { buf_T *bp; @@ -4726,8 +4674,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) assert(firstwin != NULL); // satisfy coverity if (ARGCOUNT <= 0) { - /* Don't give an error message. We don't want it when the ":all" - * command is in the .vimrc. */ + // Don't give an error message. We don't want it when the ":all" command is in the .vimrc. return; } setpcmark(); @@ -4735,9 +4682,9 @@ void do_arg_all(int count, int forceit, int keep_tabs) opened_len = ARGCOUNT; opened = xcalloc((size_t)opened_len, 1); - /* Autocommands may do anything to the argument list. Make sure it's not - * freed while we are working here by "locking" it. We still have to - * watch out for its size to be changed. */ + // Autocommands may do anything to the argument list. Make sure it's not + // freed while we are working here by "locking" it. We still have to + // watch out for its size to be changed. alist = curwin->w_alist; alist->al_refcount++; @@ -4745,13 +4692,11 @@ void do_arg_all(int count, int forceit, int keep_tabs) old_curtab = curtab; - /* - * Try closing all windows that are not in the argument list. - * Also close windows that are not full width; - * When 'hidden' or "forceit" set the buffer becomes hidden. - * Windows that have a changed buffer and can't be hidden won't be closed. - * When the ":tab" modifier was used do this for all tab pages. - */ + // Try closing all windows that are not in the argument list. + // Also close windows that are not full width; + // When 'hidden' or "forceit" set the buffer becomes hidden. + // Windows that have a changed buffer and can't be hidden won't be closed. + // When the ":tab" modifier was used do this for all tab pages. if (had_tab > 0) { goto_tabpage_tp(first_tabpage, true, true); } @@ -4796,8 +4741,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) } if (wp->w_alist != alist) { - /* Use the current argument list for all windows - * containing a file from it. */ + // Use the current argument list for all windows containing a file from it. alist_unlink(wp->w_alist); wp->w_alist = alist; wp->w_alist->al_refcount++; @@ -4811,8 +4755,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) if (i == opened_len && !keep_tabs) { // close this window if (buf_hide(buf) || forceit || buf->b_nwindows > 1 || !bufIsChanged(buf)) { - /* If the buffer was changed, and we would like to hide it, - * try autowriting. */ + // If the buffer was changed, and we would like to hide it, try autowriting. if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { bufref_T bufref; set_bufref(&bufref, buf); @@ -4851,10 +4794,8 @@ void do_arg_all(int count, int forceit, int keep_tabs) goto_tabpage_tp(tpnext, true, true); } - /* - * Open a window for files in the argument list that don't have one. - * ARGCOUNT may change while doing this, because of autocommands. - */ + // Open a window for files in the argument list that don't have one. + // ARGCOUNT may change while doing this, because of autocommands. if (count > opened_len || count <= 0) { count = opened_len; } @@ -4909,9 +4850,7 @@ void do_arg_all(int count, int forceit, int keep_tabs) autocmd_no_leave--; } - /* - * edit file "i" - */ + // edit file "i" curwin->w_arg_idx = i; if (i == 0) { new_curwin = curwin; @@ -4963,15 +4902,13 @@ void do_arg_all(int count, int forceit, int keep_tabs) xfree(opened); } -/// @return true if "buf" is a prompt buffer. +/// @return true if "buf" is a prompt buffer. bool bt_prompt(buf_T *buf) { return buf != NULL && buf->b_p_bt[0] == 'p'; } -/* - * Open a window for a number of buffers. - */ +/// Open a window for a number of buffers. void ex_buffer_all(exarg_T *eap) { buf_T *buf; @@ -4999,10 +4936,8 @@ void ex_buffer_all(exarg_T *eap) setpcmark(); - /* - * Close superfluous windows (two windows for the same buffer). - * Also close windows that are not full-width. - */ + // Close superfluous windows (two windows for the same buffer). + // Also close windows that are not full-width. if (had_tab > 0) { goto_tabpage_tp(first_tabpage, true, true); } @@ -5036,7 +4971,6 @@ void ex_buffer_all(exarg_T *eap) goto_tabpage_tp(tpnext, true, true); } - // // Go through the buffer list. When a buffer doesn't have a window yet, // open one. Otherwise move the window to the right position. // Watch out for autocommands that delete buffers or windows! @@ -5132,9 +5066,7 @@ void ex_buffer_all(exarg_T *eap) win_enter(firstwin, false); // back to first window autocmd_no_leave--; - /* - * Close superfluous windows. - */ + // Close superfluous windows. for (wp = lastwin; open_wins > count;) { r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) || autowrite(wp->w_buffer, false) == OK); @@ -5155,15 +5087,13 @@ void ex_buffer_all(exarg_T *eap) } -/* - * do_modelines() - process mode lines for the current file - * - * "flags" can be: - * OPT_WINONLY only set options local to window - * OPT_NOWIN don't set options local to window - * - * Returns immediately if the "ml" option isn't set. - */ +/// do_modelines() - process mode lines for the current file +/// +/// @param flags +/// OPT_WINONLY only set options local to window +/// OPT_NOWIN don't set options local to window +/// +/// Returns immediately if the "ml" option isn't set. void do_modelines(int flags) { linenr_T lnum; @@ -5269,10 +5199,8 @@ static int chk_modeline(linenr_T lnum, int flags) break; } - /* - * Find end of set command: ':' or end of line. - * Skip over "\:", replacing it with ":". - */ + // Find end of set command: ':' or end of line. + // Skip over "\:", replacing it with ":". for (e = s; *e != ':' && *e != NUL; e++) { if (e[0] == '\\' && e[1] == ':') { STRMOVE(e, e + 1); @@ -5282,13 +5210,11 @@ static int chk_modeline(linenr_T lnum, int flags) end = true; } - /* - * If there is a "set" command, require a terminating ':' and - * ignore the stuff after the ':'. - * "vi:set opt opt opt: foo" -- foo not interpreted - * "vi:opt opt opt: foo" -- foo interpreted - * Accept "se" for compatibility with Elvis. - */ + // If there is a "set" command, require a terminating ':' and + // ignore the stuff after the ':'. + // "vi:set opt opt opt: foo" -- foo not interpreted + // "vi:opt opt opt: foo" -- foo interpreted + // Accept "se" for compatibility with Elvis. if (STRNCMP(s, "set ", (size_t)4) == 0 || STRNCMP(s, "se ", (size_t)3) == 0) { if (*e != ':') { // no terminating ':'? @@ -5327,36 +5253,36 @@ static int chk_modeline(linenr_T lnum, int flags) return retval; } -// Return true if "buf" is a help buffer. +/// @return true if "buf" is a help buffer. bool bt_help(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return buf != NULL && buf->b_help; } -// Return true if "buf" is a normal buffer, 'buftype' is empty. +/// @return true if "buf" is a normal buffer, 'buftype' is empty. bool bt_normal(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return buf != NULL && buf->b_p_bt[0] == NUL; } -// Return true if "buf" is the quickfix buffer. +/// @return true if "buf" is the quickfix buffer. bool bt_quickfix(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return buf != NULL && buf->b_p_bt[0] == 'q'; } -// Return true if "buf" is a terminal buffer. +/// @return true if "buf" is a terminal buffer. bool bt_terminal(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return buf != NULL && buf->b_p_bt[0] == 't'; } -// Return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt" -// buffer. This means the buffer name is not a file name. +/// @return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt" / +/// buffer. This means the buffer name is not a file name. bool bt_nofile(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5366,8 +5292,8 @@ bool bt_nofile(const buf_T *const buf) || buf->b_p_bt[0] == 'p'); } -// Return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt" -// buffer. +/// @return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt" +/// buffer. bool bt_dontwrite(const buf_T *const buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5386,8 +5312,8 @@ bool bt_dontwrite_msg(const buf_T *const buf) return false; } -// Return true if the buffer should be hidden, according to 'hidden', ":hide" -// and 'bufhidden'. +/// @return true if the buffer should be hidden, according to 'hidden', ":hide" +/// and 'bufhidden'. bool buf_hide(const buf_T *const buf) { // 'bufhidden' overrules 'hidden' and ":hide", check it first @@ -5402,10 +5328,8 @@ bool buf_hide(const buf_T *const buf) return p_hid || cmdmod.hide; } -/* - * Return special buffer name. - * Returns NULL when the buffer has a normal file name. - */ +/// @return special buffer name or +/// NULL when the buffer has a normal file name. char_u *buf_spname(buf_T *buf) { if (bt_quickfix(buf)) { @@ -5446,7 +5370,7 @@ char_u *buf_spname(buf_T *buf) /// @param[out] wp stores the found window /// @param[out] tp stores the found tabpage /// -/// @return true if a window was found for the buffer. +/// @return true if a window was found for the buffer. bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) { *wp = NULL; @@ -5608,7 +5532,7 @@ int buf_signcols(buf_T *buf, int maximum) return buf->b_signcols.size; } -// Get "buf->b_fname", use "[No Name]" if it is NULL. +/// Get "buf->b_fname", use "[No Name]" if it is NULL. char_u *buf_get_fname(const buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { @@ -5618,9 +5542,7 @@ char_u *buf_get_fname(const buf_T *buf) return buf->b_fname; } -/* - * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. - */ +/// Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. void set_buflisted(int on) { if (on != curbuf->b_p_bl) { @@ -5638,7 +5560,7 @@ void set_buflisted(int on) /// /// @param buf buffer to check /// -/// @return true if the buffer's contents have changed +/// @return true if the buffer's contents have changed bool buf_contents_changed(buf_T *buf) FUNC_ATTR_NONNULL_ALL { @@ -5719,4 +5641,3 @@ void buf_open_scratch(handle_T bufnr, char *bufname) set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } - -- cgit From c3a6ff6aa3be259255976b1178dd07ac876ff124 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 11 Mar 2022 15:04:48 +0100 Subject: fix(coverity): dead code and operands don't affect result #17662 * fix(coverity/349942): structurally dead code * fix(coverity/331396): operands don't affect result * fix(coverity/331393): operands don't affect result * fix(coverity/331392): operands don't affect result * fix(coverity/331384): operands don't affect result * fix(coverity/331374): operands don't affect result * fix(coverity/331372): operands don't affect result * fix(coverity/331371): operands don't affect result * fix(coverity/331364): operands don't affect result * fix(coverity/105585): operands don't affect result --- src/nvim/autocmd.c | 3 +-- src/nvim/mark.c | 2 +- src/nvim/normal.c | 1 - src/nvim/option.c | 6 ++---- src/nvim/popupmnu.c | 7 ++----- src/nvim/tui/tui.c | 4 ++-- 6 files changed, 8 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a850e5c1a0..dfcdfd8203 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2428,9 +2428,8 @@ char *aucmd_exec_default_desc(AucmdExecutable acc) default: return NULL; } - - abort(); } + char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc) { switch (acc.type) { diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 8b29aa3676..b770fef05c 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -630,7 +630,7 @@ static char_u *mark_line(pos_T *mp, int lead_len) if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) { return vim_strsave((char_u *)"-invalid-"); } - assert(Columns >= 0 && (size_t)Columns <= SIZE_MAX); + assert(Columns >= 0); // Allow for up to 5 bytes per character. s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (size_t)Columns * 5); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 5c39e9c8bf..0e5e0ab403 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3568,7 +3568,6 @@ static void nv_zet(cmdarg_T *cap) bool undo = false; int l_p_siso = (int)get_sidescrolloff_value(curwin); - assert(l_p_siso <= INT_MAX); if (ascii_isdigit(nchar)) { /* diff --git a/src/nvim/option.c b/src/nvim/option.c index a0706dfa14..191b635dc0 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5664,12 +5664,10 @@ void comp_col(void) } } assert(sc_col >= 0 - && INT_MIN + sc_col <= Columns - && Columns - sc_col <= INT_MAX); + && INT_MIN + sc_col <= Columns); sc_col = Columns - sc_col; assert(ru_col >= 0 - && INT_MIN + ru_col <= Columns - && Columns - ru_col <= INT_MAX); + && INT_MIN + ru_col <= Columns); ru_col = Columns - ru_col; if (sc_col <= 0) { // screen too narrow, will become a mess sc_col = 1; diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index d7726409b5..e354a589a5 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -289,8 +289,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_rl) { pum_width = pum_col - pum_scrollbar + 1; } else { - assert(Columns - pum_col - pum_scrollbar >= INT_MIN - && Columns - pum_col - pum_scrollbar <= INT_MAX); + assert(Columns - pum_col - pum_scrollbar >= 0); pum_width = Columns - pum_col - pum_scrollbar; } @@ -356,7 +355,6 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i } else { pum_col = 0; } - assert(Columns - 1 >= INT_MIN); pum_width = Columns - 1; } else { if (max_width > p_pw) { @@ -367,8 +365,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_rl) { pum_col = max_width - 1; } else { - assert(Columns - max_width >= INT_MIN - && Columns - max_width <= INT_MAX); + assert(Columns - max_width >= 0); pum_col = Columns - max_width; } pum_width = max_width - pum_scrollbar; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 1ad82b7290..79a6b9ed0e 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1475,8 +1475,8 @@ static void tui_guess_size(UI *ui) // 1 - look for non-default 'columns' and 'lines' options during startup if (data->is_starting && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) { did_user_set_dimensions = true; - assert(Columns >= INT_MIN && Columns <= INT_MAX); - assert(Rows >= INT_MIN && Rows <= INT_MAX); + assert(Columns >= 0); + assert(Rows >= 0); width = Columns; height = Rows; goto end; -- cgit From d0b5c1f696016a90a5b386339f4425a7bc69f252 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 11 Mar 2022 22:59:26 +0800 Subject: vim-patch:8.2.4440: crash with specific regexp pattern and string Problem: Crash with specific regexp pattern and string. Solution: Stop at the start of the string. https://github.com/vim/vim/commit/6456fae9ba8e72c74b2c0c499eaf09974604ff30 --- src/nvim/regexp_bt.c | 4 ++++ src/nvim/testdir/test_regexp_utf8.vim | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index f6804e8415..1153b8ed33 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -4083,6 +4083,10 @@ static bool regmatch( break; if (rex.input == rex.line) { // backup to last char of previous line + if (rex.lnum == 0) { + status = RA_NOMATCH; + break; + } rex.lnum--; rex.line = reg_getline(rex.lnum); // Just in case regrepeat() didn't count right. diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index b640a6d043..70741209fe 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -540,7 +540,6 @@ endfunc " Check that [[:upper:]] matches for automatic engine func Test_match_char_class_upper() new - let _engine=®expengine " Test 1: [[:upper:]]\{2,\} set regexpengine=0 @@ -581,7 +580,7 @@ func Test_match_char_class_upper() call assert_equal(4, searchcount().total, 'TEST 3 lower') " clean up - let ®expengine=_engine + set regexpengine=0 bwipe! endfunc @@ -593,4 +592,13 @@ func Test_match_invalid_byte() call delete('Xinvalid') endfunc +func Test_match_too_complicated() + set regexpengine=1 + exe "vsplit \xeb\xdb\x99" + silent! buf \&\zs*\zs*0 + bwipe! + set regexpengine=0 +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From a6e72aa295c633e67b3533a5a2088b7916941ad0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 11 Mar 2022 23:01:20 +0800 Subject: vim-patch:8.2.4443: regexp pattern test fails on Mac Problem: Regexp pattern test fails on Mac. Solution: Do not use a swapfile for the buffer. https://github.com/vim/vim/commit/2457b2bbc28cce6e8c1106d427b8e867d4f58cfa --- src/nvim/testdir/test_regexp_utf8.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 70741209fe..eab47dbccc 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -594,7 +594,7 @@ endfunc func Test_match_too_complicated() set regexpengine=1 - exe "vsplit \xeb\xdb\x99" + exe "noswapfile vsplit \xeb\xdb\x99" silent! buf \&\zs*\zs*0 bwipe! set regexpengine=0 -- cgit From 5862176764c7a86d5fdd2685122810e14a3d5b02 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 16 Feb 2022 17:11:50 -0500 Subject: feat(remote): add basic --remote support This is starting from @geekodour's work at https://github.com/neovim/neovim/pull/8326 --- src/nvim/api/vim.c | 2 +- src/nvim/main.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/main.h | 2 ++ 3 files changed, 82 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index ebf4f65c91..b82a7553cb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,7 +1997,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); - String s = cstr_to_string("return vim._os_proc_children(select(1, ...))"); + String s = cstr_to_string("return vim._os_proc_children(select(...))"); Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); diff --git a/src/nvim/main.c b/src/nvim/main.c index a575aba50a..3a1ca2988a 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -268,6 +268,10 @@ int main(int argc, char **argv) } server_init(params.listen_addr); + if (params.remote) { + handle_remote_client(¶ms, params.remote, + params.server_addr, argc, argv); + } if (GARGCOUNT > 0) { fname = get_fname(¶ms, cwd); @@ -803,6 +807,67 @@ static void init_locale(void) } #endif +/// Handle remote subcommands +static void handle_remote_client(mparm_T *params, int remote_args, + char *server_addr, int argc, char **argv) +{ + Object rvobj = OBJECT_INIT; + rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; + rvobj.type = kObjectTypeDictionary; + CallbackReader on_data = CALLBACK_READER_INIT; + const char *error = NULL; + uint64_t rc_id = server_addr == NULL ? 0 : channel_connect(false, + server_addr, true, on_data, 50, &error); + + Boolean should_exit = true; + Boolean tabbed; + int files; + + int t_argc = remote_args; + Array args = ARRAY_DICT_INIT; + String arg_s; + for (; t_argc < argc; t_argc++) { + arg_s = cstr_to_string(argv[t_argc]); + ADD(args, STRING_OBJ(arg_s)); + } + + Error err = ERROR_INIT; + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ((int)rc_id)); + ADD(a, ARRAY_OBJ(args)); + String s = cstr_to_string("return vim._cs_remote(...)"); + Object o = executor_exec_lua_api(s, a, &err); + api_free_string(s); + api_free_array(a); + + if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else if (!ERROR_SET(&err)) { + api_set_error(&err, kErrorTypeException, + "Function returned unexpected value"); + } + + for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { + if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + // should we check items[i].value.type here? + tabbed = rvobj.data.dictionary.items[i].value.data.boolean; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + should_exit = rvobj.data.dictionary.items[i].value.data.boolean; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "files") == 0) { + files = (int)rvobj.data.dictionary.items[i].value.data.integer; + } + } + + if (should_exit) { + mch_exit(0); + } else { + if (tabbed) { + params->window_count = files; + params->window_layout = WIN_TABS; + } + } +} + /// Decides whether text (as opposed to commands) will be read from stdin. /// @see EDIT_STDIN static bool edit_stdin(bool explicit, mparm_T *parmp) @@ -868,6 +933,8 @@ static void command_line_scan(mparm_T *parmp) // "--version" give version message // "--noplugin[s]" skip plugins // "--cmd " execute cmd before vimrc + // "--remote" execute commands remotey on a server + // "--server" name of vim server to send remote commands to if (STRICMP(argv[0] + argv_idx, "help") == 0) { usage(); os_exit(0); @@ -906,6 +973,11 @@ static void command_line_scan(mparm_T *parmp) argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { // Do nothing: file args are always literal. #7679 + } else if (STRNICMP(argv[0] + argv_idx, "remote", 6) == 0) { + parmp->remote = parmp->argc - argc; + } else if (STRNICMP(argv[0] + argv_idx, "server", 6) == 0) { + want_argument = true; + argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) { p_lpl = false; } else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) { @@ -1137,6 +1209,9 @@ static void command_line_scan(mparm_T *parmp) } else if (strequal(argv[-1], "--listen")) { // "--listen {address}" parmp->listen_addr = argv[0]; + } else if (strequal(argv[-1], "--server")) { + // "--server {address}" + parmp->server_addr = argv[0]; } // "--startuptime " already handled break; @@ -1291,6 +1366,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv) paramp->use_debug_break_level = -1; paramp->window_count = -1; paramp->listen_addr = NULL; + paramp->server_addr = NULL; + paramp->remote = 0; } /// Initialize global startuptime file if "--startuptime" passed as an argument. @@ -2041,6 +2118,8 @@ static void usage(void) mch_msg(_(" --headless Don't start a user interface\n")); mch_msg(_(" --listen
Serve RPC API from this address\n")); mch_msg(_(" --noplugin Don't load plugins\n")); + mch_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); + mch_msg(_(" --server
Specify RPC server to send commands to\n")); mch_msg(_(" --startuptime Write startup timing messages to \n")); mch_msg(_("\nSee \":help startup-options\" for all options.\n")); } diff --git a/src/nvim/main.h b/src/nvim/main.h index f73af5c288..e55bef6e33 100644 --- a/src/nvim/main.h +++ b/src/nvim/main.h @@ -39,6 +39,8 @@ typedef struct { int diff_mode; // start with 'diff' set char *listen_addr; // --listen {address} + int remote; // --remote-[subcmd] {file1} {file2} + char *server_addr; // --server {address} } mparm_T; #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 70d2ab158320e72542dc2a845858d6f97da86e00 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Wed, 16 Feb 2022 17:19:41 -0500 Subject: fix(remote): make compile again --- src/nvim/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index 3a1ca2988a..843ca5f855 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -836,7 +836,7 @@ static void handle_remote_client(mparm_T *params, int remote_args, ADD(a, INTEGER_OBJ((int)rc_id)); ADD(a, ARRAY_OBJ(args)); String s = cstr_to_string("return vim._cs_remote(...)"); - Object o = executor_exec_lua_api(s, a, &err); + Object o = nlua_exec(s, a, &err); api_free_string(s); api_free_array(a); @@ -859,7 +859,7 @@ static void handle_remote_client(mparm_T *params, int remote_args, } if (should_exit) { - mch_exit(0); + os_exit(0); } else { if (tabbed) { params->window_count = files; -- cgit From 039e94f491d2f8576cbef1aeacd5ea1f7bc0982a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 24 Feb 2022 10:47:41 -0500 Subject: test(remote): add tests for --remote This also fixes a fair number of issues found in running the tests --- src/nvim/api/vim.c | 2 +- src/nvim/main.c | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b82a7553cb..b691dee2ef 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,7 +1997,7 @@ Array nvim_get_proc_children(Integer pid, Error *err) DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); - String s = cstr_to_string("return vim._os_proc_children(select(...))"); + String s = cstr_to_string("return vim._os_proc_children(...)"); Object o = nlua_exec(s, a, err); api_free_string(s); api_free_array(a); diff --git a/src/nvim/main.c b/src/nvim/main.c index 843ca5f855..a3588ac5df 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -821,7 +821,6 @@ static void handle_remote_client(mparm_T *params, int remote_args, Boolean should_exit = true; Boolean tabbed; - int files; int t_argc = remote_args; Array args = ARRAY_DICT_INIT; @@ -839,12 +838,17 @@ static void handle_remote_client(mparm_T *params, int remote_args, Object o = nlua_exec(s, a, &err); api_free_string(s); api_free_array(a); + if (ERROR_SET(&err)) { + mch_errmsg(err.msg); + mch_errmsg("\n"); + os_exit(2); + } if (o.type == kObjectTypeDictionary) { rvobj.data.dictionary = o.data.dictionary; - } else if (!ERROR_SET(&err)) { - api_set_error(&err, kErrorTypeException, - "Function returned unexpected value"); + } else { + mch_errmsg("vim._cs_remote returned unexpected value\n"); + os_exit(3); } for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { @@ -853,18 +857,15 @@ static void handle_remote_client(mparm_T *params, int remote_args, tabbed = rvobj.data.dictionary.items[i].value.data.boolean; } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { should_exit = rvobj.data.dictionary.items[i].value.data.boolean; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "files") == 0) { - files = (int)rvobj.data.dictionary.items[i].value.data.integer; } } if (should_exit) { os_exit(0); - } else { - if (tabbed) { - params->window_count = files; - params->window_layout = WIN_TABS; - } + } + if (tabbed) { + params->window_count = argc - remote_args - 1; + params->window_layout = WIN_TABS; } } -- cgit From 29c36322857b37263b07eb1301d71ccd8a2ae044 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 3 Mar 2022 16:33:27 -0500 Subject: fix(remote): report on missing wait commands, typecheck lua results Clean up lint errors, too --- src/nvim/main.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index a3588ac5df..eb60d51b9b 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -816,8 +816,10 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.type = kObjectTypeDictionary; CallbackReader on_data = CALLBACK_READER_INIT; const char *error = NULL; - uint64_t rc_id = server_addr == NULL ? 0 : channel_connect(false, - server_addr, true, on_data, 50, &error); + uint64_t rc_id = 0; + if (server_addr != NULL) { + rc_id = channel_connect(false, server_addr, true, on_data, 50, &error); + } Boolean should_exit = true; Boolean tabbed; @@ -848,17 +850,33 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.data.dictionary = o.data.dictionary; } else { mch_errmsg("vim._cs_remote returned unexpected value\n"); - os_exit(3); + os_exit(2); } for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { - // should we check items[i].value.type here? + if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); + os_exit(2); + } + mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); + mch_errmsg("\n"); + os_exit(2); + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); + os_exit(2); + } tabbed = rvobj.data.dictionary.items[i].value.data.boolean; } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); + os_exit(2); + } should_exit = rvobj.data.dictionary.items[i].value.data.boolean; } } + api_free_object(o); if (should_exit) { os_exit(0); -- cgit From 2be938a2513b1dd604ce0069667b89a64441cb2a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Thu, 10 Mar 2022 17:26:20 -0500 Subject: fix(remote): report connection error, missing return values --- src/nvim/main.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index eb60d51b9b..f08ede7197 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -820,9 +820,13 @@ static void handle_remote_client(mparm_T *params, int remote_args, if (server_addr != NULL) { rc_id = channel_connect(false, server_addr, true, on_data, 50, &error); } - - Boolean should_exit = true; - Boolean tabbed; + if (error) { + mch_msg("Failed to connect to server "); + mch_msg(server_addr); + mch_msg("\nReason: "); + mch_msg(error); + mch_msg("Continuing with remote command in case we can execute locally\n"); + } int t_argc = remote_args; Array args = ARRAY_DICT_INIT; @@ -853,6 +857,9 @@ static void handle_remote_client(mparm_T *params, int remote_args, os_exit(2); } + TriState should_exit = kNone; + TriState tabbed = kNone; + for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { @@ -867,21 +874,25 @@ static void handle_remote_client(mparm_T *params, int remote_args, mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } - tabbed = rvobj.data.dictionary.items[i].value.data.boolean; + tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); os_exit(2); } - should_exit = rvobj.data.dictionary.items[i].value.data.boolean; + should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } } + if (should_exit == kNone || tabbed == kNone) { + mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); + os_exit(2); + } api_free_object(o); - if (should_exit) { + if (should_exit == kTrue) { os_exit(0); } - if (tabbed) { + if (tabbed == kTrue) { params->window_count = argc - remote_args - 1; params->window_layout = WIN_TABS; } -- cgit From 1dbf8675c71dc500ae7502085161cd56e311ccf6 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 11 Mar 2022 11:16:34 -0500 Subject: fix(remote): respect silent in error reporting --- src/nvim/main.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index f08ede7197..33b13f4dc6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -815,17 +815,10 @@ static void handle_remote_client(mparm_T *params, int remote_args, rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; rvobj.type = kObjectTypeDictionary; CallbackReader on_data = CALLBACK_READER_INIT; - const char *error = NULL; + const char *connect_error = NULL; uint64_t rc_id = 0; if (server_addr != NULL) { - rc_id = channel_connect(false, server_addr, true, on_data, 50, &error); - } - if (error) { - mch_msg("Failed to connect to server "); - mch_msg(server_addr); - mch_msg("\nReason: "); - mch_msg(error); - mch_msg("Continuing with remote command in case we can execute locally\n"); + rc_id = channel_connect(false, server_addr, true, on_data, 50, &connect_error); } int t_argc = remote_args; @@ -839,6 +832,8 @@ static void handle_remote_client(mparm_T *params, int remote_args, Error err = ERROR_INIT; Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ((int)rc_id)); + ADD(a, CSTR_TO_OBJ(server_addr)); + ADD(a, CSTR_TO_OBJ(connect_error)); ADD(a, ARRAY_OBJ(args)); String s = cstr_to_string("return vim._cs_remote(...)"); Object o = nlua_exec(s, a, &err); -- cgit From 1201145b6893703fb351f51d9f2c742bd6946403 Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 11 Mar 2022 13:05:40 -0500 Subject: fix(remote): use STATIC_CSTR_AS_STRING --- src/nvim/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index 33b13f4dc6..230be9d9b9 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -835,9 +835,8 @@ static void handle_remote_client(mparm_T *params, int remote_args, ADD(a, CSTR_TO_OBJ(server_addr)); ADD(a, CSTR_TO_OBJ(connect_error)); ADD(a, ARRAY_OBJ(args)); - String s = cstr_to_string("return vim._cs_remote(...)"); + String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); Object o = nlua_exec(s, a, &err); - api_free_string(s); api_free_array(a); if (ERROR_SET(&err)) { mch_errmsg(err.msg); -- cgit From ab456bc304965d83585cd248284cb36c96927457 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 12 Mar 2022 08:25:28 +0000 Subject: vim-patch:8.2.3779: using freed memory when defining a user command recursively (#17688) Problem: Using freed memory when defining a user command from a user command. Solution: Do not use the command pointer after executing the command. (closes vim/vim#9318) https://github.com/vim/vim/commit/205f29c3e9b895dbaa4f738046da455a93c3812a --- src/nvim/ex_docmd.c | 10 ++++++++-- src/nvim/testdir/test_usercommands.vim | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a140d76858..63dc1e539e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6230,7 +6230,6 @@ static void do_ucmd(exarg_T *eap) size_t split_len = 0; char_u *split_buf = NULL; ucmd_T *cmd; - const sctx_T save_current_sctx = current_sctx; if (eap->cmdidx == CMD_USER) { cmd = USER_CMD(eap->useridx); @@ -6320,12 +6319,19 @@ static void do_ucmd(exarg_T *eap) buf = xmalloc(totlen + 1); } + sctx_T save_current_sctx; + bool restore_current_sctx = false; if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + restore_current_sctx = true; + save_current_sctx = current_sctx; current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; } (void)do_cmdline(buf, eap->getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); - if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + + // Careful: Do not use "cmd" here, it may have become invalid if a user + // command was added. + if (restore_current_sctx) { current_sctx = save_current_sctx; } xfree(buf); diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 967ad85a64..e96e3d94e5 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -572,4 +572,24 @@ func Test_delcommand_buffer() call assert_equal(0, exists(':Global')) endfunc +func DefCmd(name) + if len(a:name) > 30 + return + endif + exe 'command ' .. a:name .. ' call DefCmd("' .. a:name .. 'x")' + echo a:name + exe a:name +endfunc + +func Test_recursive_define() + call DefCmd('Command') + + let name = 'Command' + while len(name) < 30 + exe 'delcommand ' .. name + let name ..= 'x' + endwhile +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 5051510ade5f171c1239906c8638e804356186fe Mon Sep 17 00:00:00 2001 From: erw7 Date: Fri, 12 Nov 2021 00:07:03 +0900 Subject: fix(channel): fix channel consistency - Fix the problem that chanclose() does not work for channel created by nvim_open_term(). - Fix the problem that the loopback channel is not released. - Fix the error message when sending raw data to the loopback channel. --- src/nvim/api/vim.c | 1 + src/nvim/channel.c | 16 +++++++++++++--- src/nvim/channel.h | 1 + src/nvim/terminal.c | 8 ++++++-- 4 files changed, 21 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b691dee2ef..c7ccc6bfeb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1140,6 +1140,7 @@ Integer nvim_open_term(Buffer buffer, DictionaryOf(LuaRef) opts, Error *err) TerminalOptions topts; Channel *chan = channel_alloc(kChannelStreamInternal); chan->stream.internal.cb = cb; + chan->stream.internal.closed = false; topts.data = chan; // NB: overridden in terminal_check_size if a window is already // displaying the buffer diff --git a/src/nvim/channel.c b/src/nvim/channel.c index d79c0acc4a..f87b3a2f8f 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -138,8 +138,14 @@ bool channel_close(uint64_t id, ChannelPart part, const char **error) *error = (const char *)e_invstream; return false; } - api_free_luaref(chan->stream.internal.cb); - chan->stream.internal.cb = LUA_NOREF; + if (chan->term) { + api_free_luaref(chan->stream.internal.cb); + chan->stream.internal.cb = LUA_NOREF; + chan->stream.internal.closed = true; + terminal_close(chan->term, 0); + } else { + channel_decref(chan); + } break; default: @@ -536,7 +542,11 @@ size_t channel_send(uint64_t id, char *data, size_t len, bool data_owned, const } if (chan->streamtype == kChannelStreamInternal) { - if (!chan->term) { + if (chan->is_rpc) { + *error = _("Can't send raw data to rpc channel"); + goto retfree; + } + if (!chan->term || chan->stream.internal.closed) { *error = _("Can't send data to closed stream"); goto retfree; } diff --git a/src/nvim/channel.h b/src/nvim/channel.h index 50d6b3600a..5cec5731eb 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -44,6 +44,7 @@ typedef struct { typedef struct { LuaRef cb; + bool closed; } InternalState; typedef struct { diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 1c26e46a21..a76a806b80 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -317,10 +317,14 @@ void terminal_close(Terminal *term, int status) term->opts.close_cb(term->opts.data); } } else if (!only_destroy) { - // This was called by channel_process_exit_cb() not in process_teardown(). + // Associated channel has been closed and the editor is not exiting. // Do not call the close callback now. Wait for the user to press a key. char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status); + if (((Channel *)term->opts.data)->streamtype == kChannelStreamInternal) { + snprintf(msg, sizeof msg, "\r\n[Terminal closed]"); + } else { + snprintf(msg, sizeof msg, "\r\n[Process exited %d]", status); + } terminal_receive(term, msg, strlen(msg)); } -- cgit From 3a12737e6c13e9be774483f34655e7ac96e36c09 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Sat, 2 Nov 2019 15:06:32 +0100 Subject: refactor(main): separate connection code from --remote execution code --- src/nvim/api/vim.c | 3 +- src/nvim/main.c | 162 +++++++++++++++++++++++++++++------------------------ 2 files changed, 89 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b691dee2ef..a942c94f46 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1997,9 +1997,8 @@ Array nvim_get_proc_children(Integer pid, Error *err) DLOG("fallback to vim._os_proc_children()"); Array a = ARRAY_DICT_INIT; ADD(a, INTEGER_OBJ(pid)); - String s = cstr_to_string("return vim._os_proc_children(...)"); + String s = STATIC_CSTR_AS_STRING("return vim._os_proc_children(...)"); Object o = nlua_exec(s, a, err); - api_free_string(s); api_free_array(a); if (o.type == kObjectTypeArray) { rvobj = o.data.array; diff --git a/src/nvim/main.c b/src/nvim/main.c index 230be9d9b9..f762160c05 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -269,7 +269,7 @@ int main(int argc, char **argv) server_init(params.listen_addr); if (params.remote) { - handle_remote_client(¶ms, params.remote, + remote_request(¶ms, params.remote, params.server_addr, argc, argv); } @@ -807,91 +807,105 @@ static void init_locale(void) } #endif + +static uint64_t server_connect(char *server_addr, const char **errmsg) +{ + if (server_addr == NULL) { + *errmsg = "no address specified"; + return 0; + } + CallbackReader on_data = CALLBACK_READER_INIT; + const char *error = NULL; + bool is_tcp = strrchr(server_addr, ':') ? true : false; + // connected to channel + uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 50, &error); + if (error) { + *errmsg = error; + return 0; + } + return chan; +} + /// Handle remote subcommands -static void handle_remote_client(mparm_T *params, int remote_args, - char *server_addr, int argc, char **argv) +static void remote_request(mparm_T *params, int remote_args, + char *server_addr, int argc, char **argv) { - Object rvobj = OBJECT_INIT; - rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; - rvobj.type = kObjectTypeDictionary; - CallbackReader on_data = CALLBACK_READER_INIT; - const char *connect_error = NULL; - uint64_t rc_id = 0; - if (server_addr != NULL) { - rc_id = channel_connect(false, server_addr, true, on_data, 50, &connect_error); - } + const char *connect_error = NULL; + uint64_t chan = server_connect(server_addr, &connect_error); + Object rvobj = OBJECT_INIT; + + int t_argc = remote_args; + Array args = ARRAY_DICT_INIT; + String arg_s; + for (; t_argc < argc; t_argc++) { + arg_s = cstr_to_string(argv[t_argc]); + ADD(args, STRING_OBJ(arg_s)); + } + + Error err = ERROR_INIT; + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ((int)chan)); + ADD(a, CSTR_TO_OBJ(server_addr)); + ADD(a, CSTR_TO_OBJ(connect_error)); + ADD(a, ARRAY_OBJ(args)); + String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); + Object o = nlua_exec(s, a, &err); + api_free_array(a); + if (ERROR_SET(&err)) { + mch_errmsg(err.msg); + mch_errmsg("\n"); + os_exit(2); + } + + if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else { + mch_errmsg("vim._cs_remote returned unexpected value\n"); + os_exit(2); + } - int t_argc = remote_args; - Array args = ARRAY_DICT_INIT; - String arg_s; - for (; t_argc < argc; t_argc++) { - arg_s = cstr_to_string(argv[t_argc]); - ADD(args, STRING_OBJ(arg_s)); - } + TriState should_exit = kNone; + TriState tabbed = kNone; - Error err = ERROR_INIT; - Array a = ARRAY_DICT_INIT; - ADD(a, INTEGER_OBJ((int)rc_id)); - ADD(a, CSTR_TO_OBJ(server_addr)); - ADD(a, CSTR_TO_OBJ(connect_error)); - ADD(a, ARRAY_OBJ(args)); - String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)"); - Object o = nlua_exec(s, a, &err); - api_free_array(a); - if (ERROR_SET(&err)) { - mch_errmsg(err.msg); + for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { + if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); + os_exit(2); + } + mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); mch_errmsg("\n"); os_exit(2); - } - - if (o.type == kObjectTypeDictionary) { - rvobj.data.dictionary = o.data.dictionary; - } else { - mch_errmsg("vim._cs_remote returned unexpected value\n"); - os_exit(2); - } - - TriState should_exit = kNone; - TriState tabbed = kNone; - - for (size_t i = 0; i < rvobj.data.dictionary.size ; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); - os_exit(2); - } - mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); - mch_errmsg("\n"); + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); - os_exit(2); - } - tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { - if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); - os_exit(2); - } - should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } + tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; + } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { + mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); + os_exit(2); + } + should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } - if (should_exit == kNone || tabbed == kNone) { - mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); - os_exit(2); - } - api_free_object(o); + } + if (should_exit == kNone || tabbed == kNone) { + mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); + os_exit(2); + } + api_free_object(o); - if (should_exit == kTrue) { - os_exit(0); - } - if (tabbed == kTrue) { - params->window_count = argc - remote_args - 1; - params->window_layout = WIN_TABS; - } + if (should_exit == kTrue) { + os_exit(0); + } + if (tabbed == kTrue) { + params->window_count = argc - remote_args - 1; + params->window_layout = WIN_TABS; + } } + /// Decides whether text (as opposed to commands) will be read from stdin. /// @see EDIT_STDIN static bool edit_stdin(bool explicit, mparm_T *parmp) -- cgit From 8ba8f1a01808c881a32dd8936bb8fb26c9fbd4e8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 12 Mar 2022 16:25:21 +0000 Subject: fix(signcol): always trigger a redraw Whenever we change the size of the buffer signcol value, always trigger a redraw. Fixes: #17693 --- src/nvim/buffer.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2e2459aecf..402bd2c6de 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5485,6 +5485,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) buf->b_signcols.max++; } buf->b_signcols.size++; + redraw_buf_later(buf, NOT_VALID); return; } @@ -5505,6 +5506,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added) buf->b_signcols.size = linesum; buf->b_signcols.max = linesum; buf->b_signcols.sentinel = added->se_lnum; + redraw_buf_later(buf, NOT_VALID); } } -- cgit From a4400bf8cda8ace4c4aab67bc73a1820478f46f1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 12 Mar 2022 13:47:50 +0100 Subject: feat(ui): connect to remote ui (only debug messages for now) co-authored-by: hlpr98 --- src/nvim/api/private/dispatch.c | 8 +++++ src/nvim/globals.h | 3 ++ src/nvim/main.c | 18 ++++++++--- src/nvim/msgpack_rpc/channel.c | 5 +++ src/nvim/ui_client.c | 70 +++++++++++++++++++++++++++++++++++++++++ src/nvim/ui_client.h | 9 ++++++ 6 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/nvim/ui_client.c create mode 100644 src/nvim/ui_client.h (limited to 'src') diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index f670f06357..ba2e560d63 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -30,6 +30,7 @@ #include "nvim/api/vimscript.h" #include "nvim/api/win_config.h" #include "nvim/api/window.h" +#include "nvim/ui_client.h" static Map(String, MsgpackRpcRequestHandler) methods = MAP_INIT; @@ -38,6 +39,13 @@ static void msgpack_rpc_add_method_handler(String method, MsgpackRpcRequestHandl map_put(String, MsgpackRpcRequestHandler)(&methods, method, handler); } +void msgpack_rpc_add_redraw(void) +{ + msgpack_rpc_add_method_handler(STATIC_CSTR_AS_STRING("redraw"), + (MsgpackRpcRequestHandler) { .fn = ui_client_handle_redraw, + .fast = true }); +} + /// @param name API method name /// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len, diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 35ad57906b..b64ed7c758 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -342,6 +342,9 @@ EXTERN sctx_T current_sctx INIT(= { 0 COMMA 0 COMMA 0 }); // ID of the current channel making a client API call EXTERN uint64_t current_channel_id INIT(= 0); +// ID of the client channel. Used by ui client +EXTERN uint64_t ui_client_channel_id INIT(= 0); + EXTERN bool did_source_packages INIT(= false); // Scope information for the code that indirectly triggered the current diff --git a/src/nvim/main.c b/src/nvim/main.c index f762160c05..95ef306745 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -29,6 +29,7 @@ #include "nvim/if_cscope.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/ui_client.h" #include "nvim/vim.h" #ifdef HAVE_LOCALE_H # include @@ -269,8 +270,7 @@ int main(int argc, char **argv) server_init(params.listen_addr); if (params.remote) { - remote_request(¶ms, params.remote, - params.server_addr, argc, argv); + remote_request(¶ms, params.remote, params.server_addr, argc, argv); } if (GARGCOUNT > 0) { @@ -834,10 +834,20 @@ static void remote_request(mparm_T *params, int remote_args, uint64_t chan = server_connect(server_addr, &connect_error); Object rvobj = OBJECT_INIT; - int t_argc = remote_args; + if (strequal(argv[remote_args], "--remote-ui-test")) { + if (!chan) { + emsg(connect_error); + exit(1); + } + + ui_client_init(chan); + ui_client_execute(chan); + abort(); // unreachable + } + Array args = ARRAY_DICT_INIT; String arg_s; - for (; t_argc < argc; t_argc++) { + for (int t_argc = remote_args; t_argc < argc; t_argc++) { arg_s = cstr_to_string(argv[t_argc]); ADD(args, STRING_OBJ(arg_s)); } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 299651ee97..f4e836fa81 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -547,6 +547,11 @@ void rpc_close(Channel *channel) channel->rpc.closed = true; channel_decref(channel); + if (channel->id == ui_client_channel_id) { + // TODO(bfredl): handle this in ui_client, where os_exit() is safe + exit(0); + } + if (channel->streamtype == kChannelStreamStdio) { multiqueue_put(main_loop.fast_events, exit_event, 0); } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c new file mode 100644 index 0000000000..4a435aac4d --- /dev/null +++ b/src/nvim/ui_client.c @@ -0,0 +1,70 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include +#include +#include + +#include "nvim/vim.h" +#include "nvim/ui_client.h" +#include "nvim/api/private/helpers.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/api/private/dispatch.h" +#include "nvim/ui.h" + +void ui_client_init(uint64_t chan) +{ + Array args = ARRAY_DICT_INIT; + int width = 80; + int height = 25; + Dictionary opts = ARRAY_DICT_INIT; + + PUT(opts, "rgb", BOOLEAN_OBJ(true)); + PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); + PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); + + // TODO(bfredl): use the size of the client UI + ADD(args, INTEGER_OBJ((int)width)); + ADD(args, INTEGER_OBJ((int)height)); + ADD(args, DICTIONARY_OBJ(opts)); + + rpc_send_event(chan, "nvim_ui_attach", args); + msgpack_rpc_add_redraw(); // GAME! + ui_client_channel_id = chan; +} + +/// Handler for "redraw" events sent by the NVIM server +/// +/// This is just a stub. The mentioned functionality will be implemented. +/// +/// This function will be called by handle_request (in msgpack_rpc/channle.c) +/// The individual ui_events sent by the server are individually handled +/// by their respective handlers defined in ui_events_redraw.generated.h +/// +/// @note The "flush" event is called only once and only after handling all +/// the other events +/// @param channel_id: The id of the rpc channel +/// @param uidata: The dense array containing the ui_events sent by the server +/// @param[out] err Error details, if any +Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) +{ + for (size_t i = 0; i < args.size; i++) { + Array call = args.items[i].data.array; + char *method_name = call.items[0].data.string.data; + + fprintf(stderr, "%s: %zu\n", method_name, call.size-1); + } + return NIL; +} + +/// run the main thread in ui client mode +/// +/// This is just a stub. the full version will handle input, resizing, etc +void ui_client_execute(uint64_t chan) +{ + while (true) { + loop_poll_events(&main_loop, -1); + } + + getout(0); +} diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h new file mode 100644 index 0000000000..067f78d5c5 --- /dev/null +++ b/src/nvim/ui_client.h @@ -0,0 +1,9 @@ +#ifndef NVIM_UI_CLIENT_H +#define NVIM_UI_CLIENT_H + +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "ui_client.h.generated.h" +#endif +#endif // NVIM_UI_CLIENT_H -- cgit From f291a0339c186cf6eb83174e88029555b3c2631a Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sat, 12 Mar 2022 17:56:53 -0700 Subject: fix: use normal! in default mapping (#17695) --- src/nvim/getchar.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 299456f688..3ec5d24753 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -842,7 +842,10 @@ static void init_typebuf(void) void init_default_mappings(void) { add_map((char_u *)"Y y$", NORMAL, true); - add_map((char_u *)" nohlsearchdiffupdate", NORMAL, true); + + // Use normal! to prevent inserting raw when using i_ + // See https://github.com/neovim/neovim/issues/17473 + add_map((char_u *)" nohlsearchdiffupdatenormal! ", NORMAL, true); add_map((char_u *)" u", INSERT, true); add_map((char_u *)" u", INSERT, true); } -- cgit From 0cf2dc63bf9e3c94e1373142a6198635efd1c514 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 20:40:58 +0800 Subject: fix(win_split_ins): do not fail when oldwin is not valid Ref #14240 --- src/nvim/quickfix.c | 9 --------- src/nvim/window.c | 8 +++----- 2 files changed, 3 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index bddbd04af9..edd4d17e67 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3646,15 +3646,6 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) if (win_split(height, flags) == FAIL) { return FAIL; // not enough room for window } - - // User autocommands may have invalidated the previous window after calling - // win_split, so add a check to ensure that the win is still here - if (IS_LL_STACK(qi) && !win_valid(win)) { - // close the window that was supposed to be for the loclist - win_close(curwin, false, false); - return FAIL; - } - RESET_BINDING(curwin); if (IS_LL_STACK(qi)) { diff --git a/src/nvim/window.c b/src/nvim/window.c index d5299202b0..faf4b117f4 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1420,13 +1420,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) p_wh = i; } - if (!win_valid(oldwin)) { - return FAIL; + if (win_valid(oldwin)) { + // Send the window positions to the UI + oldwin->w_pos_changed = true; } - // Send the window positions to the UI - oldwin->w_pos_changed = true; - return OK; } -- cgit From 91ac0088e1a8bdf189bf96066eb8e0d8e632ceac Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 10 Oct 2020 13:11:34 -0400 Subject: vim-patch:8.1.0877: new buffer used every time the quickfix window is opened Problem: New buffer used every time the quickfix window is opened. Solution: Reuse the buffer. (Yegappan Lakshmanan, closes vim/vim#3902) https://github.com/vim/vim/commit/ee8188fc74a7cf9ee7acb634b2bb7a032d0cb24c --- src/nvim/buffer.c | 12 +++---- src/nvim/quickfix.c | 73 +++++++++++++++++++++++++------------- src/nvim/testdir/test_quickfix.vim | 15 +++----- 3 files changed, 58 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 2e2459aecf..3fdc111b6f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5333,16 +5333,12 @@ bool buf_hide(const buf_T *const buf) char_u *buf_spname(buf_T *buf) { if (bt_quickfix(buf)) { - win_T *win; - tabpage_T *tp; - - // For location list window, w_llist_ref points to the location list. - // For quickfix window, w_llist_ref is NULL. - if (find_win_for_buf(buf, &win, &tp) && win->w_llist_ref != NULL) { - return (char_u *)_(msg_loclist); - } else { + // Differentiate between the quickfix and location list buffers using + // the buffer number stored in the global quickfix stack. + if (buf->b_fnum == qf_stack_get_bufnr()) { return (char_u *)_(msg_qflist); } + return (char_u *)_(msg_loclist); } // There is no _file_ when 'buftype' is "nofile", b_sfname // contains the name as specified by the user. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index edd4d17e67..83a0080703 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -75,6 +75,7 @@ struct qfline_S { // There is a stack of error lists. #define LISTCOUNT 10 #define INVALID_QFIDX (-1) +#define INVALID_QFBUFNR (0) /// Quickfix list type. typedef enum @@ -126,6 +127,7 @@ struct qf_info_S { int qf_curlist; // current error list qf_list_T qf_lists[LISTCOUNT]; qfltype_T qfl_type; // type of list + int qf_bufnr; // quickfix window buffer number }; static qf_info_T ql_info; // global quickfix list @@ -1703,6 +1705,28 @@ static void locstack_queue_delreq(qf_info_T *qi) qf_delq_head = q; } +/// Return the global quickfix stack window buffer number. +int qf_stack_get_bufnr(void) +{ + return ql_info.qf_bufnr; +} + +/// Wipe the quickfix window buffer (if present) for the specified +/// quickfix/location list. +static void wipe_qf_buffer(qf_info_T *qi) + FUNC_ATTR_NONNULL_ALL +{ + if (qi->qf_bufnr != INVALID_QFBUFNR) { + buf_T *const qfbuf = buflist_findnr(qi->qf_bufnr); + if (qfbuf != NULL && qfbuf->b_nwindows == 0) { + // If the quickfix buffer is not loaded in any window, then + // wipe the buffer. + close_buffer(NULL, qfbuf, DOBUF_WIPE, false); + qi->qf_bufnr = INVALID_QFBUFNR; + } + } +} + /// Free a location list stack static void ll_free_all(qf_info_T **pqi) { @@ -1723,6 +1747,9 @@ static void ll_free_all(qf_info_T **pqi) if (quickfix_busy > 0) { locstack_queue_delreq(qi); } else { + // If the quickfix window buffer is loaded, then wipe it + wipe_qf_buffer(qi); + for (i = 0; i < qi->qf_listcount; i++) { qf_free(qf_get_list(qi, i)); } @@ -1885,6 +1912,7 @@ static qf_info_T *qf_alloc_stack(qfltype_T qfltype) qf_info_T *qi = xcalloc(1, sizeof(qf_info_T)); qi->qf_refcount++; qi->qfl_type = qfltype; + qi->qf_bufnr = INVALID_QFBUFNR; return qi; } @@ -2520,8 +2548,9 @@ static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window) return OK; } -// Find a non-quickfix window using the given location list. -// Returns NULL if a matching window is not found. +/// Find a non-quickfix window in the current tabpage using the given location +/// list stack. +/// Returns NULL if a matching window is not found. static win_T *qf_find_win_with_loclist(const qf_info_T *ll) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -3612,7 +3641,7 @@ static void qf_set_cwindow_options(void) // switch off 'swapfile' set_option_value("swf", 0L, NULL, OPT_LOCAL); set_option_value("bt", 0L, "quickfix", OPT_LOCAL); - set_option_value("bh", 0L, "wipe", OPT_LOCAL); + set_option_value("bh", 0L, "hide", OPT_LOCAL); RESET_BINDING(curwin); curwin->w_p_diff = false; set_option_value("fdm", 0L, "manual", OPT_LOCAL); @@ -3669,6 +3698,8 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) if (do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE + ECMD_NOWINENTER, oldwin) == FAIL) { return FAIL; } + // save the number of the new buffer + qi->qf_bufnr = curbuf->b_fnum; } // Set the options for the quickfix buffer/window (if not already done) @@ -3866,6 +3897,15 @@ static win_T *qf_find_win(const qf_info_T *qi) static buf_T *qf_find_buf(qf_info_T *qi) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + if (qi->qf_bufnr != INVALID_QFBUFNR) { + buf_T *const qfbuf = buflist_findnr(qi->qf_bufnr); + if (qfbuf != NULL) { + return qfbuf; + } + // buffer is no longer present + qi->qf_bufnr = INVALID_QFBUFNR; + } + FOR_ALL_TAB_WINDOWS(tp, win) { if (is_qf_win(win, qi)) { return win->w_buffer; @@ -6661,20 +6701,6 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char return retval; } -/// Find the non-location list window with the specified location list stack in -/// the current tabpage. -static win_T *find_win_with_ll(const qf_info_T *qi) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) { - return wp; - } - } - - return NULL; -} - // Free the entire quickfix/location list stack. // If the quickfix/location list window is open, then clear it. static void qf_free_stack(win_T *wp, qf_info_T *qi) @@ -6689,12 +6715,10 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) qf_update_buffer(qi, NULL); } - win_T *llwin = NULL; - win_T *orig_wp = wp; if (wp != NULL && IS_LL_WINDOW(wp)) { // If in the location list window, then use the non-location list // window with this location list (if present) - llwin = find_win_with_ll(qi); + win_T *const llwin = qf_find_win_with_loclist(qi); if (llwin != NULL) { wp = llwin; } @@ -6705,16 +6729,17 @@ static void qf_free_stack(win_T *wp, qf_info_T *qi) // quickfix list qi->qf_curlist = 0; qi->qf_listcount = 0; - } else if (IS_LL_WINDOW(orig_wp)) { + } else if (qfwin != NULL) { // If the location list window is open, then create a new empty location // list qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION); + new_ll->qf_bufnr = qfwin->w_buffer->b_fnum; // first free the list reference in the location list window - ll_free_all(&orig_wp->w_llist_ref); + ll_free_all(&qfwin->w_llist_ref); - orig_wp->w_llist_ref = new_ll; - if (llwin != NULL) { + qfwin->w_llist_ref = new_ll; + if (wp != qfwin) { win_set_loclist(wp, new_ll); } } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 6852f53ea8..12bf6d524c 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -4515,7 +4515,6 @@ func Xqfbuf_test(cchar) endfunc func Test_qfbuf() - throw 'skipped: enable after porting patch 8.1.0877' call Xqfbuf_test('c') call Xqfbuf_test('l') endfunc @@ -5207,16 +5206,14 @@ func Xtest_qftextfunc(cchar) " Non-existing function set quickfixtextfunc=Tabc - " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:') - Xexpr ['F1:10:2:green', 'F1:20:4:blue']" + call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:') call assert_fails("Xwindow", 'E117:') Xclose set quickfixtextfunc& " set option to a non-function set quickfixtextfunc=[10,\ 20] - " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:') - Xexpr ['F1:10:2:green', 'F1:20:4:blue']" + call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E117:') call assert_fails("Xwindow", 'E117:') Xclose set quickfixtextfunc& @@ -5226,8 +5223,7 @@ func Xtest_qftextfunc(cchar) return a:a .. a:b .. a:c endfunc set quickfixtextfunc=Xqftext - " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:') - Xexpr ['F1:10:2:green', 'F1:20:4:blue']" + call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue']", 'E119:') call assert_fails("Xwindow", 'E119:') Xclose @@ -5236,9 +5232,8 @@ func Xtest_qftextfunc(cchar) return ['one', [], 'two'] endfunc set quickfixtextfunc=Xqftext2 - " call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']", - " \ 'E730:') - Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red'] + call assert_fails("Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']", + \ 'E730:') call assert_fails('Xwindow', 'E730:') call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'], \ getline(1, '$')) -- cgit From 6c26ab71ceb9f4f12c55492ba7ab33a5e61af079 Mon Sep 17 00:00:00 2001 From: VVKot Date: Sun, 19 Dec 2021 07:46:28 +0000 Subject: vim-patch:8.1.0892: failure when closing a window when location list is in use Problem: Failure when closing a window when location list is in use. Solution: Handle the situation gracefully. Make sure memory for 'switchbuf' is not freed at the wrong time. (Yegappan Lakshmanan, closes vim/vim#3928) https://github.com/vim/vim/commit/eeb1b9c7ed33c152e041a286d79bf3ed00d80e40 --- src/nvim/eval.c | 2 +- src/nvim/eval/funcs.c | 13 +++++---- src/nvim/quickfix.c | 59 ++++++++++++++++++++++---------------- src/nvim/testdir/test_quickfix.vim | 34 +++++++++++++++++++--- src/nvim/window.c | 8 ++---- 5 files changed, 75 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 83223cdd5c..18b9039d60 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6436,7 +6436,7 @@ win_T *find_win_by_nr_or_id(typval_T *vp) int nr = (int)tv_get_number_chk(vp, NULL); if (nr >= LOWEST_WIN_ID) { - return win_id2wp(vp); + return win_id2wp(tv_get_number(vp)); } return find_win_by_nr(vp, NULL); diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 85c49c20e7..c8abbff933 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2197,12 +2197,13 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "win_execute(win_id, command)" function static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *tp; - win_T *wp = win_id2wp_tp(argvars, &tp); // Return an empty string if something fails. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + int id = tv_get_number(argvars); + tabpage_T *tp; + win_T *wp = win_id2wp_tp(id, &tp); if (wp != NULL && tp != NULL) { WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1)); } @@ -4130,7 +4131,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { - wparg = win_id2wp(argvars); + wparg = win_id2wp(tv_get_number(&argvars[0])); if (wparg == NULL) { return; } @@ -5917,10 +5918,10 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) int fnum; if (argvars[1].v_type != VAR_UNKNOWN) { - tabpage_T *tp; - // use window specified in the second argument - win_T *wp = win_id2wp_tp(&argvars[1], &tp); + int id = (int)tv_get_number(&argvars[1]); + tabpage_T *tp; + win_T *wp = win_id2wp_tp(id, &tp); if (wp != NULL && tp != NULL) { switchwin_T switchwin; if (switch_win_noblock(&switchwin, wp, tp, true) == OK) { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 83a0080703..591c4a6174 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1739,22 +1739,23 @@ static void ll_free_all(qf_info_T **pqi) } *pqi = NULL; // Remove reference to this list + // If the location list is still in use, then queue the delete request + // to be processed later. + if (quickfix_busy > 0) { + locstack_queue_delreq(qi); + return; + } + qi->qf_refcount--; if (qi->qf_refcount < 1) { // No references to this location list. - // If the location list is still in use, then queue the delete request - // to be processed later. - if (quickfix_busy > 0) { - locstack_queue_delreq(qi); - } else { - // If the quickfix window buffer is loaded, then wipe it - wipe_qf_buffer(qi); + // If the quickfix window buffer is loaded, then wipe it + wipe_qf_buffer(qi); - for (i = 0; i < qi->qf_listcount; i++) { - qf_free(qf_get_list(qi, i)); - } - xfree(qi); + for (i = 0; i < qi->qf_listcount; i++) { + qf_free(qf_get_list(qi, i)); } + xfree(qi); } } @@ -2750,7 +2751,7 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, int *opened_window } /// Edit the selected file or help file. -static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win_T *oldwin, +static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int prev_winid, int *opened_window) { qf_list_T *qfl = qf_get_curlist(qi); @@ -2769,7 +2770,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win } else { retval = do_ecmd(qf_ptr->qf_fnum, NULL, NULL, NULL, (linenr_T)1, ECMD_HIDE + ECMD_SET_HELP, - oldwin == curwin ? curwin : NULL); + prev_winid == curwin->handle ? curwin : NULL); } } else { retval = buflist_getfile(qf_ptr->qf_fnum, (linenr_T)1, @@ -2777,10 +2778,13 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, win } // If a location list, check whether the associated window is still // present. - if (qfl_type == QFLT_LOCATION && !win_valid_any_tab(oldwin)) { - emsg(_("E924: Current window was closed")); - *opened_window = false; - return NOTDONE; + if (qfl_type == QFLT_LOCATION) { + win_T *wp = win_id2wp(prev_winid); + if (wp == NULL && curwin->w_llist != qi) { + emsg(_("E924: Current window was closed")); + *opened_window = false; + return NOTDONE; + } } if (qfl_type == QFLT_QUICKFIX && !qflist_valid(NULL, save_qfid)) { @@ -2935,7 +2939,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int /// NOTDONE if the quickfix/location list is freed by an autocmd when opening /// the file. static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int forceit, - win_T *oldwin, int *opened_window, int openfold, int print_message) + int prev_winid, int *opened_window, int openfold, int print_message) { buf_T *old_curbuf; linenr_T old_lnum; @@ -2947,7 +2951,7 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int old_lnum = curwin->w_cursor.lnum; if (qf_ptr->qf_fnum != 0) { - retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, oldwin, + retval = qf_jump_edit_buffer(qi, qf_ptr, forceit, prev_winid, opened_window); if (retval != OK) { return retval; @@ -2996,8 +3000,8 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo int old_qf_index; char_u *old_swb = p_swb; unsigned old_swb_flags = swb_flags; + int prev_winid; int opened_window = false; - win_T *oldwin = curwin; int print_message = true; const bool old_KeyTyped = KeyTyped; // getting file may reset it int retval = OK; @@ -3011,6 +3015,8 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo return; } + incr_quickfix_busy(); + qfl = qf_get_curlist(qi); qf_ptr = qfl->qf_ptr; @@ -3033,6 +3039,8 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo print_message = false; } + prev_winid = curwin->handle; + retval = qf_jump_open_window(qi, qf_ptr, newwin, &opened_window); if (retval == FAIL) { goto failed; @@ -3041,7 +3049,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo goto theend; } - retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, oldwin, + retval = qf_jump_to_buffer(qi, qf_index, qf_ptr, forceit, prev_winid, &opened_window, old_KeyTyped, print_message); if (retval == NOTDONE) { // Quickfix/location list is freed by an autocmd @@ -3066,12 +3074,13 @@ theend: qfl->qf_ptr = qf_ptr; qfl->qf_index = qf_index; } - if (p_swb != old_swb && p_swb == empty_option && opened_window) { + if (p_swb != old_swb && p_swb == empty_option) { // Restore old 'switchbuf' value, but not when an autocommand or // modeline has changed the value. p_swb = old_swb; swb_flags = old_swb_flags; } + decr_quickfix_busy(); } @@ -3679,9 +3688,9 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) if (IS_LL_STACK(qi)) { // For the location list window, create a reference to the - // location list from the window 'win'. - curwin->w_llist_ref = win->w_llist; - win->w_llist->qf_refcount++; + // location list stack from the window 'win'. + curwin->w_llist_ref = qi; + qi->qf_refcount++; } if (oldwin != curwin) { diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 12bf6d524c..b96d11f1b7 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1,4 +1,4 @@ -" Test for the quickfix commands. +" Test for the quickfix feature. source check.vim CheckFeature quickfix @@ -1655,7 +1655,7 @@ func XquickfixSetListWithAct(cchar) \ {'filename': 'fnameD', 'text': 'D'}, \ {'filename': 'fnameE', 'text': 'E'}] - " {action} is unspecified. Same as specifing ' '. + " {action} is unspecified. Same as specifying ' '. new | only silent! Xnewer 99 call g:Xsetlist(list1) @@ -2706,7 +2706,7 @@ func Test_cwindow_jump() " Open a new window and create a location list " Open the location list window and close the other window " Jump to an entry. - " Should create a new window and jump to the entry. The scrtach buffer + " Should create a new window and jump to the entry. The scratch buffer " should not be used. enew | only set buftype=nofile @@ -4360,7 +4360,7 @@ func Test_splitview() new | only " When split opening files from a helpgrep location list window, a new help - " window should be opend with a copy of the location list. + " window should be opened with a copy of the location list. lhelpgrep window let locid = getloclist(0, {'id' : 0}).id lwindow @@ -4519,6 +4519,32 @@ func Test_qfbuf() call Xqfbuf_test('l') endfunc +" If there is an autocmd to use only one window, then opening the location +" list window used to crash Vim. +func Test_winonly_autocmd() + call s:create_test_file('Xtest1') + " Autocmd to show only one Vim window at a time + autocmd WinEnter * only + new + " Load the location list + lexpr "Xtest1:5:Line5\nXtest1:10:Line10\nXtest1:15:Line15" + let loclistid = getloclist(0, {'id' : 0}).id + " Open the location list window. Only this window will be shown and the file + " window is closed. + lopen + call assert_equal(loclistid, getloclist(0, {'id' : 0}).id) + " Jump to an entry in the location list and make sure that the cursor is + " positioned correctly. + ll 3 + call assert_equal(loclistid, getloclist(0, {'id' : 0}).id) + call assert_equal('Xtest1', bufname('')) + call assert_equal(15, line('.')) + " Cleanup + autocmd! WinEnter + new | only + call delete('Xtest1') +endfunc + " Test to make sure that an empty quickfix buffer is not reused for loading " a normal buffer. func Test_empty_qfbuf() diff --git a/src/nvim/window.c b/src/nvim/window.c index faf4b117f4..f80f1f1a65 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -7195,16 +7195,14 @@ void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) tv_list_append_number(list, winnr); } -win_T *win_id2wp(typval_T *argvars) +win_T *win_id2wp(int id) { - return win_id2wp_tp(argvars, NULL); + return win_id2wp_tp(id, NULL); } // Return the window and tab pointer of window "id". -win_T *win_id2wp_tp(typval_T *argvars, tabpage_T **tpp) +win_T *win_id2wp_tp(int id, tabpage_T **tpp) { - int id = tv_get_number(&argvars[0]); - FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { if (tpp != NULL) { -- cgit From 163ec00f44c99b08a932144164b8c467ec672c3c Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 10 Oct 2020 15:42:25 -0400 Subject: vim-patch:8.1.1015: quickfix buffer shows up in list, can't get buffer number Problem: Quickfix buffer shows up in list, can't get buffer number. Solution: Make the quickfix buffer unlisted when the quickfix window is closed. get the quickfix buffer number with getqflist(). (Yegappan Lakshmanan, closes vim/vim#4113) https://github.com/vim/vim/commit/647e24ba3dbf7ff448aa471b1a659a18267ae056 --- src/nvim/quickfix.c | 18 ++++++++++++++++++ src/nvim/testdir/test_quickfix.vim | 17 ++++++++++------- src/nvim/window.c | 5 +++++ 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 591c4a6174..2010322f43 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -6019,6 +6019,15 @@ static int qf_winid(qf_info_T *qi) return 0; } +/// Returns the number of the buffer displayed in the quickfix/location list +/// window. If there is no buffer associated with the list, then returns 0. +static int qf_getprop_qfbufnr(const qf_info_T *qi, dict_T *retdict) + FUNC_ATTR_NONNULL_ARG(2) +{ + return tv_dict_add_nr(retdict, S_LEN("qfbufnr"), + (qi == NULL) ? 0 : qi->qf_bufnr); +} + /// Convert the keys in 'what' to quickfix list property flags. static int qf_getprop_keys2flags(const dict_T *what, bool loclist) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT @@ -6062,6 +6071,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist) if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) { flags |= QF_GETLIST_FILEWINID; } + if (tv_dict_find(what, S_LEN("qfbufnr")) != NULL) { + flags |= QF_GETLIST_QFBUFNR; + } if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) { flags |= QF_GETLIST_QFTF; } @@ -6153,6 +6165,9 @@ static int qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *r if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) { status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0); } + if ((status == OK) && (flags & QF_GETLIST_QFBUFNR)) { + status = qf_getprop_qfbufnr(qi, retdict); + } if ((status == OK) && (flags & QF_GETLIST_QFTF)) { status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), ""); } @@ -6322,6 +6337,9 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) { status = qf_getprop_filewinid(wp, qi, retdict); } + if ((status == OK) && (flags & QF_GETLIST_QFBUFNR)) { + status = qf_getprop_qfbufnr(qi, retdict); + } if ((status == OK) && (flags & QF_GETLIST_QFTF)) { status = qf_getprop_qftf(qfl, retdict); } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index b96d11f1b7..ceb27f7762 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -3548,20 +3548,21 @@ func Xgetlist_empty_tests(cchar) call assert_equal(0, g:Xgetlist({'changedtick' : 0}).changedtick) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, - \ 'items' : [], 'nr' : 0, 'size' : 0, + \ 'items' : [], 'nr' : 0, 'size' : 0, 'qfbufnr' : 0, \ 'title' : '', 'winid' : 0, 'changedtick': 0, \ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, \ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '', \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0, - \ 'quickfixtextfunc' : ''}, + \ 'qfbufnr' : 0, 'quickfixtextfunc' : ''}, \ g:Xgetlist({'all' : 0})) endif " Quickfix window with empty stack silent! Xopen let qfwinid = (a:cchar == 'c') ? win_getid() : 0 + let qfbufnr = (a:cchar == 'c') ? bufnr('') : 0 call assert_equal(qfwinid, g:Xgetlist({'winid' : 0}).winid) Xclose @@ -3593,12 +3594,12 @@ func Xgetlist_empty_tests(cchar) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'quickfixtextfunc' : '', + \ 'qfbufnr' : qfbufnr, 'quickfixtextfunc' : '', \ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0, 'filewinid' : 0, + \ 'changedtick' : 0, 'filewinid' : 0, 'qfbufnr' : 0, \ 'quickfixtextfunc' : ''}, \ g:Xgetlist({'id' : qfid, 'all' : 0})) endif @@ -3616,12 +3617,12 @@ func Xgetlist_empty_tests(cchar) if a:cchar == 'c' call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0, + \ 'changedtick' : 0, 'qfbufnr' : qfbufnr, \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0})) else call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [], \ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0, - \ 'changedtick' : 0, 'filewinid' : 0, + \ 'changedtick' : 0, 'filewinid' : 0, 'qfbufnr' : 0, \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0})) endif endfunc @@ -4456,6 +4457,7 @@ func Xqfbuf_test(cchar) Xclose " Even after the quickfix window is closed, the buffer should be loaded call assert_true(bufloaded(qfbnum)) + call assert_true(qfbnum, g:Xgetlist({'qfbufnr' : 0}).qfbufnr) Xopen " Buffer should be reused when opening the window again call assert_equal(qfbnum, bufnr('')) @@ -4474,7 +4476,7 @@ func Xqfbuf_test(cchar) close " When the location list window is closed, the buffer name should not " change to 'Quickfix List' - call assert_match(qfbnum . ' h- "\[Location List]"', execute('ls')) + call assert_match(qfbnum . 'u h- "\[Location List]"', execute('ls!')) call assert_true(bufloaded(qfbnum)) " After deleting a location list buffer using ":bdelete", opening the @@ -4491,6 +4493,7 @@ func Xqfbuf_test(cchar) " removed call setloclist(0, [], 'f') call assert_false(bufexists(qfbnum)) + call assert_equal(0, getloclist(0, {'qfbufnr' : 0}).qfbufnr) " When the location list is freed with the location list window open, the " location list buffer should not be lost. It should be reused when the diff --git a/src/nvim/window.c b/src/nvim/window.c index f80f1f1a65..0f6aa3b2d5 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2596,6 +2596,11 @@ int win_close(win_T *win, bool free_buf, bool force) reset_synblock(win); } + // When the quickfix/location list window is closed, unlist the buffer. + if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)) { + win->w_buffer->b_p_bl = false; + } + /* * Close the link to the buffer. */ -- cgit From dc32a20503902a91ff7d6383521bf5dfef839876 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Feb 2022 16:17:11 +0800 Subject: test(old): add some missing tests from Vim patches 8.1.2320 and 8.1.2360 --- src/nvim/testdir/test_quickfix.vim | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index ceb27f7762..842e5fe5d7 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -169,8 +169,8 @@ func XlistTests(cchar) \ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}]) let l = split(execute('Xlist', ""), "\n") call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning', - \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', - \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l) + \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', + \ ' 3 Data/Text.hs:30 col 15 warning 33: FileWarning'], l) " For help entries in the quickfix list, only the filename without directory " should be displayed @@ -2020,6 +2020,7 @@ func s:test_xgrep(cchar) enew set makeef=Temp_File_## silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim + call assert_true(len(g:Xgetlist()) == 9) " Try with 'grepprg' set to 'internal' set grepprg=internal @@ -2028,12 +2029,12 @@ func s:test_xgrep(cchar) call assert_true(len(g:Xgetlist()) == 9) set grepprg&vim - call writefile(['Vim'], 'XtestTempFile') - set makeef=XtestTempFile - silent Xgrep Grep_Test_Text: test_quickfix.vim - call assert_equal(5, len(g:Xgetlist())) - call assert_false(filereadable('XtestTempFile')) - set makeef&vim + call writefile(['Vim'], 'XtestTempFile') + set makeef=XtestTempFile + silent Xgrep Grep_Test_Text: test_quickfix.vim + call assert_equal(5, len(g:Xgetlist())) + call assert_false(filereadable('XtestTempFile')) + set makeef&vim endfunc func Test_grep() @@ -2739,6 +2740,25 @@ func Test_cwindow_jump() call assert_true(winnr('$') == 2) call assert_true(winnr() == 1) + " open the quickfix buffer in two windows and jump to an entry. Should open + " the file in the first quickfix window. + enew | only + copen + let bnum = bufnr('') + exe 'sbuffer ' . bnum + wincmd b + cfirst + call assert_equal(2, winnr()) + call assert_equal('F1', bufname('')) + enew | only + exe 'sb' bnum + exe 'botright sb' bnum + wincmd t + clast + call assert_equal(2, winnr()) + call assert_equal('quickfix', getwinvar(1, '&buftype')) + call assert_equal('quickfix', getwinvar(3, '&buftype')) + " Jumping to a file from the location list window should find a usable " window by wrapping around the window list. enew | only -- cgit From c5e47e44aa139fa4f9e2f31fe591ddd6b5ca4909 Mon Sep 17 00:00:00 2001 From: VVKot Date: Sun, 19 Dec 2021 11:00:04 +0000 Subject: vim-patch:8.2.3759: quickfix buffer becomes hidden while still in a window Problem: Quickfix buffer becomes hidden while still in a window. Solution: Check if the closed window is the last window showing the quickfix buffer. (Yegappan Lakshmanan, closes vim/vim#9303, closes vim/vim#9300) https://github.com/vim/vim/commit/78a61068cf2c83e611d954a0fb413a09ad59dc07 --- src/nvim/quickfix.c | 18 +++++++++--------- src/nvim/testdir/test_quickfix.vim | 36 ++++++++++++++++++++++++++++++++++++ src/nvim/window.c | 6 ++++-- 3 files changed, 49 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 2010322f43..e12a8bda2b 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2475,7 +2475,7 @@ static qfline_T *qf_get_entry(qf_list_T *qfl, int errornr, int dir, int *new_qfi return qf_ptr; } -// Find a window displaying a Vim help file. +// Find a window displaying a Vim help file in the current tab page. static win_T *qf_find_help_win(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -2549,8 +2549,8 @@ static int jump_to_help_window(qf_info_T *qi, bool newwin, int *opened_window) return OK; } -/// Find a non-quickfix window in the current tabpage using the given location -/// list stack. +/// Find a non-quickfix window using the given location list stack in the +/// current tabpage. /// Returns NULL if a matching window is not found. static win_T *qf_find_win_with_loclist(const qf_info_T *ll) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT @@ -2563,7 +2563,7 @@ static win_T *qf_find_win_with_loclist(const qf_info_T *ll) return NULL; } -// Find a window containing a normal buffer +/// Find a window containing a normal buffer in the current tab page. static win_T *qf_find_win_with_normal_buf(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -2619,7 +2619,7 @@ static void qf_goto_win_with_ll_file(win_T *use_win, int qf_fnum, qf_info_T *ll_ win_T *win = use_win; if (win == NULL) { - // Find the window showing the selected file + // Find the window showing the selected file in the current tab page. FOR_ALL_WINDOWS_IN_TAB(win2, curtab) { if (win2->w_buffer->b_fnum == qf_fnum) { win = win2; @@ -3887,8 +3887,8 @@ static int is_qf_win(const win_T *win, const qf_info_T *qi) return false; } -/// Find a window displaying the quickfix/location stack 'qi' -/// Only searches in the current tabpage. +/// Find a window displaying the quickfix/location stack 'qi' in the current tab +/// page. static win_T *qf_find_win(const qf_info_T *qi) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -3901,8 +3901,8 @@ static win_T *qf_find_win(const qf_info_T *qi) return NULL; } -// Find a quickfix buffer. -// Searches in windows opened in all the tabs. +/// Find a quickfix buffer. +/// Searches in windows opened in all the tab pages. static buf_T *qf_find_buf(qf_info_T *qi) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 842e5fe5d7..48bafc4b7e 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5446,4 +5446,40 @@ func Test_win_gettype() lclose endfunc +" Test for opening the quickfix window in two tab pages and then closing one +" of the quickfix windows. This should not make the quickfix buffer unlisted. +" (github issue #9300). +func Test_two_qf_windows() + cexpr "F1:1:line1" + copen + tabnew + copen + call assert_true(&buflisted) + cclose + tabfirst + call assert_true(&buflisted) + let bnum = bufnr() + cclose + " if all the quickfix windows are closed, then buffer should be unlisted. + call assert_false(buflisted(bnum)) + %bw! + + " Repeat the test for a location list + lexpr "F2:2:line2" + lopen + let bnum = bufnr() + tabnew + exe "buffer" bnum + tabfirst + lclose + tablast + call assert_true(buflisted(bnum)) + tabclose + lopen + call assert_true(buflisted(bnum)) + lclose + call assert_false(buflisted(bnum)) + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index 0f6aa3b2d5..d659f60e66 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2596,8 +2596,10 @@ int win_close(win_T *win, bool free_buf, bool force) reset_synblock(win); } - // When the quickfix/location list window is closed, unlist the buffer. - if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)) { + // When a quickfix/location list window is closed and the buffer is + // displayed in only one window, then unlist the buffer. + if (win->w_buffer != NULL && bt_quickfix(win->w_buffer) + && win->w_buffer->b_nwindows == 1) { win->w_buffer->b_p_bl = false; } -- cgit From ff48e61ec9ce0fed62b31609c2a83caaa862ccba Mon Sep 17 00:00:00 2001 From: VVKot Date: Sun, 19 Dec 2021 11:06:16 +0000 Subject: vim-patch:8.2.3762: if quickfix buffer is wiped out getqflist() still returns it Problem: If the quickfix buffer is wiped out getqflist() still returns its number. Solution: Use zero if the buffer is no longer present. (Yegappan Lakshmanan, closes vim/vim#9306) https://github.com/vim/vim/commit/56150da6879a96db1c84c7ec4ceedeb84969f606 --- src/nvim/quickfix.c | 12 +++++++++--- src/nvim/testdir/test_quickfix.vim | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index e12a8bda2b..72fd035cbd 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -6020,12 +6020,18 @@ static int qf_winid(qf_info_T *qi) } /// Returns the number of the buffer displayed in the quickfix/location list -/// window. If there is no buffer associated with the list, then returns 0. +/// window. If there is no buffer associated with the list or the buffer is +/// wiped out, then returns 0. static int qf_getprop_qfbufnr(const qf_info_T *qi, dict_T *retdict) FUNC_ATTR_NONNULL_ARG(2) { - return tv_dict_add_nr(retdict, S_LEN("qfbufnr"), - (qi == NULL) ? 0 : qi->qf_bufnr); + int bufnum = 0; + + if (qi != NULL && buflist_findnr(qi->qf_bufnr) != NULL) { + bufnum = qi->qf_bufnr; + } + + return tv_dict_add_nr(retdict, S_LEN("qfbufnr"), bufnum); } /// Convert the keys in 'what' to quickfix list property flags. diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 48bafc4b7e..14d13049d9 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -4483,6 +4483,13 @@ func Xqfbuf_test(cchar) call assert_equal(qfbnum, bufnr('')) Xclose + " When quickfix buffer is wiped out, getqflist() should return 0 + %bw! + Xexpr "" + Xopen + bw! + call assert_equal(0, g:Xgetlist({'qfbufnr': 0}).qfbufnr) + if a:cchar == 'l' %bwipe " For a location list, when both the file window and the location list -- cgit From 9e6bc228ec58b787c0985a65139d1959c9d889f0 Mon Sep 17 00:00:00 2001 From: adrian5 Date: Sun, 13 Mar 2022 13:42:12 +0100 Subject: docs(api): improve section on nvim_set_hl (#17692) --- src/nvim/api/vim.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index b8c66a034c..80fa677485 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -123,26 +123,24 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) abort(); } -/// Set a highlight group. +/// Sets a highlight group. /// -/// Note: unlike the `:highlight` command which can update a highlight group, +/// Note: Unlike the `:highlight` command which can update a highlight group, /// this function completely replaces the definition. For example: /// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group 'Visual'. /// -/// @param ns_id number of namespace for this highlight. Use value 0 -/// to set a highlight group in the global (`:highlight`) -/// namespace. -/// @param name highlight group name, like ErrorMsg -/// @param val highlight definition map, like |nvim_get_hl_by_name|. -/// in addition the following keys are also recognized: -/// `default`: don't override existing definition, -/// like `hi default` -/// `ctermfg`: sets foreground of cterm color -/// `ctermbg`: sets background of cterm color -/// `cterm` : cterm attribute map. sets attributed for -/// cterm colors. similer to `hi cterm` -/// Note: by default cterm attributes are -/// same as attributes of gui color +/// @param ns_id Namespace id for this highlight |nvim_create_namespace()|. +/// Use 0 to set a highlight group globally |:highlight|. +/// @param name Highlight group name, e.g. "ErrorMsg" +/// @param val Highlight definition map, like |synIDattr()|. In +/// addition, the following keys are recognized: +/// - default: Don't override existing definition |:hi-default| +/// - ctermfg: Sets foreground of cterm color |highlight-ctermfg| +/// - ctermbg: Sets background of cterm color |highlight-ctermbg| +/// - cterm: cterm attribute map, like +/// |highlight-args|. +/// Note: Attributes default to those set for `gui` +/// if not set. /// @param[out] err Error details, if any /// // TODO(bfredl): val should take update vs reset flag -- cgit From 4ede2ea4b2e9cd61fb7e634fa35e6a500816be19 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Mar 2022 22:07:22 +0800 Subject: test: fix runnvim.sh (#17690) --- src/nvim/testdir/runnvim.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh index 72d88f9f93..322265737a 100755 --- a/src/nvim/testdir/runnvim.sh +++ b/src/nvim/testdir/runnvim.sh @@ -38,7 +38,7 @@ main() {( -S runnvim.vim \ "$tlog" > "out-$tlog" 2> "err-$tlog" then - fail "$test_name" F "Nvim exited with non-zero code" + fail "$test_name" "Nvim exited with non-zero code" fi { echo "Stdout of :terminal runner" @@ -53,7 +53,7 @@ main() {( if test "$oldesttest" = 1 ; then if ! diff -q test.out "$test_name.ok" > /dev/null 2>&1 ; then if test -f test.out ; then - fail "$test_name" F "Oldest test .out file differs from .ok file" + fail "$test_name" "Oldest test .out file differs from .ok file" { echo "Diff between test.out and $test_name.ok" echo "$separator" -- cgit From 4ba12b3dda34472c193c9fa8ffd7d3bd5b6c04d6 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sun, 13 Mar 2022 15:11:17 +0100 Subject: refactor: fix clint warnings (#17682) --- src/nvim/api/private/helpers.c | 5 +- src/nvim/api/vim.c | 6 +- src/nvim/charset.c | 14 ++-- src/nvim/cursor.c | 146 ++++++++++++++++------------------------- src/nvim/cursor_shape.c | 11 ++-- src/nvim/diff.c | 68 +++++++++---------- src/nvim/digraph.c | 16 ++--- src/nvim/event/socket.c | 13 ++-- src/nvim/indent.c | 13 ++-- src/nvim/lua/converter.c | 25 ++----- src/nvim/os/env.c | 4 +- src/nvim/os/pty_process_win.c | 3 +- src/nvim/os/shell.c | 2 +- src/nvim/rbuffer.h | 14 ++-- src/nvim/screen.h | 10 ++- src/nvim/syntax_defs.h | 16 ++--- src/nvim/tag.h | 10 +-- src/nvim/undo_defs.h | 6 +- src/nvim/viml/parser/parser.h | 6 +- src/nvim/window.h | 12 ++-- 20 files changed, 160 insertions(+), 240 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 8056950e26..9f41393c6b 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -111,7 +111,7 @@ bool try_leave(const TryState *const tstate, Error *const err) /// try_enter()/try_leave() pair should be used instead. void try_start(void) { - ++trylevel; + trylevel++; } /// End try block, set the error message if any and return true if an error @@ -1037,8 +1037,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt aco_save_T aco; try_start(); - switch (opt_type) - { + switch (opt_type) { case SREQ_WIN: if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) == FAIL) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 80fa677485..3292ee2ef8 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -219,7 +219,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks) bool execute = false; bool dangerous = false; - for (size_t i = 0; i < mode.size; ++i) { + for (size_t i = 0; i < mode.size; i++) { switch (mode.data[i]) { case 'n': remap = false; break; @@ -1880,7 +1880,7 @@ static void write_msg(String message, bool to_err) } \ line_buf[pos++] = message.data[i]; - ++no_wait_return; + no_wait_return++; for (uint32_t i = 0; i < message.size; i++) { if (got_int) { break; @@ -1891,7 +1891,7 @@ static void write_msg(String message, bool to_err) PUSH_CHAR(i, out_pos, out_line_buf, msg); } } - --no_wait_return; + no_wait_return--; msg_end(); } diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 3383dd2a76..6a3ff6f433 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -159,7 +159,7 @@ int buf_init_chartab(buf_T *buf, int global) if ((*p == '^') && (p[1] != NUL)) { tilde = true; - ++p; + p++; } if (ascii_isdigit(*p)) { @@ -170,7 +170,7 @@ int buf_init_chartab(buf_T *buf, int global) c2 = -1; if ((*p == '-') && (p[1] != NUL)) { - ++p; + p++; if (ascii_isdigit(*p)) { c2 = getdigits_int((char_u **)&p, true, 0); @@ -243,7 +243,7 @@ int buf_init_chartab(buf_T *buf, int global) } } } - ++c; + c++; } c = *p; @@ -292,7 +292,7 @@ void trans_characters(char_u *buf, int bufsize) memmove(buf + trs_len, buf + 1, (size_t)len); } memmove(buf, trs, (size_t)trs_len); - --len; + len--; } buf += trs_len; } @@ -1314,9 +1314,9 @@ char_u *skiptowhite_esc(char_u *p) { while (*p != ' ' && *p != '\t' && *p != NUL) { if (((*p == '\\') || (*p == Ctrl_V)) && (*(p + 1) != NUL)) { - ++p; + p++; } - ++p; + p++; } return p; } @@ -1686,7 +1686,7 @@ bool rem_backslash(const char_u *str) /// @param p void backslash_halve(char_u *p) { - for (; *p; ++p) { + for (; *p; p++) { if (rem_backslash(p)) { STRMOVE(p, p + 1); } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 55f55a46b2..1c0fd29d5b 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -25,9 +25,7 @@ # include "cursor.c.generated.h" #endif -/* - * Get the screen position of the cursor. - */ +/// @return the screen position of the cursor. int getviscol(void) { colnr_T x; @@ -36,9 +34,7 @@ int getviscol(void) return (int)x; } -/* - * Get the screen position of character col with a coladd in the cursor line. - */ +/// @return the screen position of character col with a coladd in the cursor line. int getviscol2(colnr_T col, colnr_T coladd) { colnr_T x; @@ -51,11 +47,9 @@ int getviscol2(colnr_T col, colnr_T coladd) return (int)x; } -/* - * Go to column "wcol", and add/insert white space as necessary to get the - * cursor in that column. - * The caller must have saved the cursor line for undo! - */ +/// Go to column "wcol", and add/insert white space as necessary to get the +/// cursor in that column. +/// The caller must have saved the cursor line for undo! int coladvance_force(colnr_T wcol) { int rc = coladvance2(&curwin->w_cursor, true, false, wcol); @@ -70,15 +64,13 @@ int coladvance_force(colnr_T wcol) return rc; } -/* - * Try to advance the Cursor to the specified screen column. - * If virtual editing: fine tune the cursor position. - * Note that all virtual positions off the end of a line should share - * a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)), - * beginning at coladd 0. - * - * return OK if desired column is reached, FAIL if not - */ +/// Try to advance the Cursor to the specified screen column. +/// If virtual editing: fine tune the cursor position. +/// Note that all virtual positions off the end of a line should share +/// a curwin->w_cursor.col value (n.b. this is equal to STRLEN(line)), +/// beginning at coladd 0. +/// +/// @return OK if desired column is reached, FAIL if not int coladvance(colnr_T wcol) { int rc = getvpos(&curwin->w_cursor, wcol); @@ -121,7 +113,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a if ((addspaces || finetune) && !VIsual_active) { curwin->w_curswant = linetabsize(line) + one_more; if (curwin->w_curswant > 0) { - --curwin->w_curswant; + curwin->w_curswant--; } } } else { @@ -139,10 +131,10 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a if (wcol / width > (colnr_T)csize / width && ((State & INSERT) == 0 || (int)wcol > csize + 1)) { - /* In case of line wrapping don't move the cursor beyond the - * right screen edge. In Insert mode allow going just beyond - * the last character (like what happens when typing and - * reaching the right window edge). */ + // In case of line wrapping don't move the cursor beyond the + // right screen edge. In Insert mode allow going just beyond + // the last character (like what happens when typing and + // reaching the right window edge). wcol = (csize / width + 1) * width - 1; } } @@ -155,12 +147,10 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a col += csize; } idx = (int)(ptr - line); - /* - * Handle all the special cases. The virtual_active() check - * is needed to ensure that a virtual position off the end of - * a line has the correct indexing. The one_more comparison - * replaces an explicit add of one_more later on. - */ + // Handle all the special cases. The virtual_active() check + // is needed to ensure that a virtual position off the end of + // a line has the correct indexing. The one_more comparison + // replaces an explicit add of one_more later on. if (col > wcol || (!virtual_active() && one_more == 0)) { idx -= 1; // Don't count the chars from 'showbreak'. @@ -172,8 +162,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a && addspaces && wcol >= 0 && ((col != wcol && col != wcol + 1) || csize > 1)) { - /* 'virtualedit' is set: The difference between wcol and col is - * filled with spaces. */ + // 'virtualedit' is set: The difference between wcol and col is filled with spaces. if (line[idx] == NUL) { // Append spaces @@ -256,29 +245,23 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a return OK; } -/* - * Return in "pos" the position of the cursor advanced to screen column "wcol". - * return OK if desired column is reached, FAIL if not - */ +/// Return in "pos" the position of the cursor advanced to screen column "wcol". +/// +/// @return OK if desired column is reached, FAIL if not int getvpos(pos_T *pos, colnr_T wcol) { return coladvance2(pos, false, virtual_active(), wcol); } -/* - * Increment the cursor position. See inc() for return values. - */ +/// Increment the cursor position. See inc() for return values. int inc_cursor(void) { return inc(&curwin->w_cursor); } -/* - * dec(p) - * - * Decrement the line pointer 'p' crossing line boundaries as necessary. - * Return 1 when crossing a line, -1 when at start of file, 0 otherwise. - */ +/// Decrement the line pointer 'p' crossing line boundaries as necessary. +/// +/// @return 1 when crossing a line, -1 when at start of file, 0 otherwise. int dec_cursor(void) { return dec(&curwin->w_cursor); @@ -314,8 +297,8 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) return (lnum < cursor) ? -retval : retval; } -// Make sure "pos.lnum" and "pos.col" are valid in "buf". -// This allows for the col to be on the NUL byte. +/// Make sure "pos.lnum" and "pos.col" are valid in "buf". +/// This allows for the col to be on the NUL byte. void check_pos(buf_T *buf, pos_T *pos) { char_u *line; @@ -334,14 +317,12 @@ void check_pos(buf_T *buf, pos_T *pos) } } -/* - * Make sure curwin->w_cursor.lnum is valid. - */ +/// Make sure curwin->w_cursor.lnum is valid. void check_cursor_lnum(void) { if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { - /* If there is a closed fold at the end of the file, put the cursor in - * its first line. Otherwise in the last line. */ + // If there is a closed fold at the end of the file, put the cursor in + // its first line. Otherwise in the last line. if (!hasFolding(curbuf->b_ml.ml_line_count, &curwin->w_cursor.lnum, NULL)) { curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; @@ -352,9 +333,7 @@ void check_cursor_lnum(void) } } -/* - * Make sure curwin->w_cursor.col is valid. - */ +/// Make sure curwin->w_cursor.col is valid. void check_cursor_col(void) { check_cursor_col_win(curwin); @@ -373,10 +352,10 @@ void check_cursor_col_win(win_T *win) if (len == 0) { win->w_cursor.col = 0; } else if (win->w_cursor.col >= len) { - /* Allow cursor past end-of-line when: - * - in Insert mode or restarting Insert mode - * - in Visual mode and 'selection' isn't "old" - * - 'virtualedit' is set */ + // Allow cursor past end-of-line when: + // - in Insert mode or restarting Insert mode + // - in Visual mode and 'selection' isn't "old" + // - 'virtualedit' is set */ if ((State & INSERT) || restart_edit || (VIsual_active && *p_sel != 'o') || (cur_ve_flags & VE_ONEMORE) @@ -419,32 +398,27 @@ void check_cursor_col_win(win_T *win) } } -/* - * make sure curwin->w_cursor in on a valid character - */ +/// Make sure curwin->w_cursor in on a valid character void check_cursor(void) { check_cursor_lnum(); check_cursor_col(); } -/* - * Make sure curwin->w_cursor is not on the NUL at the end of the line. - * Allow it when in Visual mode and 'selection' is not "old". - */ +/// Make sure curwin->w_cursor is not on the NUL at the end of the line. +/// Allow it when in Visual mode and 'selection' is not "old". void adjust_cursor_col(void) { if (curwin->w_cursor.col > 0 && (!VIsual_active || *p_sel == 'o') && gchar_cursor() == NUL) { - --curwin->w_cursor.col; + curwin->w_cursor.col--; } } -/* - * When curwin->w_leftcol has changed, adjust the cursor position. - * Return true if the cursor was moved. - */ +/// When curwin->w_leftcol has changed, adjust the cursor position. +/// +/// @return true if the cursor was moved. bool leftcol_changed(void) { // TODO(hinidu): I think it should be colnr_T or int, but p_siso is long. @@ -457,10 +431,8 @@ bool leftcol_changed(void) lastcol = curwin->w_leftcol + curwin->w_width_inner - curwin_col_off() - 1; validate_virtcol(); - /* - * If the cursor is right or left of the screen, move it to last or first - * character. - */ + // If the cursor is right or left of the screen, move it to last or first + // character. if (curwin->w_virtcol > (colnr_T)(lastcol - p_siso)) { retval = true; coladvance((colnr_T)(lastcol - p_siso)); @@ -469,11 +441,9 @@ bool leftcol_changed(void) coladvance((colnr_T)(curwin->w_leftcol + p_siso)); } - /* - * If the start of the character under the cursor is not on the screen, - * advance the cursor one more char. If this fails (last char of the - * line) adjust the scrolling. - */ + // If the start of the character under the cursor is not on the screen, + // advance the cursor one more char. If this fails (last char of the + // line) adjust the scrolling. getvvcol(curwin, &curwin->w_cursor, &s, NULL, &e); if (e > (colnr_T)lastcol) { retval = true; @@ -498,27 +468,21 @@ int gchar_cursor(void) return utf_ptr2char(get_cursor_pos_ptr()); } -/* - * Write a character at the current cursor position. - * It is directly written into the block. - */ +/// Write a character at the current cursor position. +/// It is directly written into the block. void pchar_cursor(char_u c) { *(ml_get_buf(curbuf, curwin->w_cursor.lnum, true) + curwin->w_cursor.col) = c; } -/* - * Return pointer to cursor line. - */ +/// @return pointer to cursor line. char_u *get_cursor_line_ptr(void) { return ml_get_buf(curbuf, curwin->w_cursor.lnum, false); } -/* - * Return pointer to cursor position. - */ +/// @return pointer to cursor position. char_u *get_cursor_pos_ptr(void) { return ml_get_buf(curbuf, curwin->w_cursor.lnum, false) + diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 6b0a5dfe12..f19da94512 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -74,8 +74,7 @@ Array mode_style_array(void) PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); PUT(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0)); - PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) - : 0)); + PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0)); } PUT(dic, "name", STRING_OBJ(cstr_to_string(cur->full_name))); PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); @@ -147,7 +146,7 @@ char *parse_shape_opt(int what) if (len == 1 && TOLOWER_ASC(modep[0]) == 'a') { all_idx = SHAPE_IDX_COUNT - 1; } else { - for (idx = 0; idx < SHAPE_IDX_COUNT; ++idx) { + for (idx = 0; idx < SHAPE_IDX_COUNT; idx++) { if (STRNICMP(modep, shape_table[idx].name, len) == 0) { break; } @@ -170,9 +169,7 @@ char *parse_shape_opt(int what) // Parse the part after the colon for (p = colonp + 1; *p && *p != ',';) { { - /* - * First handle the ones with a number argument. - */ + // First handle the ones with a number argument. i = *p; len = 0; if (STRNICMP(p, "ver", 3) == 0) { @@ -245,7 +242,7 @@ char *parse_shape_opt(int what) } // if (what != SHAPE_MOUSE) if (*p == '-') { - ++p; + p++; } } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 80bd3229c6..a3e45b8784 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -167,7 +167,7 @@ void diff_buf_add(buf_T *buf) } int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if (curtab->tp_diffbuf[i] == NULL) { curtab->tp_diffbuf[i] = buf; curtab->tp_diff_invalid = true; @@ -201,7 +201,7 @@ static void diff_buf_clear(void) static int diff_buf_idx(buf_T *buf) { int idx; - for (idx = 0; idx < DB_COUNT; ++idx) { + for (idx = 0; idx < DB_COUNT; idx++) { if (curtab->tp_diffbuf[idx] == buf) { break; } @@ -218,7 +218,7 @@ static int diff_buf_idx(buf_T *buf) static int diff_buf_idx_tp(buf_T *buf, tabpage_T *tp) { int idx; - for (idx = 0; idx < DB_COUNT; ++idx) { + for (idx = 0; idx < DB_COUNT; idx++) { if (tp->tp_diffbuf[idx] == buf) { break; } @@ -323,7 +323,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T dnext->df_lnum[idx] = line1; dnext->df_count[idx] = inserted; int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { if (dprev == NULL) { dnext->df_lnum[i] = line1; @@ -421,7 +421,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T } int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((tp->tp_diffbuf[i] != NULL) && (i != idx)) { dp->df_lnum[i] -= off; dp->df_count[i] += n; @@ -442,7 +442,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // Check if inserted lines are equal, may reduce the size of the // diff. // - // TODO: also check for equal lines in the middle and perhaps split + // TODO(unknown): also check for equal lines in the middle and perhaps split // the block. diff_check_unchanged(tp, dp); } @@ -453,7 +453,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T if ((dprev != NULL) && (dprev->df_lnum[idx] + dprev->df_count[idx] == dp->df_lnum[idx])) { int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if (tp->tp_diffbuf[i] != NULL) { dprev->df_count[i] += dp->df_count[i]; } @@ -474,7 +474,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T while (dp != NULL) { // All counts are zero, remove this entry. int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((tp->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) { break; } @@ -542,7 +542,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) // Find the first buffers, use it as the original, compare the other // buffer lines against this one. int i_org; - for (i_org = 0; i_org < DB_COUNT; ++i_org) { + for (i_org = 0; i_org < DB_COUNT; i_org++) { if (tp->tp_diffbuf[i_org] != NULL) { break; } @@ -574,7 +574,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) false)); int i_new; - for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) { + for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) { if (tp->tp_diffbuf[i_new] == NULL) { continue; } @@ -602,7 +602,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) } // Line matched in all buffers, remove it from the diff. - for (i_new = i_org; i_new < DB_COUNT; ++i_new) { + for (i_new = i_org; i_new < DB_COUNT; i_new++) { if (tp->tp_diffbuf[i_new] != NULL) { if (dir == FORWARD) { dp->df_lnum[i_new]++; @@ -629,7 +629,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) static int diff_check_sanity(tabpage_T *tp, diff_T *dp) { int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if (tp->tp_diffbuf[i] != NULL) { if (dp->df_lnum[i] + dp->df_count[i] - 1 > tp->tp_diffbuf[i]->b_ml.ml_line_count) { @@ -927,7 +927,7 @@ void ex_diffupdate(exarg_T *eap) // Use the first buffer as the original text. int idx_orig; - for (idx_orig = 0; idx_orig < DB_COUNT; ++idx_orig) { + for (idx_orig = 0; idx_orig < DB_COUNT; idx_orig++) { if (curtab->tp_diffbuf[idx_orig] != NULL) { break; } @@ -939,7 +939,7 @@ void ex_diffupdate(exarg_T *eap) // Only need to do something when there is another buffer. int idx_new; - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { + for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { if (curtab->tp_diffbuf[idx_new] != NULL) { break; } @@ -1659,7 +1659,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) off = dp->df_lnum[idx_orig] - hunk->lnum_orig; if (off > 0) { - for (i = idx_orig; i < idx_new; ++i) { + for (i = idx_orig; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { dp->df_lnum[i] -= off; } @@ -1693,7 +1693,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) off = 0; } - for (i = idx_orig; i < idx_new; ++i) { + for (i = idx_orig; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { dp->df_count[i] = dpl->df_lnum[i] + dpl->df_count[i] - dp->df_lnum[i] + off; @@ -1721,7 +1721,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) // Set values for other buffers, these must be equal to the // original buffer, otherwise there would have been a change // already. - for (i = idx_orig + 1; i < idx_new; ++i) { + for (i = idx_orig + 1; i < idx_new; i++) { if (curtab->tp_diffbuf[i] != NULL) { diff_copy_entry(dprev, dp, idx_orig, i); } @@ -1852,7 +1852,7 @@ int diff_check(win_T *wp, linenr_T lnum) // count, check if the lines are identical. cmp = false; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { if (dp->df_count[i] == 0) { zero = true; @@ -1869,7 +1869,7 @@ int diff_check(win_T *wp, linenr_T lnum) if (cmp) { // Compare all lines. If they are equal the lines were inserted // in some buffers, deleted in others, but not changed. - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) { @@ -1899,7 +1899,7 @@ int diff_check(win_T *wp, linenr_T lnum) // Insert filler lines above the line just below the change. Will return // 0 when this buf had the max count. maxcount = 0; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) { maxcount = dp->df_count[i]; } @@ -2073,7 +2073,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) // buffers we need to know the largest count. max_count = 0; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) { max_count = dp->df_count[i]; } @@ -2219,7 +2219,7 @@ int diffopt_changed(void) } if (*p == ',') { - ++p; + p++; } } @@ -2322,7 +2322,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) int off = lnum - dp->df_lnum[idx]; int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (i != idx)) { // Skip lines that are not in the other change (filler lines). if (off >= dp->df_count[i]) { @@ -2430,7 +2430,7 @@ bool diff_infold(win_T *wp, linenr_T lnum) int idx = -1; int i; - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if (curtab->tp_diffbuf[i] == wp->w_buffer) { idx = i; } else if (curtab->tp_diffbuf[i] != NULL) { @@ -2480,7 +2480,7 @@ void nv_diffgetput(bool put, size_t count) if (count == 0) { ea.arg = (char_u *)""; } else { - vim_snprintf(buf, 30, "%zu", count); + vim_snprintf(buf, sizeof(buf), "%zu", count); ea.arg = (char_u *)buf; } @@ -2529,7 +2529,7 @@ void ex_diffgetput(exarg_T *eap) if (*eap->arg == NUL) { // No argument: Find the other buffer in the list of diff buffers. - for (idx_other = 0; idx_other < DB_COUNT; ++idx_other) { + for (idx_other = 0; idx_other < DB_COUNT; idx_other++) { if ((curtab->tp_diffbuf[idx_other] != curbuf) && (curtab->tp_diffbuf[idx_other] != NULL)) { if ((eap->cmdidx != CMD_diffput) @@ -2550,7 +2550,7 @@ void ex_diffgetput(exarg_T *eap) } // Check that there isn't a third buffer in the list - for (i = idx_other + 1; i < DB_COUNT; ++i) { + for (i = idx_other + 1; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != curbuf) && (curtab->tp_diffbuf[i] != NULL) && ((eap->cmdidx != CMD_diffput) @@ -2567,7 +2567,7 @@ void ex_diffgetput(exarg_T *eap) p--; } - for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; ++i) { + for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) { } if (eap->arg + i == p) { @@ -2610,9 +2610,9 @@ void ex_diffgetput(exarg_T *eap) && (eap->line1 == curbuf->b_ml.ml_line_count) && (diff_check(curwin, eap->line1) == 0) && ((eap->line1 == 1) || (diff_check(curwin, eap->line1 - 1) == 0))) { - ++eap->line2; + eap->line2++; } else if (eap->line1 > 0) { - --eap->line1; + eap->line1--; } } @@ -2703,14 +2703,14 @@ void ex_diffgetput(exarg_T *eap) buf_empty = buf_is_empty(curbuf); added = 0; - for (i = 0; i < count; ++i) { + for (i = 0; i < count; i++) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; ml_delete(lnum, false); added--; } - for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; ++i) { + for (i = 0; i < dp->df_count[idx_from] - start_skip - end_skip; i++) { linenr_T nr = dp->df_lnum[idx_from] + start_skip + i; if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { break; @@ -2732,7 +2732,7 @@ void ex_diffgetput(exarg_T *eap) if ((start_skip == 0) && (end_skip == 0)) { // Check if there are any other buffers and if the diff is // equal in them. - for (i = 0; i < DB_COUNT; ++i) { + for (i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (i != idx_from) && (i != idx_to) @@ -2835,7 +2835,7 @@ theend: static void diff_fold_update(diff_T *dp, int skip_idx) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - for (int i = 0; i < DB_COUNT; ++i) { + for (int i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] == wp->w_buffer) && (i != skip_idx)) { foldUpdate(wp, dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i]); } diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 8eda173cac..c1fbc9a0f9 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1555,24 +1555,24 @@ static int getexactdigraph(int char1, int char2, bool meta_char) // Search user digraphs first. digr_T *dp = (digr_T *)user_digraphs.ga_data; - for (int i = 0; i < user_digraphs.ga_len; ++i) { + for (int i = 0; i < user_digraphs.ga_len; i++) { if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { retval = dp->result; break; } - ++dp; + dp++; } // Search default digraphs. if (retval == 0) { dp = digraphdefault; - for (int i = 0; dp->char1 != 0; ++i) { + for (int i = 0; dp->char1 != 0; i++) { if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { retval = dp->result; break; } - ++dp; + dp++; } } @@ -1647,12 +1647,12 @@ void putdigraph(char_u *str) dp = (digr_T *)user_digraphs.ga_data; int i; - for (i = 0; i < user_digraphs.ga_len; ++i) { + for (i = 0; i < user_digraphs.ga_len; i++) { if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { dp->result = n; break; } - ++dp; + dp++; } // Add a new digraph to the table. @@ -1684,7 +1684,7 @@ void listdigraphs(bool use_headers) dp = digraphdefault; - for (int i = 0; dp->char1 != NUL && !got_int; ++i) { + for (int i = 0; dp->char1 != NUL && !got_int; i++) { digr_T tmp; // May need to convert the result to 'encoding'. @@ -1911,7 +1911,7 @@ void ex_loadkeymap(exarg_T *eap) } xfree(kp->from); xfree(kp->to); - --curbuf->b_kmap_ga.ga_len; + curbuf->b_kmap_ga.ga_len--; } } xfree(line); diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index 7948a7be83..c20716496f 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -53,10 +53,8 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint uv_getaddrinfo_t request; int retval = uv_getaddrinfo(&loop->uv, &request, NULL, addr, port, - &(struct addrinfo){ - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - }); + &(struct addrinfo){ .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, }); if (retval != 0) { ELOG("Host lookup failed: %s", endpoint); return retval; @@ -103,10 +101,9 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) // contain 0 in this case, unless uv_tcp_getsockname() is used first. uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas, &(int){ sizeof(sas) }); - uint16_t port = (uint16_t)( - (sas.ss_family == AF_INET) - ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port - : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port); + uint16_t port = (uint16_t)((sas.ss_family == AF_INET) + ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port + : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port); // v:servername uses the string from watcher->addr size_t len = strlen(watcher->addr); snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16, diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 8cc5bc2436..59ba2c92f7 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -70,7 +70,7 @@ int get_indent_str(const char_u *ptr, int ts, bool list) { int count = 0; - for (; *ptr; ++ptr) { + for (; *ptr; ptr++) { // Count a tab for what it is worth. if (*ptr == TAB) { if (!list || curwin->w_p_lcs_chars.tab1) { @@ -442,10 +442,9 @@ int get_breakindent_win(win_T *wp, char_u *line) static long *prev_vts = NULL; // Cached vartabs values. int bri = 0; // window width minus window margin space, i.e. what rests for text - const int eff_wwidth = wp->w_width_inner - - ((wp->w_p_nu || wp->w_p_rnu) - && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) - ? number_width(wp) + 1 : 0); + const int eff_wwidth = wp->w_width_inner - + ((wp->w_p_nu || wp->w_p_rnu) + && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); // used cached indent, unless pointer or 'tabstop' changed if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts @@ -512,7 +511,7 @@ int inindent(int extra) char_u *ptr; colnr_T col; - for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); ++col) { + for (col = 0, ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr); col++) { ptr++; } @@ -630,7 +629,7 @@ int get_lisp_indent(void) continue; } - for (that = get_cursor_line_ptr(); *that != NUL; ++that) { + for (that = get_cursor_line_ptr(); *that != NUL; that++) { if (*that == ';') { while (*(that + 1) != NUL) { that++; diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 2b54e56df1..beea1959e1 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -286,9 +286,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) break; case LUA_TBOOLEAN: cur.tv->v_type = VAR_BOOL; - cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) - ? kBoolVarTrue - : kBoolVarFalse); + cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) ? kBoolVarTrue : kBoolVarFalse); break; case LUA_TSTRING: { size_t len; @@ -551,8 +549,8 @@ static bool typval_conv_special = false; const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \ if (mpval.type == conv_type) { \ if (conv_type == kMPConvDict \ - ? (void *)mpval.data.d.dict == (void *)(val) \ - : (void *)mpval.data.l.list == (void *)(val)) { \ + ? (void *)mpval.data.d.dict == (void *)(val) \ + : (void *)mpval.data.l.list == (void *)(val)) { \ lua_pushvalue(lstate, \ -((int)((kv_size(*mpstack) - backref + 1) * 2))); \ break; \ @@ -1133,10 +1131,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(lstate, -1, &len); - *cur.obj = STRING_OBJ(((String) { - .data = xmemdupz(s, len), - .size = len, - })); + *cur.obj = STRING_OBJ(((String) { .data = xmemdupz(s, len), .size = len })); break; } case LUA_TNUMBER: { @@ -1154,11 +1149,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) switch (table_props.type) { case kObjectTypeArray: - *cur.obj = ARRAY_OBJ(((Array) { - .items = NULL, - .size = 0, - .capacity = 0, - })); + *cur.obj = ARRAY_OBJ(((Array) { .items = NULL, .size = 0, .capacity = 0 })); if (table_props.maxidx != 0) { cur.obj->data.array.items = xcalloc(table_props.maxidx, @@ -1169,11 +1160,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) } break; case kObjectTypeDictionary: - *cur.obj = DICTIONARY_OBJ(((Dictionary) { - .items = NULL, - .size = 0, - .capacity = 0, - })); + *cur.obj = DICTIONARY_OBJ(((Dictionary) { .items = NULL, .size = 0, .capacity = 0 })); if (table_props.string_keys_num != 0) { cur.obj->data.dictionary.items = xcalloc(table_props.string_keys_num, diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index e9868d6b61..b738d36234 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -720,7 +720,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo && dst[-1] != ':' #endif && vim_ispathsep(*tail)) { - ++tail; + tail++; } dst += c; src = tail; @@ -738,7 +738,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo at_start = false; if (src[0] == '\\' && src[1] != NUL) { *dst++ = *src++; - --dstlen; + dstlen--; } else if ((src[0] == ' ' || src[0] == ',') && !one) { at_start = true; } diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 99231968a2..ed9f4636e3 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -58,8 +58,7 @@ int pty_process_spawn(PtyProcess *ptyproc) if (os_has_conpty_working()) { if ((conpty_object = - os_conpty_init(&in_name, &out_name, - ptyproc->width, ptyproc->height)) != NULL) { + os_conpty_init(&in_name, &out_name, ptyproc->width, ptyproc->height)) != NULL) { ptyproc->type = kConpty; } } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 656e059303..5680cdbe42 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -1229,7 +1229,7 @@ static void read_input(DynamicBuffer *buf) dynamic_buffer_ensure(buf, buf->len + 1); buf->data[buf->len++] = NL; } - ++lnum; + lnum++; if (lnum > curbuf->b_op_end.lnum) { break; } diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h index cc690050ab..e86765a4ad 100644 --- a/src/nvim/rbuffer.h +++ b/src/nvim/rbuffer.h @@ -38,17 +38,15 @@ // create infinite loops #define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ for (size_t rcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ - for ( /* NOLINT(readability/braces) */ \ - char *rptr = rbuffer_read_ptr(buf, &rcnt); \ - buf->size; \ - rptr = rbuffer_read_ptr(buf, &rcnt)) + for (char *rptr = rbuffer_read_ptr(buf, &rcnt); /* NOLINT(readability/braces) */ \ + buf->size; \ + rptr = rbuffer_read_ptr(buf, &rcnt)) #define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ for (size_t wcnt = 0, _r = 1; _r; _r = 0) /* NOLINT(readability/braces) */ \ - for ( /* NOLINT(readability/braces) */ \ - char *wptr = rbuffer_write_ptr(buf, &wcnt); \ - rbuffer_space(buf); \ - wptr = rbuffer_write_ptr(buf, &wcnt)) + for (char *wptr = rbuffer_write_ptr(buf, &wcnt); /* NOLINT(readability/braces) */ \ + rbuffer_space(buf); \ + wptr = rbuffer_write_ptr(buf, &wcnt)) // Iteration diff --git a/src/nvim/screen.h b/src/nvim/screen.h index d704a6eb8a..9e8a034d93 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -8,12 +8,10 @@ #include "nvim/pos.h" #include "nvim/types.h" -/* - * flags for update_screen() - * The higher the value, the higher the priority - */ -#define VALID 10 /* buffer not changed, or changes marked - with b_mod_* */ +// flags for update_screen() +// The higher the value, the higher the priority +#define VALID 10 // buffer not changed, or changes marked + // with b_mod_* #define INVERTED 20 // redisplay inverted part that changed #define INVERTED_ALL 25 // redisplay whole inverted part #define REDRAW_TOP 30 // display first w_upd_rows screen lines diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index 526be905e9..a005f67209 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -21,9 +21,7 @@ struct sp_syn { int16_t *cont_in_list; // cont.in group IDs, if non-zero }; -/* - * Each keyword has one keyentry, which is linked in a hash list. - */ +// Each keyword has one keyentry, which is linked in a hash list. typedef struct keyentry keyentry_T; struct keyentry { @@ -35,9 +33,7 @@ struct keyentry { char_u keyword[1]; // actually longer }; -/* - * Struct used to store one state of the state stack. - */ +// Struct used to store one state of the state stack. typedef struct buf_state { int bs_idx; // index of pattern int bs_flags; // flags for pattern @@ -46,10 +42,8 @@ typedef struct buf_state { reg_extmatch_T *bs_extmatch; // external matches from start pattern } bufstate_T; -/* - * syn_state contains the syntax state stack for the start of one line. - * Used by b_sst_array[]. - */ +// syn_state contains the syntax state stack for the start of one line. +// Used by b_sst_array[]. struct syn_state { synstate_T *sst_next; // next entry in used or free list linenr_T sst_lnum; // line number for this state @@ -66,4 +60,4 @@ struct syn_state { // may have made the state invalid }; -#endif // NVIM_SYNTAX_DEFS_H +#endif // NVIM_SYNTAX_DEFS_H diff --git a/src/nvim/tag.h b/src/nvim/tag.h index 64bacceb1b..902fe0c7ba 100644 --- a/src/nvim/tag.h +++ b/src/nvim/tag.h @@ -4,9 +4,7 @@ #include "nvim/ex_cmds_defs.h" #include "nvim/types.h" -/* - * Values for do_tag(). - */ +// Values for do_tag(). #define DT_TAG 1 // jump to newer position or same tag again #define DT_POP 2 // jump to older position #define DT_NEXT 3 // jump to next match of same tag @@ -20,9 +18,7 @@ #define DT_LTAG 11 // tag using location list #define DT_FREE 99 // free cached matches -// // flags for find_tags(). -// #define TAG_HELP 1 // only search for help tags #define TAG_NAMES 2 // only return name of tag #define TAG_REGEXP 4 // use tag pattern as regexp @@ -36,9 +32,7 @@ #define TAG_MANY 300 // When finding many tags (for completion), // find up to this many tags -/* - * Structure used for get_tagfname(). - */ +// Structure used for get_tagfname(). typedef struct { char_u *tn_tags; // value of 'tags' when starting char_u *tn_np; // current position in tn_tags diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 3267b2f71e..d8470b07b1 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -33,8 +33,8 @@ struct u_entry { }; struct u_header { - /* The following have a pointer and a number. The number is used when - * reading the undo file in u_read_undo() */ + // The following have a pointer and a number. The number is used when reading + // the undo file in u_read_undo() union { u_header_T *ptr; // pointer to next undo header in list long seq; @@ -80,4 +80,4 @@ typedef struct { FILE *bi_fp; } bufinfo_T; -#endif // NVIM_UNDO_DEFS_H +#endif // NVIM_UNDO_DEFS_H diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h index b2933c3781..b8835127e7 100644 --- a/src/nvim/viml/parser/parser.h +++ b/src/nvim/viml/parser/parser.h @@ -81,10 +81,8 @@ typedef struct { bool can_continuate; } ParserState; -static inline void viml_parser_init( - ParserState *const ret_pstate, - const ParserLineGetter get_line, void *const cookie, - ParserHighlight *const colors) +static inline void viml_parser_init(ParserState *const ret_pstate, const ParserLineGetter get_line, + void *const cookie, ParserHighlight *const colors) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2); /// Initialize a new parser state instance diff --git a/src/nvim/window.h b/src/nvim/window.h index 42701f72b4..b75b8abd9b 100644 --- a/src/nvim/window.h +++ b/src/nvim/window.h @@ -12,13 +12,11 @@ #define FNAME_EXP 2 // expand to path #define FNAME_HYP 4 // check for hypertext link #define FNAME_INCL 8 // apply 'includeexpr' -#define FNAME_REL 16 /* ".." and "./" are relative to the (current) - file instead of the current directory */ +#define FNAME_REL 16 // ".." and "./" are relative to the (current) + // file instead of the current directory #define FNAME_UNESC 32 // remove backslashes used for escaping -/* - * arguments for win_split() - */ +// arguments for win_split() #define WSP_ROOM 1 // require enough room #define WSP_VERT 2 // split vertically #define WSP_TOP 4 // window at top-left of shell @@ -28,9 +26,7 @@ #define WSP_ABOVE 64 // put new window above/left #define WSP_NEWLOC 128 // don't copy location list -/* - * Minimum screen size - */ +// Minimum screen size #define MIN_COLUMNS 12 // minimal columns for screen #define MIN_LINES 2 // minimal lines for screen -- cgit From 6e1caeaf3a5e4674244220cad5b1e1834a8b8b36 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 13 Mar 2022 21:50:17 +0800 Subject: vim-patch:8.2.4557: confusing comment about 'cursorlineopt' Problem: Confusing comment about 'cursorlineopt'. Solution: Adjust comment. (closes vim/vim#9939) Add parenthesis around logical OR. https://github.com/vim/vim/commit/754d2b40369d8fdcf77fc05cc608f86387016bd9 --- src/nvim/screen.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0a1f388456..6fd431a82a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2788,11 +2788,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && (wp->w_p_culopt_flags & CULOPT_NBR) && (row == startrow + filler_lines || (row > startrow + filler_lines - && wp->w_p_culopt_flags & CULOPT_LINE))) { + && (wp->w_p_culopt_flags & CULOPT_LINE)))) { // When 'cursorline' is set highlight the line number of // the current line differently. - // When 'cursorlineopt' has "screenline" only highlight - // the line number itself. + // When 'cursorlineopt' does not have "line" only + // highlight the line number itself. // TODO(vim): Can we use CursorLine instead of CursorLineNr // when CursorLineNr isn't set? char_attr = win_hl_attr(wp, HLF_CLN); -- cgit From f3f67da340e4501cc1b20642e9bcfbd8c8f1df91 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 12 Mar 2022 18:30:11 +0000 Subject: refactor: column drawing - move some logic out of win_line into specific easy to read sub-functions. - remove drawing logic from get_sign_display_info. --- src/nvim/screen.c | 150 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 6fd431a82a..ac07e60632 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1948,6 +1948,27 @@ static inline void provider_err_virt_text(linenr_T lnum, char *err) decor_add_ephemeral(lnum-1, 0, lnum-1, 0, &err_decor); } +static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len) +{ + long num; + char *fmt = "%*ld "; + + if (wp->w_p_nu && !wp->w_p_rnu) { + // 'number' + 'norelativenumber' + num = (long)lnum; + } else { + // 'relativenumber', don't use negative numbers + num = labs((long)get_cursor_rel_lnum(wp, lnum)); + if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { + // 'number' + 'relativenumber' + num = lnum; + fmt = "%-*ld "; + } + } + + snprintf((char *)buf, buf_len, fmt, number_width(wp), num); +} + /// Display line "lnum" of window 'wp' on the screen. /// wp->w_virtcol needs to be valid. /// @@ -2702,8 +2723,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc get_sign_display_info(false, wp, lnum, sattrs, row, startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + &p_extra, &n_extra, &char_attr, sign_idx); + sign_idx++; + if (sign_idx < wp->w_scwidth) { + draw_state = WL_SIGN - 1; + } else { + sign_idx = 0; + } } } @@ -2722,29 +2748,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc get_sign_display_info(true, wp, lnum, sattrs, row, startrow, filler_lines, filler_todo, &c_extra, &c_final, extra, sizeof(extra), - &p_extra, &n_extra, - &char_attr, &draw_state, &sign_idx); + &p_extra, &n_extra, &char_attr, sign_idx); } else { + // Draw the line number (empty space after wrapping). if (row == startrow + filler_lines) { - // Draw the line number (empty space after wrapping). */ - long num; - char *fmt = "%*ld "; - - if (wp->w_p_nu && !wp->w_p_rnu) { - // 'number' + 'norelativenumber' - num = (long)lnum; - } else { - // 'relativenumber', don't use negative numbers - num = labs((long)get_cursor_rel_lnum(wp, lnum)); - if (num == 0 && wp->w_p_nu && wp->w_p_rnu) { - // 'number' + 'relativenumber' - num = lnum; - fmt = "%-*ld "; - } - } - - snprintf((char *)extra, sizeof(extra), - fmt, number_width(wp), num); + get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra)); if (wp->w_skipcol > 0) { for (p_extra = extra; *p_extra == ' '; p_extra++) { *p_extra = '-'; @@ -2762,41 +2770,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } p_extra = extra; c_extra = NUL; - c_final = NUL; } else { c_extra = ' '; - c_final = NUL; } + c_final = NUL; n_extra = number_width(wp) + 1; - char_attr = win_hl_attr(wp, HLF_N); - - if (wp->w_p_rnu && lnum < wp->w_cursor.lnum) { - // Use LineNrAbove - char_attr = win_hl_attr(wp, HLF_LNA); - } - if (wp->w_p_rnu && lnum > wp->w_cursor.lnum) { - // Use LineNrBelow - char_attr = win_hl_attr(wp, HLF_LNB); - } - - sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); - if (num_sattr != NULL) { - // :sign defined with "numhl" highlight. - char_attr = num_sattr->sat_numhl; - } else if (wp->w_p_cul - && lnum == wp->w_cursor.lnum - && (wp->w_p_culopt_flags & CULOPT_NBR) - && (row == startrow + filler_lines - || (row > startrow + filler_lines - && (wp->w_p_culopt_flags & CULOPT_LINE)))) { - // When 'cursorline' is set highlight the line number of - // the current line differently. - // When 'cursorlineopt' does not have "line" only - // highlight the line number itself. - // TODO(vim): Can we use CursorLine instead of CursorLineNr - // when CursorLineNr isn't set? - char_attr = win_hl_attr(wp, HLF_CLN); - } + char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines, sattrs); } } } @@ -4569,20 +4548,66 @@ static bool use_cursor_line_sign(win_T *wp, linenr_T lnum) && (wp->w_p_culopt_flags & CULOPT_NBR); } +/// Return true if CursorLineNr highlight is to be used for the number column. +/// +/// - 'cursorline' must be set +/// - lnum must be the cursor line +/// - 'cursorlineopt' has "number" +/// - don't highlight filler lines (when in diff mode) +/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number +/// itself on the first screenline of the wrapped line, otherwise highlight the number column of +/// all screenlines of the wrapped line. +static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines) +{ + return wp->w_p_cul + && lnum == wp->w_cursor.lnum + && (wp->w_p_culopt_flags & CULOPT_NBR) + && (row == startrow + filler_lines + || (row > startrow + filler_lines + && (wp->w_p_culopt_flags & CULOPT_LINE))); +} + +static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines, + sign_attrs_T *sattrs) +{ + sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1); + if (num_sattr != NULL) { + // :sign defined with "numhl" highlight. + return num_sattr->sat_numhl; + } + + if (wp->w_p_rnu) { + if (lnum < wp->w_cursor.lnum) { + // Use LineNrAbove + return win_hl_attr(wp, HLF_LNA); + } + if (lnum > wp->w_cursor.lnum) { + // Use LineNrBelow + return win_hl_attr(wp, HLF_LNB); + } + } + + if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) { + // TODO(vim): Can we use CursorLine instead of CursorLineNr + // when CursorLineNr isn't set? + return win_hl_attr(wp, HLF_CLN); + } + + return win_hl_attr(wp, HLF_N); +} + // Get information needed to display the sign in line 'lnum' in window 'wp'. // If 'nrcol' is TRUE, the sign is going to be displayed in the number column. // Otherwise the sign is going to be displayed in the sign column. // // @param count max number of signs // @param[out] n_extrap number of characters from pp_extra to display -// @param[in, out] sign_idxp Index of the displayed sign +// @param sign_idxp Index of the displayed sign static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[], int row, int startrow, int filler_lines, int filler_todo, int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size, - char_u **pp_extra, int *n_extrap, int *char_attrp, - int *draw_statep, int *sign_idxp) + char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx) { - int count = wp->w_scwidth; // Draw cells with the sign value or blank. *c_extrap = ' '; *c_finalp = NUL; @@ -4598,7 +4623,7 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att } if (row == startrow + filler_lines && filler_todo <= 0) { - sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, *sign_idxp, count); + sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, sign_idx, wp->w_scwidth); if (sattr != NULL) { *pp_extra = sattr->sat_text; if (*pp_extra != NULL) { @@ -4641,13 +4666,6 @@ static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_att } } } - - (*sign_idxp)++; - if (*sign_idxp < count) { - *draw_statep = WL_SIGN - 1; - } else { - *sign_idxp = 0; - } } -- cgit From 198bf3a8f2897d679a297fd04e3183dbac8bd61e Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sat, 12 Mar 2022 17:54:31 +0100 Subject: refactor: minimize variable scope and eliminate empty declarations --- src/nvim/api/buffer.c | 3 +- src/nvim/api/deprecated.c | 3 +- src/nvim/api/tabpage.c | 6 +-- src/nvim/autocmd.c | 118 +++++++++++++++------------------------------- src/nvim/change.c | 78 +++++++++++------------------- src/nvim/charset.c | 36 +++++--------- src/nvim/cursor.c | 30 +++++------- src/nvim/cursor_shape.c | 3 +- src/nvim/debugger.c | 30 ++++-------- src/nvim/diff.c | 104 +++++++++++++++------------------------- src/nvim/digraph.c | 32 ++++--------- 11 files changed, 152 insertions(+), 291 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index a3af51008f..9f0cadd5ce 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -597,12 +597,11 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In if (start_row == end_row) { old_byte = (bcount_t)end_col - start_col; } else { - const char *bufline; old_byte += (bcount_t)strlen(str_at_start) - start_col; for (int64_t i = 1; i < end_row - start_row; i++) { int64_t lnum = start_row + i; - bufline = (char *)ml_get_buf(buf, lnum, false); + const char *bufline = (char *)ml_get_buf(buf, lnum, false); old_byte += (bcount_t)(strlen(bufline))+1; } old_byte += (bcount_t)end_col+1; diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index d2013c597c..6a41df0aa9 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -51,11 +51,10 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err) FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(2) { - Integer rv = 0; buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { - return rv; + return 0; } return buf->b_fnum; diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 14b6be8eeb..b994d18c43 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -102,11 +102,10 @@ void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) { - Window rv = 0; tabpage_T *tab = find_tab_by_handle(tabpage, err); if (!tab || !valid_tabpage(tab)) { - return rv; + return 0; } if (tab == curtab) { @@ -130,11 +129,10 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) { - Integer rv = 0; tabpage_T *tab = find_tab_by_handle(tabpage, err); if (!tab) { - return rv; + return 0; } return tabpage_index(tab); diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index dfcdfd8203..8e4b043169 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -137,8 +137,6 @@ static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE // Show the autocommands for one AutoPat. static void aupat_show(AutoPat *ap) { - AutoCmd *ac; - // Check for "got_int" (here and at various places below), which is set // when "q" has been hit for the "--more--" prompt if (got_int) { @@ -153,7 +151,7 @@ static void aupat_show(AutoPat *ap) msg_col = 4; msg_outtrans(ap->pat); - for (ac = ap->cmds; ac != NULL; ac = ac->next) { + for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { // skip removed commands if (aucmd_exec_is_deleted(ac->exec)) { continue; @@ -278,9 +276,6 @@ static void aucmd_del(AutoCmd *ac) /// This is only done when not executing autocommands. static void au_cleanup(void) { - AutoPat *ap; - AutoPat **prev_ap; - if (autocmd_busy || !au_need_clean) { return; } @@ -288,8 +283,8 @@ static void au_cleanup(void) // Loop over all events. FOR_ALL_AUEVENTS(event) { // Loop over all autocommand patterns. - prev_ap = &(first_autopat[(int)event]); - for (ap = *prev_ap; ap != NULL; ap = *prev_ap) { + AutoPat **prev_ap = &(first_autopat[(int)event]); + for (AutoPat *ap = *prev_ap; ap != NULL; ap = *prev_ap) { bool has_cmd = false; // Loop over all commands for this pattern. @@ -351,10 +346,8 @@ AutoPat *au_get_autopat_for_event(event_T event) // autocmds. void aubuflocal_remove(buf_T *buf) { - AutoPatCmd *apc; - // invalidate currently executing autocommands - for (apc = active_apc_list; apc; apc = apc->next) { + for (AutoPatCmd *apc = active_apc_list; apc; apc = apc->next) { if (buf->b_fnum == apc->arg_bufnr) { apc->arg_bufnr = 0; } @@ -586,13 +579,12 @@ event_T event_name2nr(const char_u *start, char_u **end) { const char_u *p; int i; - int len; // the event name ends with end of line, '|', a blank or a comma for (p = start; *p && !ascii_iswhite(*p) && *p != ',' && *p != '|'; p++) { } for (i = 0; event_names[i].name != NULL; i++) { - len = (int)event_names[i].len; + int len = (int)event_names[i].len; if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) { break; } @@ -615,9 +607,7 @@ event_T event_name2nr(const char_u *start, char_u **end) const char *event_nr2name(event_T event) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST { - int i; - - for (i = 0; event_names[i].name != NULL; i++) { + for (int i = 0; event_names[i].name != NULL; i++) { if (event_names[i].event == event) { return event_names[i].name; } @@ -670,11 +660,8 @@ int check_ei(void) // Returns the old value of 'eventignore' in allocated memory. char_u *au_event_disable(char *what) { - char_u *new_ei; - char_u *save_ei; - - save_ei = vim_strsave(p_ei); - new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what)); + char_u *save_ei = vim_strsave(p_ei); + char_u *new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what)); if (*what == ',' && *p_ei == NUL) { STRCPY(new_ei, what + 1); } else { @@ -729,7 +716,6 @@ void au_event_restore(char_u *old_ei) void do_autocmd(char_u *arg_in, int forceit) { char_u *arg = arg_in; - char_u *pat; char_u *envpat = NULL; char_u *cmd; int need_free = false; @@ -747,7 +733,7 @@ void do_autocmd(char_u *arg_in, int forceit) // Scan over the events. // If we find an illegal name, return here, don't do anything. - pat = arg_event_skip(arg, group != AUGROUP_ALL); + char_u *pat = arg_event_skip(arg, group != AUGROUP_ALL); if (pat == NULL) { return; } @@ -877,8 +863,6 @@ int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u * AutoPat *ap; AutoPat **prev_ap; int findgroup; - int patlen; - int is_buflocal; int buflocal_nr; char_u buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" @@ -897,10 +881,10 @@ int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u * } // Loop through all the specified patterns. - patlen = (int)aucmd_pattern_length(pat); + int patlen = (int)aucmd_pattern_length(pat); while (patlen) { // detect special buffer-local patterns - is_buflocal = aupat_is_buflocal(pat, patlen); + int is_buflocal = aupat_is_buflocal(pat, patlen); if (is_buflocal) { buflocal_nr = aupat_get_buflocal_nr(pat, patlen); @@ -968,9 +952,6 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro AutoPat *ap; AutoPat **prev_ap; AutoCmd *ac; - AutoCmd **prev_ac; - int is_buflocal; - int buflocal_nr; int findgroup; char_u buflocal_pat[BUFLOCAL_PAT_LEN]; // for "" @@ -986,8 +967,8 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro // detect special buffer-local patterns - is_buflocal = aupat_is_buflocal(pat, patlen); - buflocal_nr = 0; + int is_buflocal = aupat_is_buflocal(pat, patlen); + int buflocal_nr = 0; if (is_buflocal) { buflocal_nr = aupat_get_buflocal_nr(pat, patlen); @@ -1083,7 +1064,7 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro } // Add the autocmd at the end of the AutoCmd list. - prev_ac = &(ap->cmds); + AutoCmd **prev_ac = &(ap->cmds); while ((ac = *prev_ac) != NULL) { prev_ac = &ac->next; } @@ -1160,16 +1141,14 @@ char_u *aucmd_next_pattern(char_u *pat, size_t patlen) /// @param do_msg give message for no matching autocmds? int do_doautocmd(char_u *arg, bool do_msg, bool *did_something) { - char_u *fname; int nothing_done = true; - int group; if (did_something != NULL) { *did_something = false; } // Check for a legal group name. If not, use AUGROUP_ALL. - group = arg_augroup_get(&arg); + int group = arg_augroup_get(&arg); if (*arg == '*') { emsg(_("E217: Can't execute autocommands for ALL events")); @@ -1178,7 +1157,7 @@ int do_doautocmd(char_u *arg, bool do_msg, bool *did_something) // Scan over the events. // If we find an illegal name, return here, don't do anything. - fname = arg_event_skip(arg, group != AUGROUP_ALL); + char_u *fname = arg_event_skip(arg, group != AUGROUP_ALL); if (fname == NULL) { return FAIL; } @@ -1542,11 +1521,9 @@ bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT /// Return true if the CursorHold/CursorHoldI event can be triggered. bool trigger_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - int state; - if (!did_cursorhold && has_cursorhold() && reg_recording == 0 && typebuf.tb_len == 0 && !ins_compl_active()) { - state = get_real_state(); + int state = get_real_state(); if (state == NORMAL_BUSY || (state & INSERT) != 0) { return true; } @@ -1570,19 +1547,8 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f buf_T *buf, exarg_T *eap) { char_u *sfname = NULL; // short file name - char_u *tail; - bool save_changed; - buf_T *old_curbuf; bool retval = false; - char_u *save_sourcing_name; - linenr_T save_sourcing_lnum; - char_u *save_autocmd_fname; - int save_autocmd_bufnr; - char_u *save_autocmd_match; - int save_autocmd_busy; - int save_autocmd_nested; static int nesting = 0; - AutoPatCmd patcmd; AutoPat *ap; char_u *save_cmdarg; long save_cmdbang; @@ -1639,13 +1605,13 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f } // Save the autocmd_* variables and info about the current buffer. - save_autocmd_fname = autocmd_fname; - save_autocmd_bufnr = autocmd_bufnr; - save_autocmd_match = autocmd_match; - save_autocmd_busy = autocmd_busy; - save_autocmd_nested = autocmd_nested; - save_changed = curbuf->b_changed; - old_curbuf = curbuf; + char_u *save_autocmd_fname = autocmd_fname; + int save_autocmd_bufnr = autocmd_bufnr; + char_u *save_autocmd_match = autocmd_match; + int save_autocmd_busy = autocmd_busy; + int save_autocmd_nested = autocmd_nested; + bool save_changed = curbuf->b_changed; + buf_T *old_curbuf = curbuf; // Set the file name to be used for . // Make a copy to avoid that changing a buffer name or directory makes it @@ -1738,9 +1704,9 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f // Don't redraw while doing autocommands. RedrawingDisabled++; - save_sourcing_name = sourcing_name; + char_u *save_sourcing_name = sourcing_name; sourcing_name = NULL; // don't free this one - save_sourcing_lnum = sourcing_lnum; + linenr_T save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 0; // no line number here const sctx_T save_current_sctx = current_sctx; @@ -1773,9 +1739,10 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f did_filetype = true; } - tail = path_tail(fname); + char_u *tail = path_tail(fname); // Find first autocommand that matches + AutoPatCmd patcmd; patcmd.curpat = first_autopat[(int)event]; patcmd.nextcmd = NULL; patcmd.group = group; @@ -2009,7 +1976,6 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) AutoPatCmd *acp = (AutoPatCmd *)cookie; char_u *retval; - AutoCmd *ac; // Can be called again after returning the last line. if (acp->curpat == NULL) { @@ -2046,7 +2012,7 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) } } - ac = acp->nextcmd; + AutoCmd *ac = acp->nextcmd; if (p_verbose >= 9) { verbose_enter_scroll(); @@ -2099,12 +2065,10 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) /// @param buf buffer the file is open in bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSED_RESULT { - AutoPat *ap; - char_u *fname; char_u *tail = path_tail(sfname); bool retval = false; - fname = (char_u *)FullName_save((char *)sfname, false); + char_u *fname = (char_u *)FullName_save((char *)sfname, false); if (fname == NULL) { return false; } @@ -2117,7 +2081,7 @@ bool has_autocmd(event_T event, char_u *sfname, buf_T *buf) FUNC_ATTR_WARN_UNUSE forward_slash(fname); #endif - for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { + for (AutoPat *ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { if (ap->pat != NULL && ap->cmds != NULL && (ap->buflocal_nr == 0 ? match_file_pat(NULL, @@ -2153,13 +2117,10 @@ char_u *expand_get_augroup_name(expand_T *xp, int idx) /// @param doautocmd true for :doauto*, false for :autocmd char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd) { - char_u *p; - int group; - // check for a group name, skip it if present autocmd_include_groups = false; - p = arg; - group = arg_augroup_get(&arg); + char_u *p = arg; + int group = arg_augroup_get(&arg); // If there only is a group name that's what we expand. if (*arg == NUL && group != AUGROUP_ALL && !ascii_iswhite(arg[-1])) { @@ -2205,7 +2166,6 @@ char_u *expand_get_event_name(expand_T *xp, int idx) // xp is a required parameter to be used with ExpandGeneric (void)xp; - // List group names char *name = augroup_name(idx + 1); if (name != NULL) { @@ -2247,10 +2207,7 @@ bool autocmd_supported(const char *const event) /// @param arg autocommand string bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT { - event_T event; - AutoPat *ap; buf_T *buflocal_buf = NULL; - int group; bool retval = false; // Make a copy so that we can change the '#' chars to a NUL. @@ -2261,7 +2218,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT } // First, look for an autocmd group name. - group = augroup_find(arg_save); + int group = augroup_find(arg_save); char *event_name; if (group == AUGROUP_ERROR) { // Didn't match a group name, assume the first argument is an event. @@ -2285,7 +2242,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT char *pattern = p; // "pattern" is NULL when there is no pattern. // Find the index (enum) for the event name. - event = event_name2nr((char_u *)event_name, (char_u **)&p); + event_T event = event_name2nr((char_u *)event_name, (char_u **)&p); // return false if the event name is not recognized if (event == NUM_EVENTS) { @@ -2295,7 +2252,7 @@ bool au_exists(const char *const arg) FUNC_ATTR_WARN_UNUSED_RESULT // Find the first autocommand for this event. // If there isn't any, return false; // If there is one and no pattern given, return true; - ap = first_autopat[(int)event]; + AutoPat *ap = first_autopat[(int)event]; if (ap == NULL) { goto theend; } @@ -2535,7 +2492,6 @@ static char_u *arg_event_skip(char_u *arg, int have_group) // Returns the group ID or AUGROUP_ALL. static int arg_augroup_get(char_u **argp) { - char_u *group_name; char_u *p; char_u *arg = *argp; int group = AUGROUP_ALL; @@ -2543,7 +2499,7 @@ static int arg_augroup_get(char_u **argp) for (p = arg; *p && !ascii_iswhite(*p) && *p != '|'; p++) { } if (p > arg) { - group_name = vim_strnsave(arg, (size_t)(p - arg)); + char_u *group_name = vim_strnsave(arg, (size_t)(p - arg)); group = augroup_find((char *)group_name); if (group == AUGROUP_ERROR) { group = AUGROUP_ALL; // no match, use all groups diff --git a/src/nvim/change.c b/src/nvim/change.c index 736867b6d3..607414ac3c 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -42,7 +42,7 @@ /// Careful: may trigger autocommands that reload the buffer. void change_warning(buf_T *buf, int col) { - static char *w_readonly = N_("W10: Warning: Changing a readonly file"); + static const char *w_readonly = N_("W10: Warning: Changing a readonly file"); if (buf->b_did_warn == false && curbufIsChanged() == 0 @@ -140,10 +140,6 @@ void changed_internal(void) /// Careful: may trigger autocommands that reload the buffer. static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra) { - int i; - pos_T *p; - int add; - // mark the buffer as modified changed(); @@ -158,13 +154,14 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra // Create a new entry if a new undo-able change was started or we // don't have an entry yet. if (curbuf->b_new_change || curbuf->b_changelistlen == 0) { + int add; if (curbuf->b_changelistlen == 0) { add = true; } else { // Don't create a new entry when the line number is the same // as the last one and the column is not too far away. Avoids // creating many entries for typing "xxxxx". - p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark; + pos_T *p = &curbuf->b_changelist[curbuf->b_changelistlen - 1].mark; if (p->lnum != lnum) { add = true; } else { @@ -243,7 +240,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra // If the changed line is in a range of previously folded lines, // compare with the first line in that range. if (wp->w_cursor.lnum <= lnum) { - i = find_wl_entry(wp, lnum); + int i = find_wl_entry(wp, lnum); if (i >= 0 && wp->w_cursor.lnum > wp->w_lines[i].wl_lnum) { changed_line_abv_curs_win(wp); } @@ -264,7 +261,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra // For entries below the change: Correct the lnums for // inserted/deleted lines. Makes it possible to stop displaying // after the change. - for (i = 0; i < wp->w_lines_valid; i++) { + for (int i = 0; i < wp->w_lines_valid; i++) { if (wp->w_lines[i].wl_valid) { if (wp->w_lines[i].wl_lnum >= lnum) { if (wp->w_lines[i].wl_lnum < lnume) { @@ -352,12 +349,10 @@ void changed_bytes(linenr_T lnum, colnr_T col) // Diff highlighting in other diff windows may need to be updated too. if (curwin->w_p_diff) { - linenr_T wlnum; - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_p_diff && wp != curwin) { redraw_later(wp, VALID); - wlnum = diff_lnum_win(lnum, wp); + linenr_T wlnum = diff_lnum_win(lnum, wp); if (wlnum > 0) { changedOneline(wp->w_buffer, wlnum); } @@ -673,21 +668,18 @@ void ins_char_bytes(char_u *buf, size_t charlen) /// Caller must have prepared for undo. void ins_str(char_u *s) { - char_u *oldp, *newp; int newlen = (int)STRLEN(s); - int oldlen; - colnr_T col; linenr_T lnum = curwin->w_cursor.lnum; if (virtual_active() && curwin->w_cursor.coladd > 0) { coladvance_force(getviscol()); } - col = curwin->w_cursor.col; - oldp = ml_get(lnum); - oldlen = (int)STRLEN(oldp); + colnr_T col = curwin->w_cursor.col; + char_u *oldp = ml_get(lnum); + int oldlen = (int)STRLEN(oldp); - newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1); + char_u *newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1); if (col > 0) { memmove(newp, oldp, (size_t)col); } @@ -719,13 +711,9 @@ int del_char(bool fixpos) int del_chars(long count, int fixpos) { int bytes = 0; - long i; - char_u *p; - int l; - - p = get_cursor_pos_ptr(); - for (i = 0; i < count && *p != NUL; i++) { - l = utfc_ptr2len(p); + char_u *p = get_cursor_pos_ptr(); + for (long i = 0; i < count && *p != NUL; i++) { + int l = utfc_ptr2len(p); bytes += l; p += l; } @@ -768,12 +756,11 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine) if (p_deco && use_delcombine && utfc_ptr2len(oldp + col) >= count) { int cc[MAX_MCO]; - int n; (void)utfc_ptr2char(oldp + col, cc); if (cc[0] != NUL) { // Find the last composing char, there can be several. - n = col; + int n = col; do { col = n; count = utf_ptr2len(oldp + n); @@ -828,23 +815,18 @@ int copy_indent(int size, char_u *src) { char_u *p = NULL; char_u *line = NULL; - char_u *s; - int todo; int ind_len; int line_len = 0; int tab_pad; - int ind_done; - int round; - int ind_col; // Round 1: compute the number of characters needed for the indent // Round 2: copy the characters. - for (round = 1; round <= 2; round++) { - todo = size; + for (int round = 1; round <= 2; round++) { + int todo = size; ind_len = 0; - ind_done = 0; - ind_col = 0; - s = src; + int ind_done = 0; + int ind_col = 0; + char_u *s = src; // Count/copy the usable portion of the source line. while (todo > 0 && ascii_iswhite(*s)) { @@ -1065,7 +1047,6 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) if (!trunc_line && do_si && *saved_line != NUL && (p_extra == NULL || first_char != '{')) { char_u *ptr; - char_u last_char; old_cursor = curwin->w_cursor; ptr = saved_line; @@ -1075,8 +1056,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) lead_len = 0; } if (dir == FORWARD) { - // Skip preprocessor directives, unless they are - // recognised as comments. + // Skip preprocessor directives, unless they are recognised as comments. if (lead_len == 0 && ptr[0] == '#') { while (ptr[0] == '#' && curwin->w_cursor.lnum > 1) { ptr = ml_get(--curwin->w_cursor.lnum); @@ -1119,7 +1099,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) while (p > ptr && ascii_iswhite(*p)) { p--; } - last_char = *p; + char_u last_char = *p; // find the character just before the '{' or ';' if (last_char == '{' || last_char == ';') { @@ -1896,10 +1876,8 @@ void del_lines(long nlines, bool undo) /// If "include_space" is set, include trailing whitespace while calculating the length. int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_space) { - int i, j; - int result; + int j; int got_com = false; - int found_one; char_u part_buf[COM_MAX_LEN]; // buffer for one option part char_u *string; // pointer to comment string char_u *list; @@ -1907,7 +1885,8 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa char_u *prev_list; char_u *saved_flags = NULL; - result = i = 0; + int result = 0; + int i = 0; while (ascii_iswhite(line[i])) { // leading white space is ignored i++; } @@ -1915,7 +1894,7 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa // Repeat to match several nested comment strings. while (line[i] != NUL) { // scan through the 'comments' option for a match - found_one = false; + int found_one = false; for (list = curbuf->b_p_com; *list;) { // Get one option part into part_buf[]. Advance "list" to next // one. Put "string" at start of string. @@ -2041,20 +2020,19 @@ int get_leader_len(char_u *line, char_u **flags, bool backward, bool include_spa int get_last_leader_offset(char_u *line, char_u **flags) { int result = -1; - int i, j; + int j; int lower_check_bound = 0; char_u *string; char_u *com_leader; char_u *com_flags; char_u *list; - int found_one; char_u part_buf[COM_MAX_LEN]; // buffer for one option part // Repeat to match several nested comment strings. - i = (int)STRLEN(line); + int i = (int)STRLEN(line); while (--i >= lower_check_bound) { // scan through the 'comments' option for a match - found_one = false; + int found_one = false; for (list = curbuf->b_p_com; *list;) { char_u *flags_save = list; diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 6a3ff6f433..986a89b0a5 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -268,15 +268,12 @@ int buf_init_chartab(buf_T *buf, int global) /// @param bufsize void trans_characters(char_u *buf, int bufsize) { - int len; // length of string needing translation - int room; // room in buffer after string - char_u *trs; // translated character - int trs_len; // length of trs[] - - len = (int)STRLEN(buf); - room = bufsize - len; + char_u *trs; // translated character + int len = (int)STRLEN(buf); // length of string needing translation + int room = bufsize - len; // room in buffer after string while (*buf != 0) { + int trs_len; // length of trs[] // Assume a multi-byte character doesn't need translation. if ((trs_len = utfc_ptr2len(buf)) > 1) { len -= trs_len; @@ -873,14 +870,11 @@ bool vim_isprintc_strict(int c) bool in_win_border(win_T *wp, colnr_T vcol) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { - int width1; // width of first line (after line number) - int width2; // width of further lines - if (wp->w_width_inner == 0) { // there is no border return false; } - width1 = wp->w_width_inner - win_col_off(wp); + int width1 = wp->w_width_inner - win_col_off(wp); // width of first line (after line number) if ((int)vcol < width1 - 1) { return false; @@ -889,7 +883,7 @@ bool in_win_border(win_T *wp, colnr_T vcol) if ((int)vcol == width1 - 1) { return true; } - width2 = width1 + win_col_off2(wp); + int width2 = width1 + win_col_off2(wp); // width of further lines if (width2 <= 0) { return false; @@ -911,18 +905,15 @@ bool in_win_border(win_T *wp, colnr_T vcol) /// @param end void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) { - colnr_T vcol; char_u *ptr; // points to current char char_u *posptr; // points to char at pos->col - char_u *line; // start of the line int incr; int head; long *vts = wp->w_buffer->b_p_vts_array; int ts = (int)wp->w_buffer->b_p_ts; - int c; - vcol = 0; - line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); + colnr_T vcol = 0; + char_u *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); // start of the line if (pos->col == MAXCOL) { // continue until the NUL @@ -949,7 +940,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en && !wp->w_p_bri) { for (;;) { head = 0; - c = *ptr; + int c = *ptr; // make sure we don't go past the end of the line if (c == NUL) { @@ -1066,19 +1057,16 @@ colnr_T getvcol_nolist(pos_T *posp) void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) { colnr_T col; - colnr_T coladd; - colnr_T endadd; - char_u *ptr; if (virtual_active()) { // For virtual mode, only want one value getvcol(wp, pos, &col, NULL, NULL); - coladd = pos->coladd; - endadd = 0; + colnr_T coladd = pos->coladd; + colnr_T endadd = 0; // Cannot put the cursor on part of a wide character. - ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); + char_u *ptr = ml_get_buf(wp->w_buffer, pos->lnum, false); if (pos->col < (colnr_T)STRLEN(ptr)) { int c = utf_ptr2char(ptr + pos->col); diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 1c0fd29d5b..d924119fdf 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -92,19 +92,16 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a { colnr_T wcol = wcol_arg; int idx; - char_u *ptr; - char_u *line; colnr_T col = 0; - int csize = 0; - int one_more; int head = 0; - one_more = (State & INSERT) - || (State & TERM_FOCUS) - || restart_edit != NUL - || (VIsual_active && *p_sel != 'o') - || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL); - line = ml_get_buf(curbuf, pos->lnum, false); + int one_more = (State & INSERT) + || (State & TERM_FOCUS) + || restart_edit != NUL + || (VIsual_active && *p_sel != 'o') + || ((get_ve_flags() & VE_ONEMORE) && wcol < MAXCOL); + + char_u *line = ml_get_buf(curbuf, pos->lnum, false); if (wcol >= MAXCOL) { idx = (int)STRLEN(line) - 1 + one_more; @@ -118,6 +115,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } } else { int width = curwin->w_width_inner - win_col_off(curwin); + int csize = 0; if (finetune && curwin->w_p_wrap @@ -139,7 +137,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a } } - ptr = line; + char_u *ptr = line; while (col <= wcol && *ptr != NUL) { // Count a tab for what it's worth (if list mode not on) csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); @@ -301,16 +299,13 @@ linenr_T get_cursor_rel_lnum(win_T *wp, linenr_T lnum) /// This allows for the col to be on the NUL byte. void check_pos(buf_T *buf, pos_T *pos) { - char_u *line; - colnr_T len; - if (pos->lnum > buf->b_ml.ml_line_count) { pos->lnum = buf->b_ml.ml_line_count; } if (pos->col > 0) { - line = ml_get_buf(buf, pos->lnum, false); - len = (colnr_T)STRLEN(line); + char_u *line = ml_get_buf(buf, pos->lnum, false); + colnr_T len = (colnr_T)STRLEN(line); if (pos->col > len) { pos->col = len; } @@ -343,12 +338,11 @@ void check_cursor_col(void) /// @see mb_check_adjust_col void check_cursor_col_win(win_T *win) { - colnr_T len; colnr_T oldcol = win->w_cursor.col; colnr_T oldcoladd = win->w_cursor.col + win->w_cursor.coladd; unsigned int cur_ve_flags = get_ve_flags(); - len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false)); + colnr_T len = (colnr_T)STRLEN(ml_get_buf(win->w_buffer, win->w_cursor.lnum, false)); if (len == 0) { win->w_cursor.col = 0; } else if (win->w_cursor.col >= len) { diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index f19da94512..cb8c8cddf6 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -94,7 +94,6 @@ Array mode_style_array(void) /// @returns error message for an illegal option, NULL otherwise. char *parse_shape_opt(int what) { - char_u *modep; char_u *colonp; char_u *commap; char_u *slashp; @@ -119,7 +118,7 @@ char *parse_shape_opt(int what) } } // Repeat for all comma separated parts. - modep = p_guicursor; + char_u *modep = p_guicursor; while (modep != NULL && *modep != NUL) { colonp = vim_strchr(modep, ':'); commap = vim_strchr(modep, ','); diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index b48a3155b6..cd823546b4 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -58,7 +58,6 @@ void do_debug(char_u *cmd) tasave_T typeaheadbuf; bool typeahead_saved = false; int save_ignore_script = 0; - int save_ex_normal_busy; int n; char_u *cmdline = NULL; char_u *p; @@ -117,7 +116,7 @@ void do_debug(char_u *cmd) // with the commands being executed. Reset "ex_normal_busy" to avoid // the side effects of using ":normal". Save the stuff buffer and make // it empty. Set ignore_script to avoid reading from script input. - save_ex_normal_busy = ex_normal_busy; + int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; if (!debug_greedy) { save_typeahead(&typeaheadbuf); @@ -390,11 +389,10 @@ static char_u *debug_skipped_name; /// Called from do_one_cmd() before executing a command. void dbg_check_breakpoint(exarg_T *eap) { - char *p; - debug_skipped = false; if (debug_breakpoint_name != NULL) { if (!eap->skip) { + char *p; // replace K_SNR with "" if (debug_breakpoint_name[0] == K_SPECIAL && debug_breakpoint_name[1] == KS_EXTRA @@ -430,12 +428,10 @@ void dbg_check_breakpoint(exarg_T *eap) /// @return true when the debug mode is entered this time. bool dbg_check_skipped(exarg_T *eap) { - int prev_got_int; - if (debug_skipped) { // Save the value of got_int and reset it. We don't want a previous // interruption cause flushing the input buffer. - prev_got_int = got_int; + int prev_got_int = got_int; got_int = false; debug_breakpoint_name = debug_skipped_name; // eap->skip is true @@ -482,12 +478,11 @@ static int dbg_parsearg(char_u *arg, garray_T *gap) { char_u *p = arg; char_u *q; - struct debuggy *bp; bool here = false; ga_grow(gap, 1); - bp = &DEBUGGY(gap, gap->ga_len); + struct debuggy *bp = &DEBUGGY(gap, gap->ga_len); // Find "func" or "file". if (STRNCMP(p, "func", 4) == 0) { @@ -564,16 +559,13 @@ static int dbg_parsearg(char_u *arg, garray_T *gap) /// ":breakadd". Also used for ":profile". void ex_breakadd(exarg_T *eap) { - struct debuggy *bp; - garray_T *gap; - - gap = &dbg_breakp; + garray_T *gap = &dbg_breakp; if (eap->cmdidx == CMD_profile) { gap = &prof_ga; } if (dbg_parsearg(eap->arg, gap) == OK) { - bp = &DEBUGGY(gap, gap->ga_len); + struct debuggy *bp = &DEBUGGY(gap, gap->ga_len); bp->dbg_forceit = eap->forceit; if (bp->dbg_type != DBG_EXPR) { @@ -616,20 +608,18 @@ void ex_debuggreedy(exarg_T *eap) void ex_breakdel(exarg_T *eap) { struct debuggy *bp, *bpi; - int nr; int todel = -1; bool del_all = false; linenr_T best_lnum = 0; - garray_T *gap; + garray_T *gap = &dbg_breakp; - gap = &dbg_breakp; if (eap->cmdidx == CMD_profdel) { gap = &prof_ga; } if (ascii_isdigit(*eap->arg)) { // ":breakdel {nr}" - nr = atoi((char *)eap->arg); + int nr = atoi((char *)eap->arg); for (int i = 0; i < gap->ga_len; i++) { if (DEBUGGY(gap, i).dbg_nr == nr) { todel = i; @@ -693,13 +683,11 @@ void ex_breakdel(exarg_T *eap) /// ":breaklist". void ex_breaklist(exarg_T *eap) { - struct debuggy *bp; - if (GA_EMPTY(&dbg_breakp)) { msg(_("No breakpoints defined")); } else { for (int i = 0; i < dbg_breakp.ga_len; i++) { - bp = &BREAKP(i); + struct debuggy *bp = &BREAKP(i); if (bp->dbg_type == DBG_FILE) { home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true); } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a3e45b8784..1b8a9f41e9 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -166,8 +166,7 @@ void diff_buf_add(buf_T *buf) return; } - int i; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if (curtab->tp_diffbuf[i] == NULL) { curtab->tp_diffbuf[i] = buf; curtab->tp_diff_invalid = true; @@ -304,7 +303,6 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T diff_T *dprev = NULL; diff_T *dp = tp->tp_first_diff; - linenr_T last; linenr_T lnum_deleted = line1; // lnum of remaining deletion int n; int off; @@ -354,7 +352,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T // 3 5 6 // compute last line of this change - last = dp->df_lnum[idx] + dp->df_count[idx] - 1; + linenr_T last = dp->df_lnum[idx] + dp->df_count[idx] - 1; // 1. change completely above line1: nothing to do if (last >= line1 - 1) { @@ -628,8 +626,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) /// @return OK if the diff block doesn't contain invalid line numbers. static int diff_check_sanity(tabpage_T *tp, diff_T *dp) { - int i; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if (tp->tp_diffbuf[i] != NULL) { if (dp->df_lnum[i] + dp->df_count[i] - 1 > tp->tp_diffbuf[i]->b_ml.ml_line_count) { @@ -719,16 +716,13 @@ static void clear_diffout(diffout_T *dout) /// @return FAIL for failure. static int diff_write_buffer(buf_T *buf, diffin_T *din) { - linenr_T lnum; - char_u *s; long len = 0; - char_u *ptr; // xdiff requires one big block of memory with all the text. - for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1; } - ptr = try_malloc(len); + char_u *ptr = try_malloc(len); if (ptr == NULL) { // Allocating memory failed. This can happen, because we try to read // the whole buffer text into memory. Set the failed flag, the diff @@ -746,8 +740,8 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) din->din_mmfile.size = len; len = 0; - for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { - for (s = ml_get_buf(buf, lnum, false); *s != NUL;) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) { if (diff_flags & DIFF_ICASE) { char_u cbuf[MB_MAXBYTES + 1]; @@ -811,9 +805,6 @@ static int diff_write(buf_T *buf, diffin_T *din) /// @param eap can be NULL static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) { - buf_T *buf; - int idx_new; - if (dio->dio_internal) { ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); } else { @@ -833,9 +824,11 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) goto theend; } + buf_T *buf; + // :diffupdate! if (eap != NULL && eap->forceit) { - for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { + for (int idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { buf = curtab->tp_diffbuf[idx_new]; if (buf_valid(buf)) { buf_check_timestamp(buf); @@ -850,7 +843,7 @@ static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) } // Make a difference between the first buffer and every other. - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { + for (int idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { buf = curtab->tp_diffbuf[idx_new]; if (buf == NULL || buf->b_ml.ml_mfp == NULL) { continue; // skip buffer that isn't loaded @@ -893,10 +886,8 @@ int diff_internal(void) /// static int diff_internal_failed(void) { - int idx; - // Only need to do something when there is another buffer. - for (idx = 0; idx < DB_COUNT; idx++) { + for (int idx = 0; idx < DB_COUNT; idx++) { if (curtab->tp_diffbuf[idx] != NULL && curtab->tp_diffbuf[idx]->b_diff_failed) { return true; @@ -1775,9 +1766,8 @@ static void diff_copy_entry(diff_T *dprev, diff_T *dp, int idx_orig, int idx_new void diff_clear(tabpage_T *tp) FUNC_ATTR_NONNULL_ALL { - diff_T *p; diff_T *next_p; - for (p = tp->tp_first_diff; p != NULL; p = next_p) { + for (diff_T *p = tp->tp_first_diff; p != NULL; p = next_p) { next_p = p->df_next; xfree(p); } @@ -1799,12 +1789,8 @@ void diff_clear(tabpage_T *tp) /// @return diff status. int diff_check(win_T *wp, linenr_T lnum) { - int idx; // index in tp_diffbuf[] for this buffer diff_T *dp; - int maxcount; - int i; buf_T *buf = wp->w_buffer; - int cmp; if (curtab->tp_diff_invalid) { // update after a big change @@ -1821,7 +1807,7 @@ int diff_check(win_T *wp, linenr_T lnum) return 0; } - idx = diff_buf_idx(buf); + int idx = diff_buf_idx(buf); // index in tp_diffbuf[] for this buffer if (idx == DB_COUNT) { // no diffs for buffer "buf" @@ -1850,9 +1836,9 @@ int diff_check(win_T *wp, linenr_T lnum) // Changed or inserted line. If the other buffers have a count of // zero, the lines were inserted. If the other buffers have the same // count, check if the lines are identical. - cmp = false; + int cmp = false; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { if (dp->df_count[i] == 0) { zero = true; @@ -1869,7 +1855,7 @@ int diff_check(win_T *wp, linenr_T lnum) if (cmp) { // Compare all lines. If they are equal the lines were inserted // in some buffers, deleted in others, but not changed. - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] != 0)) { @@ -1898,8 +1884,8 @@ int diff_check(win_T *wp, linenr_T lnum) // Insert filler lines above the line just below the change. Will return // 0 when this buf had the max count. - maxcount = 0; - for (i = 0; i < DB_COUNT; i++) { + int maxcount = 0; + for (int i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (dp->df_count[i] > maxcount)) { maxcount = dp->df_count[i]; } @@ -2030,8 +2016,6 @@ void diff_set_topline(win_T *fromwin, win_T *towin) buf_T *frombuf = fromwin->w_buffer; linenr_T lnum = fromwin->w_topline; diff_T *dp; - int max_count; - int i; int fromidx = diff_buf_idx(frombuf); if (fromidx == DB_COUNT) { @@ -2071,9 +2055,9 @@ void diff_set_topline(win_T *fromwin, win_T *towin) if (lnum >= dp->df_lnum[fromidx]) { // Inside a change: compute filler lines. With three or more // buffers we need to know the largest count. - max_count = 0; + int max_count = 0; - for (i = 0; i < DB_COUNT; i++) { + for (int i = 0; i < DB_COUNT; i++) { if ((curtab->tp_diffbuf[i] != NULL) && (max_count < dp->df_count[i])) { max_count = dp->df_count[i]; } @@ -2421,7 +2405,6 @@ bool diff_infold(win_T *wp, linenr_T lnum) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { bool other = false; - diff_T *dp; // Return if 'diff' isn't set. if (!wp->w_p_diff) { @@ -2453,7 +2436,7 @@ bool diff_infold(win_T *wp, linenr_T lnum) return true; } - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { + for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { // If this change is below the line there can't be any further match. if (dp->df_lnum[idx] - diff_context > lnum) { break; @@ -2505,7 +2488,6 @@ void ex_diffgetput(exarg_T *eap) int count; linenr_T off = 0; diff_T *dp; - diff_T *dprev; diff_T *dfree; int i; int added; @@ -2641,7 +2623,7 @@ void ex_diffgetput(exarg_T *eap) } } - dprev = NULL; + diff_T *dprev = NULL; for (dp = curtab->tp_first_diff; dp != NULL;) { if (dp->df_lnum[idx_cur] > eap->line2 + off) { @@ -2924,13 +2906,10 @@ int diff_move_to(int dir, long count) /// "buf1" in diff mode. static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1) { - int idx1; - int idx2; - diff_T *dp; int baseline = 0; - idx1 = diff_buf_idx(buf1); - idx2 = diff_buf_idx(curbuf); + int idx1 = diff_buf_idx(buf1); + int idx2 = diff_buf_idx(curbuf); if ((idx1 == DB_COUNT) || (idx2 == DB_COUNT) @@ -2948,7 +2927,7 @@ static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1) return lnum1; } - for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { + for (diff_T *dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (dp->df_lnum[idx1] > lnum1) { return lnum1 - baseline; } @@ -3004,11 +2983,8 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) { diff_T *dp; - int idx; - int i; - linenr_T n; - idx = diff_buf_idx(curbuf); + int idx = diff_buf_idx(curbuf); if (idx == DB_COUNT) { // safety check @@ -3034,14 +3010,14 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) } // Find index for "wp". - i = diff_buf_idx(wp->w_buffer); + int i = diff_buf_idx(wp->w_buffer); if (i == DB_COUNT) { // safety check return (linenr_T)0; } - n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); + linenr_T n = lnum + (dp->df_lnum[i] - dp->df_lnum[idx]); if (n > dp->df_lnum[i] + dp->df_count[i]) { n = dp->df_lnum[i] + dp->df_count[i]; } @@ -3054,16 +3030,14 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) /// static int parse_diff_ed(char_u *line, diffhunk_T *hunk) { - char_u *p; - long f1, l1, f2, l2; - int difftype; + long l1, l2; // The line must be one of three formats: // change: {first}[,{last}]c{first}[,{last}] // append: {first}a{first}[,{last}] // delete: {first}[,{last}]d{first} - p = line; - f1 = getdigits(&p, true, 0); + char_u *p = line; + long f1 = getdigits(&p, true, 0); if (*p == ',') { p++; l1 = getdigits(&p, true, 0); @@ -3073,8 +3047,8 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) if (*p != 'a' && *p != 'c' && *p != 'd') { return FAIL; // invalid diff format } - difftype = *p++; - f2 = getdigits(&p, true, 0); + int difftype = *p++; + long f2 = getdigits(&p, true, 0); if (*p == ',') { p++; l2 = getdigits(&p, true, 0); @@ -3108,14 +3082,14 @@ static int parse_diff_ed(char_u *line, diffhunk_T *hunk) /// static int parse_diff_unified(char_u *line, diffhunk_T *hunk) { - char_u *p; - long oldline, oldcount, newline, newcount; - // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ - p = line; + char_u *p = line; if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { - oldline = getdigits(&p, true, 0); + long oldcount; + long newline; + long newcount; + long oldline = getdigits(&p, true, 0); if (*p == ',') { p++; oldcount = getdigits(&p, true, 0); diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index c1fbc9a0f9..6e5389c979 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1506,7 +1506,6 @@ char_u *get_digraph_for_char(int val_arg) /// @returns composed character, or NUL when ESC was used. int get_digraph(bool cmdline) { - int cc; no_mapping++; int c = plain_vgetc(); no_mapping--; @@ -1526,7 +1525,7 @@ int get_digraph(bool cmdline) add_to_showcmd(c); } no_mapping++; - cc = plain_vgetc(); + int cc = plain_vgetc(); no_mapping--; if (cc != ESC) { @@ -1614,17 +1613,14 @@ int getdigraph(int char1, int char2, bool meta_char) /// @param str void putdigraph(char_u *str) { - char_u char1, char2; - digr_T *dp; - while (*str != NUL) { str = skipwhite(str); if (*str == NUL) { return; } - char1 = *str++; - char2 = *str++; + char_u char1 = *str++; + char_u char2 = *str++; if (char2 == 0) { emsg(_(e_invarg)); @@ -1644,7 +1640,7 @@ void putdigraph(char_u *str) int n = getdigits_int(&str, true, 0); // If the digraph already exists, replace the result. - dp = (digr_T *)user_digraphs.ga_data; + digr_T *dp = (digr_T *)user_digraphs.ga_data; int i; for (i = 0; i < user_digraphs.ga_len; i++) { @@ -1677,12 +1673,11 @@ static void digraph_header(const char *msg) void listdigraphs(bool use_headers) { - digr_T *dp; result_T previous = 0; msg_putchar('\n'); - dp = digraphdefault; + digr_T *dp = digraphdefault; for (int i = 0; dp->char1 != NUL && !got_int; i++) { digr_T tmp; @@ -1749,11 +1744,7 @@ static void printdigraph(const digr_T *dp, result_T *previous) FUNC_ATTR_NONNULL_ARG(1) { char_u buf[30]; - char_u *p; - - int list_width; - - list_width = 13; + int list_width = 13; if (dp->result != 0) { if (previous != NULL) { @@ -1780,7 +1771,7 @@ static void printdigraph(const digr_T *dp, result_T *previous) } } - p = &buf[0]; + char_u *p = &buf[0]; *p++ = dp->char1; *p++ = dp->char2; *p++ = ' '; @@ -1863,8 +1854,6 @@ char *keymap_init(void) /// @param eap void ex_loadkeymap(exarg_T *eap) { - char_u *line; - char_u *p; char_u *s; #define KMAP_LLEN 200 // max length of "to" and "from" together @@ -1887,13 +1876,13 @@ void ex_loadkeymap(exarg_T *eap) // Get each line of the sourced file, break at the end. for (;;) { - line = eap->getline(0, eap->cookie, 0, true); + char_u *line = eap->getline(0, eap->cookie, 0, true); if (line == NULL) { break; } - p = skipwhite(line); + char_u *p = skipwhite(line); if ((*p != '"') && (*p != NUL)) { kmap_T *kp = GA_APPEND_VIA_PTR(kmap_T, &curbuf->b_kmap_ga); @@ -1946,7 +1935,6 @@ static void keymap_unload(void) { char_u buf[KMAP_MAXLEN + 10]; char_u *save_cpo = p_cpo; - kmap_T *kp; if (!(curbuf->b_kmap_state & KEYMAP_LOADED)) { return; @@ -1956,7 +1944,7 @@ static void keymap_unload(void) p_cpo = (char_u *)"C"; // clear the ":lmap"s - kp = (kmap_T *)curbuf->b_kmap_ga.ga_data; + kmap_T *kp = (kmap_T *)curbuf->b_kmap_ga.ga_data; for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf((char *)buf, sizeof(buf), " %s", kp[i].from); -- cgit From 4d6863554b59f6edeffbb4e8bcd9f4d13bb08e63 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(eval/funcs): convert function comments to doxygen format --- src/nvim/eval/funcs.c | 1025 +++++++++++++++---------------------------------- 1 file changed, 314 insertions(+), 711 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c8abbff933..b249dffe11 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -175,7 +175,7 @@ char_u *get_expr_name(expand_T *xp, int idx) /// /// @param[in] name Name of the function. /// -/// Returns pointer to the function definition or NULL if not found. +/// @return pointer to the function definition or NULL if not found. const VimLFuncDef *find_internal_func(const char *const name) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL { @@ -229,9 +229,7 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T return ERROR_NONE; } -/* - * Return TRUE for a non-zero Number and a non-empty String. - */ +/// @return TRUE for a non-zero Number and a non-empty String. static int non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER @@ -243,11 +241,11 @@ static int non_zero_arg(typval_T *argvars) && *argvars[0].vval.v_string != NUL)); } -// Apply a floating point C function on a typval with one float_T. -// -// Some versions of glibc on i386 have an optimization that makes it harder to -// call math functions indirectly from inside an inlined function, causing -// compile-time errors. Avoid `inline` in that case. #3072 +/// Apply a floating point C function on a typval with one float_T. +/// +/// Some versions of glibc on i386 have an optimization that makes it harder to +/// call math functions indirectly from inside an inlined function, causing +/// compile-time errors. Avoid `inline` in that case. #3072 static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; @@ -293,9 +291,7 @@ end: api_clear_error(&err); } -/* - * "abs(expr)" function - */ +/// "abs(expr)" function static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type == VAR_FLOAT) { @@ -315,9 +311,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "add(list, item)" function - */ +/// "add(list, item)" function static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 1; // Default: failed. @@ -345,9 +339,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "and(expr, expr)" function - */ +/// "and(expr, expr)" function static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) @@ -363,7 +355,7 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) api_free_dictionary(metadata); } -// "append(lnum, string/list)" function +/// "append(lnum, string/list)" function static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const linenr_T lnum = tv_get_lnum(&argvars[0]); @@ -371,7 +363,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_buffer_lines(curbuf, lnum, true, &argvars[1], rettv); } -// "appendbufline(buf, lnum, string/list)" function +/// "appendbufline(buf, lnum, string/list)" function static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *const buf = tv_get_buf(&argvars[0], false); @@ -403,9 +395,7 @@ static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "argidx()" function - */ +/// "argidx()" function static void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = curwin->w_arg_idx; @@ -421,9 +411,7 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "argv(nr)" function - */ +/// "argv(nr)" function static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { aentry_T *arglist = NULL; @@ -458,31 +446,31 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "assert_beeps(cmd [, error])" function +/// "assert_beeps(cmd [, error])" function static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_beeps(argvars, false); } -// "assert_nobeep(cmd [, error])" function +/// "assert_nobeep(cmd [, error])" function static void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_beeps(argvars, true); } -// "assert_equal(expected, actual[, msg])" function +/// "assert_equal(expected, actual[, msg])" function static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL); } -// "assert_equalfile(fname-one, fname-two[, msg])" function +/// "assert_equalfile(fname-one, fname-two[, msg])" function static void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_equalfile(argvars); } -// "assert_notequal(expected, actual[, msg])" function +/// "assert_notequal(expected, actual[, msg])" function static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL); @@ -536,15 +524,13 @@ static void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); } -// "assert_true(actual[, msg])" function +/// "assert_true(actual[, msg])" function static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = assert_bool(argvars, true); } -/* - * "atan2()" function - */ +/// "atan2()" function static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T fx; @@ -558,27 +544,21 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "browse(save, title, initdir, default)" function - */ +/// "browse(save, title, initdir, default)" function static void f_browse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; } -/* - * "browsedir(title, initdir)" function - */ +/// "browsedir(title, initdir)" function static void f_browsedir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { f_browse(argvars, rettv, NULL); } -/* - * Find a buffer by number or exact name. - */ +/// Find a buffer by number or exact name. static buf_T *find_buffer(typval_T *avar) { buf_T *buf = NULL; @@ -605,7 +585,7 @@ static buf_T *find_buffer(typval_T *avar) return buf; } -// "bufadd(expr)" function +/// "bufadd(expr)" function static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *name = (char_u *)tv_get_string(&argvars[0]); @@ -613,17 +593,13 @@ static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = buflist_add(*name == NUL ? NULL : name, 0); } -/* - * "bufexists(expr)" function - */ +/// "bufexists(expr)" function static void f_bufexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL); } -/* - * "buflisted(expr)" function - */ +/// "buflisted(expr)" function static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *buf; @@ -632,7 +608,7 @@ static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (buf != NULL && buf->b_p_bl); } -// "bufload(expr)" function +/// "bufload(expr)" function static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr) { buf_T *buf = get_buf_arg(&argvars[0]); @@ -647,9 +623,7 @@ static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr) } } -/* - * "bufloaded(expr)" function - */ +/// "bufloaded(expr)" function static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *buf; @@ -658,9 +632,7 @@ static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (buf != NULL && buf->b_ml.ml_mfp != NULL); } -/* - * "bufname(expr)" function - */ +/// "bufname(expr)" function static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const buf_T *buf; @@ -676,9 +648,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "bufnr(expr)" function - */ +/// "bufnr(expr)" function static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const buf_T *buf; @@ -750,9 +720,7 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_win_common(argvars, rettv, true); } -/* - * Get buffer by number or pattern. - */ +/// Get buffer by number or pattern. buf_T *tv_get_buf(typval_T *tv, int curtab_only) { char_u *name = tv->vval.v_string; @@ -820,9 +788,7 @@ buf_T *get_buf_arg(typval_T *arg) return buf; } -/* - * "byte2line(byte)" function - */ +/// "byte2line(byte)" function static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long boff = tv_get_number(&argvars[0]) - 1; @@ -857,17 +823,13 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) rettv->vval.v_number = (varnumber_T)(t - str); } -/* - * "byteidx()" function - */ +/// "byteidx()" function static void f_byteidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { byteidx(argvars, rettv, FALSE); } -/* - * "byteidxcomp()" function - */ +/// "byteidxcomp()" function static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr) { byteidx(argvars, rettv, TRUE); @@ -919,15 +881,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "changenr()" function - */ +/// "changenr()" function static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = curbuf->b_u_seq_cur; } -// "chanclose(id[, stream])" function +/// "chanclose(id[, stream])" function static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -966,7 +926,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "chansend(id, data)" function +/// "chansend(id, data)" function static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -1007,9 +967,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "char2nr(string)" function - */ +/// "char2nr(string)" function static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[1].v_type != VAR_UNKNOWN) { @@ -1021,9 +979,10 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = utf_ptr2char((const char_u *)tv_get_string(&argvars[0])); } -/// Get the current cursor column and store it in 'rettv'. If 'charcol' is true, -/// returns the character index of the column. Otherwise, returns the byte index -/// of the column. +/// Get the current cursor column and store it in 'rettv'. +/// +/// @return the character index of the column if 'charcol' is true, +/// otherwise the byte index of the column. static void get_col(typval_T *argvars, typval_T *rettv, bool charcol) { colnr_T col = 0; @@ -1064,7 +1023,7 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_col(argvars, rettv, true); } -// "charidx()" function +/// "charidx()" function static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; @@ -1110,7 +1069,7 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = len > 0 ? len - 1 : 0; } -// "chdir(dir)" function +/// "chdir(dir)" function static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *cwd; @@ -1147,9 +1106,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "cindent(lnum)" function - */ +/// "cindent(lnum)" function static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; @@ -1180,9 +1137,7 @@ static win_T *get_optional_window(typval_T *argvars, int idx) return win; } -/* - * "clearmatches()" function - */ +/// "clearmatches()" function static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *win = get_optional_window(argvars, 0); @@ -1198,9 +1153,7 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_col(argvars, rettv, false); } -/* - * "complete()" function - */ +/// "complete()" function static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if ((State & INSERT) == 0) { @@ -1227,17 +1180,13 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_completion(startcol - 1, argvars[1].vval.v_list); } -/* - * "complete_add()" function - */ +/// "complete_add()" function static void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false); } -/* - * "complete_check()" function - */ +/// "complete_check()" function static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int saved = RedrawingDisabled; @@ -1248,7 +1197,7 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) RedrawingDisabled = saved; } -// "complete_info()" function +/// "complete_info()" function static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); @@ -1265,9 +1214,7 @@ static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_complete_info(what_list, rettv->vval.v_dict); } -/* - * "confirm(message, buttons[, default [, type]])" function - */ +/// "confirm(message, buttons[, default [, type]])" function static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf[NUMBUFLEN]; @@ -1322,17 +1269,13 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "copy()" function - */ +/// "copy()" function static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { var_item_copy(NULL, &argvars[0], rettv, false, 0); } -/* - * "count()" function - */ +/// "count()" function static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long n = 0; @@ -1423,11 +1366,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/* - * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function - * - * Checks the existence of a cscope connection. - */ +/// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function +/// +/// Checks the existence of a cscope connection. static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; @@ -1622,13 +1563,14 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) /// "cursor(list)" /// /// Moves the cursor to the specified line and column. -/// Returns 0 when the position could be set, -1 otherwise. +/// +/// @return 0 when the position could be set, -1 otherwise. static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { set_cursorpos(argvars, rettv, false); } -// "debugbreak()" function +/// "debugbreak()" function static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int pid; @@ -1652,7 +1594,7 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "deepcopy()" function +/// "deepcopy()" function static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int noref = 0; @@ -1669,7 +1611,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "delete()" function +/// "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; @@ -1705,7 +1647,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// dictwatcheradd(dict, key, funcref) function +/// dictwatcheradd(dict, key, funcref) function static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (check_secure()) { @@ -1743,7 +1685,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) callback); } -// dictwatcherdel(dict, key, funcref) function +/// dictwatcherdel(dict, key, funcref) function static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (check_secure()) { @@ -1852,25 +1794,19 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "did_filetype()" function - */ +/// "did_filetype()" function static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = did_filetype; } -/* - * "diff_filler()" function - */ +/// "diff_filler()" function static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars))); } -/* - * "diff_hlID()" function - */ +/// "diff_hlID()" function static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(argvars); @@ -1922,9 +1858,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1); } -/* - * "empty({expr})" function - */ +/// "empty({expr})" function static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool n = true; @@ -2021,9 +1955,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_free_fullenv(env); } -/* - * "escape({string}, {chars})" function - */ +/// "escape({string}, {chars})" function static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf[NUMBUFLEN]; @@ -2047,9 +1979,7 @@ static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; } -/* - * "eval()" function - */ +/// "eval()" function static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *s = tv_get_string_chk(&argvars[0]); @@ -2070,17 +2000,13 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "eventhandler()" function - */ +/// "eventhandler()" function static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = vgetc_busy; } -/* - * "executable()" function - */ +/// "executable()" function static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (tv_check_for_string(&argvars[0]) == FAIL) { @@ -2188,13 +2114,13 @@ static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int capture_ga = save_capture_ga; } -// "execute(command)" function +/// "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { execute_common(argvars, rettv, fptr, 0); } -// "win_execute(win_id, command)" function +/// "win_execute(win_id, command)" function static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // Return an empty string if something fails. @@ -2224,9 +2150,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)path; } -/* - * "exists()" function - */ +/// "exists()" function static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n = false; @@ -2266,9 +2190,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/* - * "expand()" function - */ +/// "expand()" function static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { size_t len; @@ -2353,8 +2275,8 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); } -// "expandcmd()" function -// Expand all the special characters in a command string. +/// "expandcmd()" function +/// Expand all the special characters in a command string. static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char *errormsg = NULL; @@ -2414,10 +2336,8 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "extend(list, list [, idx])" function - * "extend(dict, dict [, action])" function - */ +/// "extend(list, list [, idx])" function +/// "extend(dict, dict [, action])" function static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const arg_errmsg = N_("extend() argument"); @@ -2494,9 +2414,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "feedkeys()" function - */ +/// "feedkeys()" function static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // This is not allowed in the sandbox. If the commands would still be @@ -2525,10 +2443,9 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); } -/* - * Return 0 for not writable, 1 for writable file, 2 for a dir which we have - * rights to write into. - */ +/// @return 0 for not writable +/// 1 for writable file +/// 2 for a dir which we have rights to write into. static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *filename = tv_get_string(&argvars[0]); @@ -2595,33 +2512,25 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } -/* - * "filter()" function - */ +/// "filter()" function static void f_filter(typval_T *argvars, typval_T *rettv, FunPtr fptr) { filter_map(argvars, rettv, FALSE); } -/* - * "finddir({fname}[, {path}[, {count}]])" function - */ +/// "finddir({fname}[, {path}[, {count}]])" function static void f_finddir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { findfilendir(argvars, rettv, FINDFILE_DIR); } -/* - * "findfile({fname}[, {path}[, {count}]])" function - */ +/// "findfile({fname}[, {path}[, {count}]])" function static void f_findfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { findfilendir(argvars, rettv, FINDFILE_FILE); } -/* - * "float2nr({float})" function - */ +/// "float2nr({float})" function static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; @@ -2637,9 +2546,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "fmod()" function - */ +/// "fmod()" function static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T fx; @@ -2653,18 +2560,14 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "fnameescape({string})" function - */ +/// "fnameescape({string})" function static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = (char_u *)vim_strsave_fnameescape(tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } -/* - * "fnamemodify({fname}, {mods})" function - */ +/// "fnamemodify({fname}, {mods})" function static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *fbuf = NULL; @@ -2693,9 +2596,7 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * "foldclosed()" function - */ +/// "foldclosed()" function static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) { const linenr_T lnum = tv_get_lnum(argvars); @@ -2714,25 +2615,19 @@ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) rettv->vval.v_number = -1; } -/* - * "foldclosed()" function - */ +/// "foldclosed()" function static void f_foldclosed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { foldclosed_both(argvars, rettv, FALSE); } -/* - * "foldclosedend()" function - */ +/// "foldclosedend()" function static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { foldclosed_both(argvars, rettv, TRUE); } -/* - * "foldlevel()" function - */ +/// "foldlevel()" function static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const linenr_T lnum = tv_get_lnum(argvars); @@ -2741,9 +2636,7 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "foldtext()" function - */ +/// "foldtext()" function static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T foldstart; @@ -2796,9 +2689,7 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "foldtextresult(lnum)" function - */ +/// "foldtextresult(lnum)" function static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *text; @@ -2829,9 +2720,7 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) entered = false; } -/* - * "foreground()" function - */ +/// "foreground()" function static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr) { } @@ -2858,9 +2747,7 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "get()" function - */ +/// "get()" function static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) { listitem_T *li; @@ -3020,12 +2907,12 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * Get line or list of lines from buffer "buf" into "rettv". - * Return a range (from start to end) of lines in rettv from the specified - * buffer. - * If 'retlist' is TRUE, then the lines are returned as a Vim List. - */ +/// Get line or list of lines from buffer "buf" into "rettv". +/// +/// @param retlist if TRUE, then the lines are returned as a Vim List. +/// +/// @return range (from start to end) of lines in rettv from the specified +/// buffer. static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) { rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); @@ -3058,9 +2945,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli } } -/* - * "getbufline()" function - */ +/// "getbufline()" function static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *const buf = tv_get_buf_from_arg(&argvars[0]); @@ -3073,9 +2958,7 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buffer_lines(buf, lnum, end, true, rettv); } -/* - * "getbufvar()" function - */ +/// "getbufvar()" function static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool done = false; @@ -3135,7 +3018,7 @@ f_getbufvar_end: } } -// "getchangelist()" function +/// "getchangelist()" function static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, 2); @@ -3175,7 +3058,7 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "getchar()" and "getcharstr()" functions +/// "getchar()" and "getcharstr()" functions static void getchar_common(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL { @@ -3277,13 +3160,13 @@ static void getchar_common(typval_T *argvars, typval_T *rettv) } } -// "getchar()" function +/// "getchar()" function static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { getchar_common(argvars, rettv); } -// "getcharstr()" function +/// "getcharstr()" function static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { getchar_common(argvars, rettv); @@ -3303,9 +3186,7 @@ static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getcharmod()" function - */ +/// "getcharmod()" function static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = mod_mask; @@ -3372,9 +3253,7 @@ static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) getpos_both(argvars, rettv, false, true); } -/* - * "getcharsearch()" function - */ +/// "getcharsearch()" function static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); @@ -3386,26 +3265,20 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } -/* - * "getcmdline()" function - */ +/// "getcmdline()" function static void f_getcmdline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_cmdline_str(); } -/* - * "getcmdpos()" function - */ +/// "getcmdpos()" function static void f_getcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = get_cmdline_pos() + 1; } -/* - * "getcmdtype()" function - */ +/// "getcmdtype()" function static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -3413,9 +3286,7 @@ static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string[0] = get_cmdline_type(); } -/* - * "getcmdwintype()" function - */ +/// "getcmdwintype()" function static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -3424,7 +3295,7 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string[0] = cmdwin_type; } -// "getcompletion()" function +/// "getcompletion()" function static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *pat; @@ -3623,18 +3494,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(cwd); } -/* - * "getfontname()" function - */ +/// "getfontname()" function static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; } -/* - * "getfperm({fname})" function - */ +/// "getfperm({fname})" function static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char *perm = NULL; @@ -3654,9 +3521,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)perm; } -/* - * "getfsize({fname})" function - */ +/// "getfsize({fname})" function static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *fname = tv_get_string(&argvars[0]); @@ -3681,9 +3546,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getftime({fname})" function - */ +/// "getftime({fname})" function static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *fname = tv_get_string(&argvars[0]); @@ -3696,9 +3559,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getftype({fname})" function - */ +/// "getftype({fname})" function static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *type = NULL; @@ -3732,7 +3593,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = type; } -// "getjumplist()" function +/// "getjumplist()" function static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, kListLenMayKnow); @@ -3763,9 +3624,7 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "getline(lnum, [end])" function - */ +/// "getline(lnum, [end])" function static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T end; @@ -3809,9 +3668,7 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buf_local_marks(buf, rettv->vval.v_list); } -/* - * "getmatches()" function - */ +/// "getmatches()" function static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { matchitem_T *cur; @@ -3866,7 +3723,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "getmousepos()" function +/// "getmousepos()" function static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; @@ -3919,9 +3776,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(d, S_LEN("column"), column); } -/* - * "getpid()" function - */ +/// "getpid()" function static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = os_get_pid(); @@ -4058,9 +3913,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "gettabvar()" function - */ +/// "gettabvar()" function static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool done = false; @@ -4098,15 +3951,13 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "gettabwinvar()" function - */ +/// "gettabwinvar()" function static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { getwinvar(argvars, rettv, 1); } -// "gettagstack()" function +/// "gettagstack()" function static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wp = curwin; // default is current window @@ -4158,12 +4009,12 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// Dummy timer callback. Used by f_wait(). +/// Dummy timer callback. Used by f_wait(). static void dummy_timer_due_cb(TimeWatcher *tw, void *data) { } -// Dummy timer close callback. Used by f_wait(). +/// Dummy timer close callback. Used by f_wait(). static void dummy_timer_close_cb(TimeWatcher *tw, void *data) { xfree(tw); @@ -4226,7 +4077,7 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr) time_watcher_close(tw, dummy_timer_close_cb); } -// "win_screenpos()" function +/// "win_screenpos()" function static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, 2); @@ -4235,9 +4086,7 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); } -// -// Move the window wp into a new split of targetwin in a given direction -// +/// Move the window wp into a new split of targetwin in a given direction static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) { int dir; @@ -4275,7 +4124,7 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags } } -// "win_splitmove()" function +/// "win_splitmove()" function static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wp; @@ -4315,7 +4164,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) win_move_into_split(wp, targetwin, size, flags); } -// "getwinpos({timeout})" function +/// "getwinpos({timeout})" function static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, 2); @@ -4323,17 +4172,13 @@ static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(rettv->vval.v_list, -1); } -/* - * "getwinposx()" function - */ +/// "getwinposx()" function static void f_getwinposx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; } -/* - * "getwinposy()" function - */ +/// "getwinposy()" function static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; @@ -4345,9 +4190,7 @@ static void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) getwinvar(argvars, rettv, 0); } -/* - * "glob()" function - */ +/// "glob()" function static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int options = WILD_SILENT|WILD_USE_NL; @@ -4445,7 +4288,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "glob2regpat()" function +/// "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error @@ -4781,9 +4624,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "hasmapto()" function - */ +/// "hasmapto()" function static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *mode; @@ -4806,9 +4647,7 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "histadd()" function - */ +/// "histadd()" function static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType histype; @@ -4831,9 +4670,7 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "histdel()" function - */ +/// "histdel()" function static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; @@ -4856,9 +4693,7 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/* - * "histget()" function - */ +/// "histget()" function static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType type; @@ -4880,9 +4715,7 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; } -/* - * "histnr()" function - */ +/// "histnr()" function static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const history = tv_get_string_chk(&argvars[0]); @@ -4895,25 +4728,19 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = i; } -/* - * "highlightID(name)" function - */ +/// "highlightID(name)" function static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0])); } -/* - * "highlight_exists()" function - */ +/// "highlight_exists()" function static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0])); } -/* - * "hostname()" function - */ +/// "hostname()" function static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char hostname[256]; @@ -4923,9 +4750,7 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave((char_u *)hostname); } -/* - * iconv() function - */ +/// iconv() function static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { vimconv_T vimconv; @@ -4953,9 +4778,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(to); } -/* - * "indent()" function - */ +/// "indent()" function static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const linenr_T lnum = tv_get_lnum(argvars); @@ -4966,9 +4789,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "index()" function - */ +/// "index()" function static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long idx = 0; @@ -5042,26 +4863,20 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) static bool inputsecret_flag = false; -/* - * "input()" function - * Also handles inputsecret() when inputsecret is set. - */ +/// "input()" function +/// Also handles inputsecret() when inputsecret is set. static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr) { get_user_input(argvars, rettv, FALSE, inputsecret_flag); } -/* - * "inputdialog()" function - */ +/// "inputdialog()" function static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) { get_user_input(argvars, rettv, TRUE, inputsecret_flag); } -/* - * "inputlist()" function - */ +/// "inputlist()" function static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int selected; @@ -5127,9 +4942,7 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) inputsecret_flag = false; } -/* - * "insert()" function - */ +/// "insert()" function static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { list_T *l; @@ -5201,32 +5014,26 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "interrupt()" function +/// "interrupt()" function static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED, FunPtr fptr FUNC_ATTR_UNUSED) { got_int = true; } -/* - * "invert(expr)" function - */ +/// "invert(expr)" function static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } -/* - * "isdirectory()" function - */ +/// "isdirectory()" function static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); } -/* - * "islocked()" function - */ +/// "islocked()" function static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; @@ -5269,7 +5076,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_lval(&lv); } -// "isinf()" function +/// "isinf()" function static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type == VAR_FLOAT @@ -5278,7 +5085,7 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "isnan()" function +/// "isnan()" function static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT @@ -5296,15 +5103,13 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) dummy_ap, argvars); } -/* - * "items(dict)" function - */ +/// "items(dict)" function static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_list(argvars, rettv, 2); } -// "jobpid(id)" function +/// "jobpid(id)" function static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -5328,7 +5133,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = proc->pid; } -// "jobresize(job, width, height)" function +/// "jobresize(job, width, height)" function static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -5475,7 +5280,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en return env; } -// "jobstart()" function +/// "jobstart()" function static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -5596,7 +5401,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "jobstop()" function +/// "jobstop()" function static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -5629,7 +5434,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "jobwait(ids[, timeout])" function +/// "jobwait(ids[, timeout])" function static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -5728,9 +5533,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = rv; } -/* - * "join()" function - */ +/// "join()" function static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type != VAR_LIST) { @@ -5795,17 +5598,13 @@ static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)encode_tv2json(&argvars[0], NULL); } -/* - * "keys()" function - */ +/// "keys()" function static void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_list(argvars, rettv, 0); } -/* - * "last_buffer_nr()" function. - */ +/// "last_buffer_nr()" function. static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n = 0; @@ -5819,9 +5618,7 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/* - * "len()" function - */ +/// "len()" function static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { @@ -5894,23 +5691,19 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) } } -/* - * "libcall()" function - */ +/// "libcall()" function static void f_libcall(typval_T *argvars, typval_T *rettv, FunPtr fptr) { libcall_common(argvars, rettv, VAR_STRING); } -/* - * "libcallnr()" function - */ +/// "libcallnr()" function static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { libcall_common(argvars, rettv, VAR_NUMBER); } -// "line(string, [winid])" function +/// "line(string, [winid])" function static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = 0; @@ -5941,9 +5734,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = lnum; } -/* - * "line2byte(lnum)" function - */ +/// "line2byte(lnum)" function static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const linenr_T lnum = tv_get_lnum(argvars); @@ -5957,9 +5748,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "lispindent(lnum)" function - */ +/// "lispindent(lnum)" function static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const pos_T pos = curwin->w_cursor; @@ -5973,7 +5762,7 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "list2str()" function +/// "list2str()" function static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; @@ -6002,9 +5791,7 @@ static void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ga.ga_data; } -/* - * "localtime()" function - */ +/// "localtime()" function static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = (varnumber_T)time(NULL); @@ -6090,25 +5877,19 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv); } -/* - * "map()" function - */ +/// "map()" function static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr) { filter_map(argvars, rettv, TRUE); } -/* - * "maparg()" function - */ +/// "maparg()" function static void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { get_maparg(argvars, rettv, TRUE); } -/* - * "mapcheck()" function - */ +/// "mapcheck()" function static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) { get_maparg(argvars, rettv, FALSE); @@ -6330,17 +6111,13 @@ theend: p_cpo = save_cpo; } -/* - * "match()" function - */ +/// "match()" function static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) { find_some_match(argvars, rettv, kSomeMatch); } -/* - * "matchadd()" function - */ +/// "matchadd()" function static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char grpbuf[NUMBUFLEN]; @@ -6432,9 +6209,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); } -/* - * "matcharg()" function - */ +/// "matcharg()" function static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int id = tv_get_number(&argvars[0]); @@ -6457,9 +6232,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "matchdelete()" function - */ +/// "matchdelete()" function static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *win = get_optional_window(argvars, 1); @@ -6471,25 +6244,19 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "matchend()" function - */ +/// "matchend()" function static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { find_some_match(argvars, rettv, kSomeMatchEnd); } -/* - * "matchlist()" function - */ +/// "matchlist()" function static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { find_some_match(argvars, rettv, kSomeMatchList); } -/* - * "matchstr()" function - */ +/// "matchstr()" function static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { find_some_match(argvars, rettv, kSomeMatchStr); @@ -6550,25 +6317,19 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool rettv->vval.v_number = n; } -/* - * "max()" function - */ +/// "max()" function static void f_max(typval_T *argvars, typval_T *rettv, FunPtr fptr) { max_min(argvars, rettv, TRUE); } -/* - * "min()" function - */ +/// "min()" function static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) { max_min(argvars, rettv, FALSE); } -/* - * "mkdir()" function - */ +/// "mkdir()" function static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int prot = 0755; // -V536 @@ -6783,9 +6544,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "nextnonblank()" function - */ +/// "nextnonblank()" function static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum; @@ -6802,9 +6561,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = lnum; } -/* - * "nr2char()" function - */ +/// "nr2char()" function static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[1].v_type != VAR_UNKNOWN) { @@ -6835,18 +6592,14 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = xmemdupz(buf, (size_t)len); } -/* - * "or(expr, expr)" function - */ +/// "or(expr, expr)" function static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) | tv_get_number_chk(&argvars[1], NULL); } -/* - * "pathshorten()" function - */ +/// "pathshorten()" function static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int trim_len = 1; @@ -6868,9 +6621,7 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "pow()" function - */ +/// "pow()" function static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T fx; @@ -6884,9 +6635,7 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "prevnonblank()" function - */ +/// "prevnonblank()" function static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(argvars); @@ -6900,9 +6649,7 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = lnum; } -/* - * "printf()" function - */ +/// "printf()" function static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -6925,7 +6672,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "prompt_setcallback({buffer}, {callback})" function +/// "prompt_setcallback({buffer}, {callback})" function static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *buf; @@ -6949,7 +6696,7 @@ static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr buf->b_prompt_callback = prompt_callback; } -// "prompt_setinterrupt({buffer}, {callback})" function +/// "prompt_setinterrupt({buffer}, {callback})" function static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *buf; @@ -6993,7 +6740,7 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave(buf_prompt_text(buf)); } -// "prompt_setprompt({buffer}, {text})" function +/// "prompt_setprompt({buffer}, {text})" function static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) { buf_T *buf; @@ -7012,16 +6759,14 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf->b_prompt_text = vim_strsave(text); } -// "pum_getpos()" function +/// "pum_getpos()" function static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); pum_set_event_info(rettv->vval.v_dict); } -/* - * "pumvisible()" function - */ +/// "pumvisible()" function static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (pum_visible()) { @@ -7199,15 +6944,13 @@ static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr) script_host_eval("perl", argvars, rettv); } -// "rubyeval()" function +/// "rubyeval()" function static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { script_host_eval("ruby", argvars, rettv); } -/* - * "range()" function - */ +/// "range()" function static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T start; @@ -7242,7 +6985,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// Evaluate "expr" for readdir(). +/// Evaluate "expr" for readdir(). static varnumber_T readdir_checkitem(typval_T *expr, const char *name) { typval_T save_val; @@ -7273,7 +7016,7 @@ theend: return retval; } -// "readdir()" function +/// "readdir()" function static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { typval_T *expr; @@ -7329,9 +7072,7 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_clear_strings(&ga); } -/* - * "readfile()" function - */ +/// "readfile()" function static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool binary = false; @@ -7565,13 +7306,13 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "reg_executing()" function +/// "reg_executing()" function static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr) { return_register(reg_executing, rettv); } -// "reg_recording()" function +/// "reg_recording()" function static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr) { return_register(reg_recording, rettv); @@ -7673,9 +7414,7 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "remove()" function - */ +/// "remove()" function static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { list_T *l; @@ -7809,9 +7548,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "rename({from}, {to})" function - */ +/// "rename({from}, {to})" function static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (check_secure()) { @@ -7823,9 +7560,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "repeat()" function - */ +/// "repeat()" function static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n = tv_get_number(&argvars[1]); @@ -7862,9 +7597,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "resolve()" function - */ +/// "resolve()" function static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -8033,9 +7766,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) simplify_filename(rettv->vval.v_string); } -/* - * "reverse({list})" function - */ +/// "reverse({list})" function static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type == VAR_BLOB) { @@ -8165,11 +7896,10 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) #define SP_END 0x40 ///< leave cursor at end of match #define SP_COLUMN 0x80 ///< start at cursor column -/* - * Get flags for a search function. - * Possibly sets "p_ws". - * Returns BACKWARD, FORWARD or zero (for an error). - */ +/// Get flags for a search function. +/// Possibly sets "p_ws". +/// +/// @return BACKWARD, FORWARD or zero (for an error). static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; @@ -8227,7 +7957,7 @@ static int get_search_arg(typval_T *varp, int *flagsp) return dir; } -// Shared by search() and searchpos() functions. +/// Shared by search() and searchpos() functions. static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; @@ -8363,7 +8093,7 @@ theend: return retval; } -// "rpcnotify()" function +/// "rpcnotify()" function static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -8398,7 +8128,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } -// "rpcrequest()" function +/// "rpcrequest()" function static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -8496,7 +8226,7 @@ end: api_clear_error(&err); } -// "rpcstart()" function (DEPRECATED) +/// "rpcstart()" function (DEPRECATED) static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -8563,7 +8293,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "rpcstop()" function +/// "rpcstop()" function static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_NUMBER; @@ -8593,7 +8323,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "screenattr()" function +/// "screenattr()" function static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int c; @@ -8611,7 +8341,7 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = c; } -// "screenchar()" function +/// "screenchar()" function static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int c; @@ -8629,7 +8359,7 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = c; } -// "screenchars()" function +/// "screenchars()" function static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int row = tv_get_number_chk(&argvars[0], NULL) - 1; @@ -8654,9 +8384,9 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "screencol()" function -// -// First column is 1 to be consistent with virtcol(). +/// "screencol()" function +/// +/// First column is 1 to be consistent with virtcol(). static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ui_current_col() + 1; @@ -8688,13 +8418,13 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("endcol"), ecol); } -// "screenrow()" function +/// "screenrow()" function static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = ui_current_row() + 1; } -// "screenstring()" function +/// "screenstring()" function static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = NULL; @@ -8710,7 +8440,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strsave(grid->chars[grid->line_offset[row] + col]); } -// "search()" function +/// "search()" function static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; @@ -8718,9 +8448,7 @@ static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = search_cmn(argvars, NULL, &flags); } -/* - * "searchdecl()" function - */ +/// "searchdecl()" function static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int locally = 1; @@ -8742,9 +8470,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * Used by searchpair() and searchpairpos() - */ +/// Used by searchpair() and searchpairpos() static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) { bool save_p_ws = p_ws; @@ -8818,17 +8544,13 @@ theend: return retval; } -/* - * "searchpair()" function - */ +/// "searchpair()" function static void f_searchpair(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = searchpair_cmn(argvars, NULL); } -/* - * "searchpairpos()" function - */ +/// "searchpairpos()" function static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T match_pos; @@ -9014,9 +8736,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir return retval; } -/* - * "searchpos()" function - */ +/// "searchpos()" function static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T match_pos; @@ -9130,9 +8850,7 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "setbufvar()" function - */ +/// "setbufvar()" function static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (check_secure() @@ -9249,9 +8967,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "setcmdpos()" function - */ +/// "setcmdpos()" function static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int pos = (int)tv_get_number(&argvars[0]) - 1; @@ -9313,9 +9029,7 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = os_setperm(fname, mode) == OK; } -/* - * "setline()" function - */ +/// "setline()" function static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum = tv_get_lnum(&argvars[0]); @@ -9403,9 +9117,7 @@ skip_args: recursive--; } -/* - * "setloclist()" function - */ +/// "setloclist()" function static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *win; @@ -9418,9 +9130,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "setmatches()" function - */ +/// "setmatches()" function static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; @@ -9529,9 +9239,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_position(argvars, rettv, false); } -/* - * "setqflist()" function - */ +/// "setqflist()" function static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { set_qf_ll_list(NULL, argvars, rettv); @@ -9567,9 +9275,7 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c return OK; } -/* - * "setreg()" function - */ +/// "setreg()" function static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool append = false; @@ -9713,9 +9419,7 @@ free_lstval: } } -/* - * "settabvar()" function - */ +/// "settabvar()" function static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; @@ -9746,15 +9450,13 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "settabwinvar()" function - */ +/// "settabwinvar()" function static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { setwinvar(argvars, rettv, 1); } -// "settagstack()" function +/// "settagstack()" function static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { static char *e_invact2 = N_("E962: Invalid action: '%s'"); @@ -9807,9 +9509,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "setwinvar()" function - */ +/// "setwinvar()" function static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { setwinvar(argvars, rettv, 0); @@ -9826,9 +9526,7 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; } -/* - * "shellescape({string})" function - */ +/// "shellescape({string})" function static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const bool do_special = non_zero_arg(&argvars[1]); @@ -9839,9 +9537,7 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; } -/* - * shiftwidth() function - */ +/// shiftwidth() function static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; @@ -10125,9 +9821,7 @@ static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) }); } -/* - * "simplify()" function - */ +/// "simplify()" function static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const p = tv_get_string(&argvars[0]); @@ -10204,9 +9898,7 @@ static sortinfo_T *sortinfo = NULL; #define ITEM_COMPARE_FAIL 999 -/* - * Compare functions for f_sort() and f_uniq() below. - */ +/// Compare functions for f_sort() and f_uniq() below. static int item_compare(const void *s1, const void *s2, bool keep_zero) { ListSortItem *const si1 = (ListSortItem *)s1; @@ -10370,9 +10062,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) return item_compare2(s1, s2, false); } -/* - * "sort({list})" function - */ +/// "sort({list})" function static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { ListSortItem *ptrs; @@ -10565,7 +10255,7 @@ static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_sort_uniq(argvars, rettv, false); } -// "reltimefloat()" function +/// "reltimefloat()" function static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { @@ -10578,9 +10268,7 @@ static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "soundfold({word})" function - */ +/// "soundfold({word})" function static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -10588,9 +10276,7 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)eval_soundfold(s); } -/* - * "spellbadword()" function - */ +/// "spellbadword()" function static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *word = ""; @@ -10649,9 +10335,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) NULL), -1); } -/* - * "spellsuggest()" function - */ +/// "spellsuggest()" function static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool typeerr = false; @@ -10806,9 +10490,7 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "str2float()" function - */ +/// "str2float()" function static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); @@ -10824,7 +10506,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_FLOAT; } -// "str2list()" function +/// "str2list()" function static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, kListLenUnknown); @@ -10835,7 +10517,7 @@ static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "str2nr()" function +/// "str2nr()" function static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; @@ -10878,9 +10560,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "strftime({format}[, {time}])" function - */ +/// "strftime({format}[, {time}])" function static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { time_t seconds; @@ -10932,7 +10612,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "strgetchar()" function +/// "strgetchar()" function static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; @@ -10960,9 +10640,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "stridx()" function - */ +/// "stridx()" function static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; @@ -10994,26 +10672,20 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "string()" function - */ +/// "string()" function static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)encode_tv2string(&argvars[0], NULL); } -/* - * "strlen()" function - */ +/// "strlen()" function static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } -/* - * "strchars()" function - */ +/// "strchars()" function static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *s = tv_get_string(&argvars[0]); @@ -11036,9 +10708,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "strdisplaywidth()" function - */ +/// "strdisplaywidth()" function static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const s = tv_get_string(&argvars[0]); @@ -11051,9 +10721,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); } -/* - * "strwidth()" function - */ +/// "strwidth()" function static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const s = tv_get_string(&argvars[0]); @@ -11061,7 +10729,7 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s); } -// "strcharpart()" function +/// "strcharpart()" function static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const p = tv_get_string(&argvars[0]); @@ -11115,9 +10783,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len); } -/* - * "strpart()" function - */ +/// "strpart()" function static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; @@ -11163,7 +10829,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } -// "strptime({format}, {timestring})" function +/// "strptime({format}, {timestring})" function static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char fmt_buf[NUMBUFLEN]; @@ -11195,9 +10861,7 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(enc); } -/* - * "strridx()" function - */ +/// "strridx()" function static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf[NUMBUFLEN]; @@ -11240,18 +10904,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "strtrans()" function - */ +/// "strtrans()" function static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]), true); } -/* - * "submatch()" function - */ +/// "submatch()" function static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; @@ -11282,9 +10942,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "substitute()" function - */ +/// "substitute()" function static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char patbuf[NUMBUFLEN]; @@ -11353,9 +11011,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = id; } -/* - * "synIDattr(id, what [, mode])" function - */ +/// "synIDattr(id, what [, mode])" function static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const int id = (int)tv_get_number(&argvars[0]); @@ -11431,9 +11087,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); } -/* - * "synIDtrans(id)" function - */ +/// "synIDtrans(id)" function static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int id = tv_get_number(&argvars[0]); @@ -11447,9 +11101,7 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = id; } -/* - * "synconcealed(lnum, col)" function - */ +/// "synconcealed(lnum, col)" function static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int syntax_flags = 0; @@ -11491,9 +11143,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(rettv->vval.v_list, matchid); } -/* - * "synstack(lnum, col)" function - */ +/// "synstack(lnum, col)" function static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_set_ret(rettv, NULL); @@ -11529,9 +11179,7 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * "tabpagebuflist()" function - */ +/// "tabpagebuflist()" function static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wp = NULL; @@ -11553,9 +11201,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "tabpagenr()" function - */ +/// "tabpagenr()" function static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; @@ -11581,9 +11227,7 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Common code for tabpagewinnr() and winnr(). - */ +/// Common code for tabpagewinnr() and winnr(). static int get_winnr(tabpage_T *tp, typval_T *argvar) { win_T *twin; @@ -11648,9 +11292,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) return nr; } -/* - * "tabpagewinnr()" function - */ +/// "tabpagewinnr()" function static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; @@ -11663,9 +11305,7 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = nr; } -/* - * "tagfiles()" function - */ +/// "tagfiles()" function static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char *fname; @@ -11684,9 +11324,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(fname); } -/* - * "taglist()" function - */ +/// "taglist()" function static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *const tag_pattern = tv_get_string(&argvars[0]); @@ -11704,16 +11342,14 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) (char_u *)tag_pattern, (char_u *)fname); } -/* - * "tempname()" function - */ +/// "tempname()" function static void f_tempname(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = vim_tempname(); } -// "termopen(cmd[, cwd])" function +/// "termopen(cmd[, cwd])" function static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (check_secure()) { @@ -11826,7 +11462,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) channel_create_event(chan, NULL); } -// "test_garbagecollect_now()" function +/// "test_garbagecollect_now()" function static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // This is dangerous, any Lists and Dicts used internally may be freed @@ -11834,7 +11470,7 @@ static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr garbage_collect(true); } -// "test_write_list_log()" function +/// "test_write_list_log()" function static void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr) { const char *const fname = tv_get_string_chk(&argvars[0]); @@ -11917,7 +11553,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -// "timer_stop(timerid)" function +/// "timer_stop(timerid)" function static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type != VAR_NUMBER) { @@ -11938,9 +11574,7 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) timer_stop_all(); } -/* - * "tolower(string)" function - */ +/// "tolower(string)" function static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -11948,9 +11582,7 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) false); } -/* - * "toupper(string)" function - */ +/// "toupper(string)" function static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -11958,9 +11590,7 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) true); } -/* - * "tr(string, fromstr, tostr)" function - */ +/// "tr(string, fromstr, tostr)" function static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf[NUMBUFLEN]; @@ -12040,7 +11670,7 @@ error: return; } -// "trim({expr})" function +/// "trim({expr})" function static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char buf1[NUMBUFLEN]; @@ -12123,9 +11753,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = vim_strnsave(head, tail - head); } -/* - * "type(expr)" function - */ +/// "type(expr)" function static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n = -1; @@ -12157,9 +11785,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = n; } -/* - * "undofile(name)" function - */ +/// "undofile(name)" function static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -12178,9 +11804,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "undotree()" function - */ +/// "undotree()" function static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); @@ -12198,17 +11822,13 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } -/* - * "values(dict)" function - */ +/// "values(dict)" function static void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_list(argvars, rettv, 1); } -/* - * "virtcol(string)" function - */ +/// "virtcol(string)" function static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { colnr_T vcol = 0; @@ -12234,9 +11854,7 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = vcol; } -/* - * "visualmode()" function - */ +/// "visualmode()" function static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u str[2]; @@ -12252,9 +11870,7 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "wildmenumode()" function - */ +/// "wildmenumode()" function static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (wild_menu_showing || ((State & CMDLINE) && pum_visible())) { @@ -12368,9 +11984,7 @@ static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "wincol()" function - */ +/// "wincol()" function static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr) { validate_cursor(); @@ -12388,7 +12002,7 @@ static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "winlayout()" function +/// "winlayout()" function static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tp; @@ -12407,18 +12021,14 @@ static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_framelayout(tp->tp_topframe, rettv->vval.v_list, true); } -/* - * "winline()" function - */ +/// "winline()" function static void f_winline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { validate_cursor(); rettv->vval.v_number = curwin->w_wrow + 1; } -/* - * "winnr()" function - */ +/// "winnr()" function static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; @@ -12427,9 +12037,7 @@ static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = nr; } -/* - * "winrestcmd()" function - */ +/// "winrestcmd()" function static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; @@ -12456,9 +12064,7 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; } -/* - * "winrestview()" function - */ +/// "winrestview()" function static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; @@ -12509,9 +12115,7 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/* - * "winsaveview()" function - */ +/// "winsaveview()" function static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; @@ -12542,7 +12146,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -// "windowsversion()" function +/// "windowsversion()" function static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; @@ -12633,9 +12237,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } -/* - * "xor(expr, expr)" function - */ + +/// "xor(expr, expr)" function static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) -- cgit From 880d3537d06ef067021c73bc361fa47ab8347992 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 21 Feb 2022 10:55:36 +0000 Subject: vim-patch:8.2.4428: crash when switching tabpage while in the cmdline window Problem: Crash when switching tabpage while in the cmdline window. Solution: Disallow switching tabpage when in the cmdline window. https://github.com/vim/vim/commit/0f6e28f686dbb59ab3b562408ab9b2234797b9b1 Ensure cmdline window doesn't stop us from closing tabs with EXITFREE. mem_free_all -> win_free_all -> tabpage_close -> ... -> goto_tabpage_tp -> CHECK_CMDWIN can cause an infinite loop if Nvim is exited without using standard methods such as :qa! and friends (e.g: killed via a signal). This issue had caused the ASAN CI's functionaltests to timeout. Cherry-pick Test_cmdwin_tabpage from v8.2.4463. https://github.com/vim/vim/commit/38b85cb4d7216705058708bacbc25ab90cd61595 This bug was already fixed in Nvim. Note that g inside cmdwin is already tested for in tabnewentered_spec.lua anyway. E492 is thrown after E11 when using ":norm" in assert_fails for some reason (except after v8.2.1919, which isn't ported yet). As v8.2.1183 isn't ported yet, so we cannot assert E11 directly. Modify the test to check for E11 and E492 seperately; when v8.2.1183 is ported, the assertion for E492 will fail and the changes can be reverted to match upstream. Remove redundant CHECK_CMDWIN from goto_tabpage; it's handled with text_locked() and text_locked_msg() above: vim-patch:8.2.4434: duplicate check for cmdline window Problem: Duplicate check for cmdline window. Solution: Remove the second check. (Sean Dewar, closes vim/vim#9816) https://github.com/vim/vim/commit/16b51d26fe2cc3afb09afd439069220dea74581d --- src/nvim/eval.c | 7 ++----- src/nvim/ex_docmd.c | 9 +++++---- src/nvim/ex_getln.c | 2 +- src/nvim/memory.c | 3 +++ src/nvim/testdir/test_cmdline.vim | 12 ++++++++++++ src/nvim/window.c | 16 ++++++++++++++++ 6 files changed, 39 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18b9039d60..b2ce15fd87 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3257,9 +3257,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // b: variables - // In cmdwin, the alternative buffer should be used. - hashtab_T *ht - = is_in_cmdwin() ? &prevwin->w_buffer->b_vars->dv_hashtab : &curbuf->b_vars->dv_hashtab; + const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab; if (bdone < ht->ht_used) { if (bdone++ == 0) { hi = ht->ht_array; @@ -3273,8 +3271,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // w: variables - // In cmdwin, the alternative window should be used. - ht = is_in_cmdwin() ? &prevwin->w_vars->dv_hashtab : &curwin->w_vars->dv_hashtab; + ht = &prevwin_curwin()->w_vars->dv_hashtab; if (wdone < ht->ht_used) { if (wdone++ == 0) { hi = ht->ht_array; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 63dc1e539e..6bd465e6ee 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2704,7 +2704,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * // only full match global is accepted. // Look for buffer-local user commands first, then global ones. - gap = is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; + gap = &prevwin_curwin()->w_buffer->b_ucmds; for (;;) { for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); @@ -5378,7 +5378,7 @@ static void uc_list(char_u *name, size_t name_len) uint32_t a; // In cmdwin, the alternative buffer should be used. - garray_T *gap = is_in_cmdwin() ? &prevwin->w_buffer->b_ucmds : &curbuf->b_ucmds; + const garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds; for (;;) { for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); @@ -6357,7 +6357,7 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // In cmdwin, the alternative buffer should be used. - const buf_T *const buf = is_in_cmdwin() ? prevwin->w_buffer : curbuf; + const buf_T *const buf = prevwin_curwin()->w_buffer; if (idx < buf->b_ucmds.ga_len) { return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; @@ -6379,7 +6379,8 @@ static char_u *get_user_command_name(int idx, int cmdidx) } if (cmdidx == CMD_USER_BUF) { // In cmdwin, the alternative buffer should be used. - buf_T *buf = is_in_cmdwin() ? prevwin->w_buffer : curbuf; + const buf_T *const buf = prevwin_curwin()->w_buffer; + if (idx < buf->b_ucmds.ga_len) { return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 30287cd6f2..f52f3afe7d 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6589,7 +6589,7 @@ static int open_cmdwin(void) /// @return true if in the cmdwin, not editing the command line. bool is_in_cmdwin(void) - FUNC_ATTR_PURE + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return cmdwin_type != 0 && get_cmdline_type() == NUL; } diff --git a/src/nvim/memory.c b/src/nvim/memory.c index d68ca6b62e..373693a6fe 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -575,6 +575,9 @@ void free_all_mem(void) // Don't want to trigger autocommands from here on. block_autocmds(); + // Ensure cmdline window doesn't prevent closing tabs and windows. + cmdwin_type = 0; + // Close all tabs and windows. Reset 'equalalways' to avoid redraws. p_ea = false; if (first_tabpage->tp_next != NULL) { diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index ff4cbe544c..c589d941da 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -1123,6 +1123,18 @@ func Test_cmdlineclear_tabenter() call delete('XtestCmdlineClearTabenter') endfunc +func Test_cmdwin_tabpage() + tabedit + " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here. + " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly. + " For now, assert E11 and E492 seperately. When v8.2.1183 is ported, the + " assert for E492 will fail and this workaround should be removed. + " call assert_fails("silent norm q/g :I\", 'E11:') + call assert_fails("silent norm q/g ", 'E11:') + call assert_fails("silent norm q/g :I\", 'E492:') + tabclose! +endfunc + " test that ";" works to find a match at the start of the first line func Test_zero_line_search() new diff --git a/src/nvim/window.c b/src/nvim/window.c index d659f60e66..5878a6ba0b 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -73,6 +73,15 @@ typedef enum { static char *m_onlyone = N_("Already only one window"); +/// @return the current window, unless in the cmdline window and "prevwin" is +/// set, then return "prevwin". +win_T *prevwin_curwin(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // In cmdwin, the alternative buffer should be used. + return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin; +} + /// all CTRL-W window commands are handled here, called from normal_cmd(). /// /// @param xchar extra char from ":wincmd gx" or NUL @@ -3857,6 +3866,11 @@ int win_new_tabpage(int after, char_u *filename) tabpage_T *newtp; int n; + if (cmdwin_type != 0) { + emsg(_(e_cmdwin)); + return FAIL; + } + newtp = alloc_tabpage(); // Remember the current windows in this Tab page. @@ -4255,6 +4269,8 @@ void goto_tabpage(int n) /// @param trigger_leave_autocmds when true trigger *Leave autocommands. void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds) { + CHECK_CMDWIN; + // Don't repeat a message in another tab page. set_keep_msg(NULL, 0); -- cgit From 490874f3da4c39a36438b6cfa6d79e594b82806f Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 21 Feb 2022 16:19:49 +0000 Subject: vim-patch:8.2.4432: cannot use settabvar() while the cmdline window is open Problem: Cannot use settabvar() while the cmdline window is open. Solution: Only give an error when actually switching tabpage. (closes vim/vim#9813) https://github.com/vim/vim/commit/592f6250017c31c8996325403e511f4502077ba5 --- src/nvim/window.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 5878a6ba0b..3c34620db4 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -4269,7 +4269,9 @@ void goto_tabpage(int n) /// @param trigger_leave_autocmds when true trigger *Leave autocommands. void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds) { - CHECK_CMDWIN; + if (trigger_enter_autocmds || trigger_leave_autocmds) { + CHECK_CMDWIN; + } // Don't repeat a message in another tab page. set_keep_msg(NULL, 0); -- cgit From 7519af4f0fb54f4ef8b48ec4f18036069f18bf62 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 25 Feb 2022 00:13:25 +0000 Subject: vim-patch:8.2.4438: crash on exit when using cmdline window Problem: Crash on exit when using cmdline window. Solution: Reset "cmdwin_type" before exiting. (closes vim/vim#9817) https://github.com/vim/vim/commit/ca0c1caa36823ea8e61184268d7337e79995352f Bram also went with the cmdwin_type = 0 solution, but putting it in read_error_exit isn't ideal and only fixes one specific variant of the bug, so don't port that change. Port the test only, but skip it as Nvim does not exit after stdin is exhausted. Using -es instead does exit, but read_error_exit does not run preserve_exit in that case, and does not have issues exiting even without resetting cmdwin_type. Note that the test has problems and is fixed in later patches. --- src/nvim/testdir/test_exit.vim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index bd3e9eb4d4..1d3722c718 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -1,6 +1,7 @@ " Tests for exiting Vim. source shared.vim +source check.vim func Test_exiting() let after =<< trim [CODE] @@ -109,4 +110,21 @@ func Test_exit_code() call delete('Xtestout') endfunc +func Test_exit_error_reading_input() + throw 'Skipped: Nvim does not exit after stdin is read' + + CheckNotGui + + call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew\q:"], 'Xscript') + + " Nvim requires "-s -" to read stdin as Normal mode input + " if RunVim([], [], '< Xscript') + if RunVim([], [], '-s - < Xscript') + call assert_equal(['l = 1'], readfile('Xtestout')) + endif + call delete('Xscript') + call delete('Xtestout') +endfun + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From fe5318293442aabdd4c7fd12ad892adcba1f80fb Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 25 Feb 2022 00:22:12 +0000 Subject: vim-patch:8.2.4442: test for error reading input fails on MS-Windows Problem: Test for error reading input fails on MS-Windows. Solution: Don't run the test on MS-Windows. https://github.com/vim/vim/commit/70b9e4f4c3a62e325fd16ac108bd12feb026ede5 --- src/nvim/testdir/test_exit.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index 1d3722c718..cbbd82d7d3 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -114,6 +114,7 @@ func Test_exit_error_reading_input() throw 'Skipped: Nvim does not exit after stdin is read' CheckNotGui + CheckNotMSWindows call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew\q:"], 'Xscript') -- cgit From 0412dba45622eeb20e83d5458bab8714e2da0d2e Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 25 Feb 2022 00:20:35 +0000 Subject: vim-patch:8.2.4444: beep caused by test Problem: Beep caused by test. ASAN reports leaks. Solution: Do not put a NL at the end of the script. Make the text work on MS-Windows. Do not run the test with ASAN. https://github.com/vim/vim/commit/68eab67119734ea1efc7cef1287276d969f2713a The test is skipped, but cherry-pick CheckNotAsan from v8.2.2424 anyway. https://github.com/vim/vim/commit/97202d951685fc4d90085da676a90644cbf72571 --- src/nvim/testdir/check.vim | 8 ++++++++ src/nvim/testdir/test_exit.vim | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim index 883f036fe1..8f97d959ce 100644 --- a/src/nvim/testdir/check.vim +++ b/src/nvim/testdir/check.vim @@ -137,6 +137,14 @@ func CheckNotMSWindows() endif endfunc +" Command to check for not running under ASAN +command CheckNotAsan call CheckNotAsan() +func CheckNotAsan() + if execute('version') =~# '-fsanitize=[a-z,]*\' + throw 'Skipped: does not work with ASAN' + endif +endfunc + " Command to check for satisfying any of the conditions. " e.g. CheckAnyOf Feature:bsd Feature:sun Linux command -nargs=+ CheckAnyOf call CheckAnyOf() diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index cbbd82d7d3..412802efb2 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -114,13 +114,14 @@ func Test_exit_error_reading_input() throw 'Skipped: Nvim does not exit after stdin is read' CheckNotGui - CheckNotMSWindows + " The early exit causes memory not to be freed somehow + CheckNotAsan - call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew\q:"], 'Xscript') + call writefile([":au VimLeave * call writefile(['l = ' .. v:exiting], 'Xtestout')", ":tabnew", "q:"], 'Xscript', 'b') " Nvim requires "-s -" to read stdin as Normal mode input - " if RunVim([], [], '< Xscript') - if RunVim([], [], '-s - < Xscript') + " if RunVim([], [], ' Date: Fri, 25 Feb 2022 00:22:54 +0000 Subject: vim-patch:8.2.4445: exit test fails on MS-Windows anyway Problem: Exit test fails on MS-Windows anyway. Solution: Skip the test on MS-Windows. https://github.com/vim/vim/commit/29a9e6971849b4a9eabf14fee1130d51cecfbaa7 --- src/nvim/testdir/test_exit.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index 412802efb2..4e5da49adb 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -114,6 +114,7 @@ func Test_exit_error_reading_input() throw 'Skipped: Nvim does not exit after stdin is read' CheckNotGui + CheckNotMSWindows " The early exit causes memory not to be freed somehow CheckNotAsan -- cgit From 7e19c18a544b5f1f15cec0444385ddae80687a26 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 25 Feb 2022 00:17:17 +0000 Subject: vim-patch:8.2.4454: resetting cmdwin_type only for one situation Problem: Resetting cmdwin_type only for one situation. Solution: Reset cmdwin_type before closing windows. (closes vim/vim#9822) https://github.com/vim/vim/commit/6a8b13614e5bcb233d20403ae9f008ccba152be3 Move the check to win_free_all to match Vim. --- src/nvim/memory.c | 3 --- src/nvim/testdir/test_exit.vim | 1 + src/nvim/window.c | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 373693a6fe..d68ca6b62e 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -575,9 +575,6 @@ void free_all_mem(void) // Don't want to trigger autocommands from here on. block_autocmds(); - // Ensure cmdline window doesn't prevent closing tabs and windows. - cmdwin_type = 0; - // Close all tabs and windows. Reset 'equalalways' to avoid redraws. p_ea = false; if (first_tabpage->tp_next != NULL) { diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim index 4e5da49adb..befcaec2b2 100644 --- a/src/nvim/testdir/test_exit.vim +++ b/src/nvim/testdir/test_exit.vim @@ -123,6 +123,7 @@ func Test_exit_error_reading_input() " Nvim requires "-s -" to read stdin as Normal mode input " if RunVim([], [], 'tp_next != NULL) { tabpage_close(TRUE); } -- cgit From c5f190e0c21b4e8465502fd9f260c3d49a4102ab Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Mar 2022 11:37:44 +0000 Subject: vim-patch:8.2.1401: cannot jump to the last used tabpage Problem: Cannot jump to the last used tabpage. Solution: Add g and tabpagnr('#'). (Yegappan Lakshmanan, closes vim/vim#6661, neovim #11626) https://github.com/vim/vim/commit/62a232506d06f6d1b3b7271801c907d6294dfe84 Nvim implemented this feature before Vim, but Vim made some useful changes (e.g: beeping on failure). Port the changes to closer match Vim (also makes porting future patches easier). Also note that because CHECK_CMDWIN was added to goto_tabpage_tp, there is no need to do the extra work with tabpage_index and goto_tabpage inside goto_tabpage_lastused to fix cmdwin issues any more (#11692). Note that while goto_tabpage_tp doesn't check for textlock like goto_tabpage does, it shouldn't matter as it is already checked for earlier. Add tags for to tabpage.txt, and refer to over CTRL-Tab to be consistent with other docs like the patch. Remove mention of "previous tabpage" (it can be confused with the tabpage to the left, e.g: `:tabprevious`). Similarly, don't rename old_curtab to last_tab in enter_tabpage (it might be confused with the right-most tabpage, e.g: `:tablast`). Cherry-pick Test_tabpage change from v8.2.0634. https://github.com/vim/vim/commit/92b83ccfda7a1d654ccaaf161a9c8a8e01fbcf76 --- src/nvim/eval/funcs.c | 4 +--- src/nvim/globals.h | 5 +++-- src/nvim/normal.c | 11 ++++++---- src/nvim/testdir/test_tabpage.vim | 46 +++++++++++++++++++++++++++++++++++++++ src/nvim/window.c | 24 +++++++++++--------- 5 files changed, 71 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b249dffe11..b74f9759ac 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -11213,9 +11213,7 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (strcmp(arg, "$") == 0) { nr = tabpage_index(NULL) - 1; } else if (strcmp(arg, "#") == 0) { - nr = valid_tabpage(lastused_tabpage) - ? tabpage_index(lastused_tabpage) - : nr; + nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0; } else { semsg(_(e_invexpr2), arg); } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index b64ed7c758..cbd67afb09 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -448,10 +448,11 @@ EXTERN int aucmd_win_used INIT(= false); // aucmd_win is being used EXTERN frame_T *topframe; // top of the window frame tree // Tab pages are alternative topframes. "first_tabpage" points to the first -// one in the list, "curtab" is the current one. +// one in the list, "curtab" is the current one. "lastused_tabpage" is the +// last used one. EXTERN tabpage_T *first_tabpage; -EXTERN tabpage_T *lastused_tabpage; EXTERN tabpage_T *curtab; +EXTERN tabpage_T *lastused_tabpage; EXTERN bool redraw_tabline INIT(= false); // need to redraw tabline // Iterates over all tabs in the tab list diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0e5e0ab403..f402865d2d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5869,7 +5869,7 @@ static void nv_gomark(cmdarg_T *cap) } } -// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands. +/// Handle CTRL-O, CTRL-I, "g;", "g,", and "CTRL-Tab" commands. static void nv_pcmark(cmdarg_T *cap) { pos_T *pos; @@ -5878,7 +5878,9 @@ static void nv_pcmark(cmdarg_T *cap) if (!checkclearopq(cap->oap)) { if (cap->cmdchar == TAB && mod_mask == MOD_MASK_CTRL) { - goto_tabpage_lastused(); + if (!goto_tabpage_lastused()) { + clearopbeep(cap->oap); + } return; } if (cap->cmdchar == 'g') { @@ -6642,9 +6644,10 @@ static void nv_g_cmd(cmdarg_T *cap) goto_tabpage(-(int)cap->count1); } break; + case TAB: - if (!checkclearop(oap)) { - goto_tabpage_lastused(); + if (!checkclearop(oap) && !goto_tabpage_lastused()) { + clearopbeep(oap); } break; diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 9869dc7590..b458f9a311 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -128,6 +128,8 @@ function Test_tabpage() 1tabmove call assert_equal(2, tabpagenr()) + call assert_fails('let t = tabpagenr("@")', 'E15:') + call assert_equal(0, tabpagewinnr(-1)) call assert_fails("99tabmove", 'E16:') call assert_fails("+99tabmove", 'E16:') call assert_fails("-99tabmove", 'E16:') @@ -683,4 +685,48 @@ func Test_tabline_tabmenu() %bw! endfunc +" Test for jumping to last accessed tabpage +func Test_lastused_tabpage() + tabonly! + call assert_equal(0, tabpagenr('#')) + call assert_beeps('call feedkeys("g\", "xt")') + call assert_beeps('call feedkeys("\", "xt")') + call assert_beeps('call feedkeys("\g\", "xt")') + + " open four tab pages + tabnew + tabnew + tabnew + + 2tabnext + + " Test for g + call assert_equal(4, tabpagenr('#')) + call feedkeys("g\", "xt") + call assert_equal(4, tabpagenr()) + call assert_equal(2, tabpagenr('#')) + + " Test for + call feedkeys("\", "xt") + call assert_equal(2, tabpagenr()) + call assert_equal(4, tabpagenr('#')) + + " Test for g + call feedkeys("\g\", "xt") + call assert_equal(4, tabpagenr()) + call assert_equal(2, tabpagenr('#')) + + " Try to jump to a closed tab page + tabclose 2 + call assert_equal(0, tabpagenr('#')) + call feedkeys("g\", "xt") + call assert_equal(3, tabpagenr()) + call feedkeys("\", "xt") + call assert_equal(3, tabpagenr()) + call feedkeys("\g\", "xt") + call assert_equal(3, tabpagenr()) + + tabclose! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index f2b84a4124..a235b07b47 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -530,10 +530,6 @@ wingotofile: do_nv_ident('g', xchar); break; - case TAB: - goto_tabpage_lastused(); - break; - case 'f': // CTRL-W gf: "gf" in a new tab page case 'F': // CTRL-W gF: "gF" in a new tab page cmdmod.tab = tabpage_index(curtab) + 1; @@ -547,6 +543,12 @@ wingotofile: goto_tabpage(-(int)Prenum1); break; + case TAB: // CTRL-W g: go to last used tab page + if (!goto_tabpage_lastused()) { + beep_flush(); + } + break; + case 'e': if (curwin->w_floating || !ui_has(kUIMultigrid)) { beep_flush(); @@ -4119,8 +4121,8 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a { int old_off = tp->tp_firstwin->w_winrow; win_T *next_prevwin = tp->tp_prevwin; - tabpage_T *old_curtab = curtab; + curtab = tp; firstwin = tp->tp_firstwin; lastwin = tp->tp_lastwin; @@ -4291,13 +4293,15 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le } } -// Go to the last accessed tab page, if there is one. -void goto_tabpage_lastused(void) +/// Go to the last accessed tab page, if there is one. +/// @return true if the tab page is valid, false otherwise. +bool goto_tabpage_lastused(void) { - int index = tabpage_index(lastused_tabpage); - if (index < tabpage_index(NULL)) { - goto_tabpage(index); + if (valid_tabpage(lastused_tabpage)) { + goto_tabpage_tp(lastused_tabpage, true, true); + return true; } + return false; } /* -- cgit From 365a9b074f2df3c573ae4a520084818bdd46cd3d Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Mon, 14 Mar 2022 12:16:12 +0000 Subject: vim-patch:8.2.1413: previous tab page not usable from an Ex command Problem: Previous tab page not usable from an Ex command. Solution: Add the "#" argument for :tabnext et al. (Yegappan Lakshmanan, closes vim/vim#6677) https://github.com/vim/vim/commit/94f4ffa7704921a3634e56b878e6dc362bc3d508 Do not rename old_curtab to prev_tp in win_new_tabpage, this can be confused with the previous tabpage (`:tabprevious`). Cherry-pick ex_errmsg from v8.2.1280. https://github.com/vim/vim/commit/8930caaa1a283092aca81fdbc3fcf15c7eadb197 --- src/nvim/ex_docmd.c | 20 +++++++++++++++++++- src/nvim/testdir/test_tabpage.vim | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6bd465e6ee..09cf6601ee 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2036,6 +2036,18 @@ doend: return ea.nextcmd; } +static char ex_error_buf[MSG_BUF_LEN]; + +/// @return an error message with argument included. +/// Uses a static buffer, only the last error will be kept. +/// "msg" will be translated, caller should use N_(). +char *ex_errmsg(const char *const msg, const char_u *const arg) + FUNC_ATTR_NONNULL_ALL +{ + vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg); + return ex_error_buf; +} + // Parse and skip over command modifiers: // - update eap->cmd // - store flags in "cmdmod". @@ -4861,7 +4873,13 @@ static int get_tabpage_arg(exarg_T *eap) if (STRCMP(p, "$") == 0) { tab_number = LAST_TAB_NR; } else if (STRCMP(p, "#") == 0) { - tab_number = tabpage_index(lastused_tabpage); + if (valid_tabpage(lastused_tabpage)) { + tab_number = tabpage_index(lastused_tabpage); + } else { + eap->errmsg = ex_errmsg(e_invargval, eap->arg); + tab_number = 0; + goto theend; + } } else if (p == p_save || *p_save == '-' || *p != NUL || tab_number > LAST_TAB_NR) { // No numbers as argument. diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index b458f9a311..51ab5c1022 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -692,6 +692,7 @@ func Test_lastused_tabpage() call assert_beeps('call feedkeys("g\", "xt")') call assert_beeps('call feedkeys("\", "xt")') call assert_beeps('call feedkeys("\g\", "xt")') + call assert_fails('tabnext #', 'E475:') " open four tab pages tabnew @@ -716,17 +717,41 @@ func Test_lastused_tabpage() call assert_equal(4, tabpagenr()) call assert_equal(2, tabpagenr('#')) + " Test for :tabnext # + tabnext # + call assert_equal(2, tabpagenr()) + call assert_equal(4, tabpagenr('#')) + " Try to jump to a closed tab page - tabclose 2 + tabclose # call assert_equal(0, tabpagenr('#')) call feedkeys("g\", "xt") - call assert_equal(3, tabpagenr()) + call assert_equal(2, tabpagenr()) call feedkeys("\", "xt") - call assert_equal(3, tabpagenr()) + call assert_equal(2, tabpagenr()) call feedkeys("\g\", "xt") - call assert_equal(3, tabpagenr()) + call assert_equal(2, tabpagenr()) + call assert_fails('tabnext #', 'E475:') + call assert_equal(2, tabpagenr()) - tabclose! + " Test for :tabonly # + let wnum = win_getid() + $tabnew + tabonly # + call assert_equal(wnum, win_getid()) + call assert_equal(1, tabpagenr('$')) + + " Test for :tabmove # + tabnew + let wnum = win_getid() + tabnew + tabnew + tabnext 2 + tabmove # + call assert_equal(4, tabpagenr()) + call assert_equal(wnum, win_getid()) + + tabonly! endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 378db4f32ab004eb5e37a7bac4f3ca132cf1ac98 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(eval): convert function comments to doxygen format --- src/nvim/eval.c | 848 +++++++++++++++++++++++++------------------------------- 1 file changed, 378 insertions(+), 470 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18b9039d60..464e408efa 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -331,7 +331,7 @@ void restore_v_event(dict_T *v_event, save_v_event_T *sve) } } -// Return "n1" divided by "n2", taking care of dividing by zero. +/// @return "n1" divided by "n2", taking care of dividing by zero. varnumber_T num_divide(varnumber_T n1, varnumber_T n2) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { @@ -352,7 +352,7 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2) return result; } -// Return "n1" modulus "n2", taking care of dividing by zero. +/// @return "n1" modulus "n2", taking care of dividing by zero. varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { @@ -360,9 +360,7 @@ varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) return (n2 == 0) ? 0 : (n1 % n2); } -/* - * Initialize the global and v: variables. - */ +/// Initialize the global and v: variables. void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; @@ -502,10 +500,8 @@ void eval_clear(void) #endif -/* - * Set an internal variable to a string value. Creates the variable if it does - * not already exist. - */ +/// Set an internal variable to a string value. Creates the variable if it does +/// not already exist. void set_internal_string_var(const char *name, char_u *value) FUNC_ATTR_NONNULL_ARG(1) { @@ -523,9 +519,10 @@ static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /// Start recording command output to a variable -/// Returns OK if successfully completed the setup. FAIL otherwise. /// /// @param append append to an existing variable +/// +/// @return OK if successfully completed the setup. FAIL otherwise. int var_redir_start(char_u *name, int append) { int save_emsg; @@ -586,15 +583,13 @@ int var_redir_start(char_u *name, int append) return OK; } -/* - * Append "value[value_len]" to the variable set by var_redir_start(). - * The actual appending is postponed until redirection ends, because the value - * appended may in fact be the string we write to, changing it may cause freed - * memory to be used: - * :redir => foo - * :let foo - * :redir END - */ +/// Append "value[value_len]" to the variable set by var_redir_start(). +/// The actual appending is postponed until redirection ends, because the value +/// appended may in fact be the string we write to, changing it may cause freed +/// memory to be used: +/// :redir => foo +/// :let foo +/// :redir END void var_redir_str(char_u *value, int value_len) { int len; @@ -614,10 +609,8 @@ void var_redir_str(char_u *value, int value_len) redir_ga.ga_len += len; } -/* - * Stop redirecting command output to a variable. - * Frees the allocated memory. - */ +/// Stop redirecting command output to a variable. +/// Frees the allocated memory. void var_redir_stop(void) { typval_T tv; @@ -744,7 +737,7 @@ int eval_to_bool(char_u *arg, bool *error, char_u **nextcmd, int skip) return retval; } -// Call eval1() and give an error message if not done at a lower level. +/// Call eval1() and give an error message if not done at a lower level. static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) FUNC_ATTR_NONNULL_ARG(1, 2) { @@ -767,8 +760,8 @@ static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) return ret; } -/// @return whether a typval is a valid expression to pass to eval_expr_typval() -/// or eval_expr_to_bool(). An empty string returns false; +/// @return whether a typval is a valid expression to pass to eval_expr_typval() +/// or eval_expr_to_bool(). An empty string returns false; bool eval_expr_valid_arg(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST { @@ -867,10 +860,9 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip return retval; } -/* - * Skip over an expression at "*pp". - * Return FAIL for an error, OK otherwise. - */ +/// Skip over an expression at "*pp". +/// +/// @return FAIL for an error, OK otherwise. int skip_expr(char_u **pp) { typval_T rettv; @@ -917,10 +909,10 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, bool convert) return (char_u *)retval; } -/* - * Call eval_to_string() without using current local variables and using - * textlock. When "use_sandbox" is TRUE use the sandbox. - */ +/// Call eval_to_string() without using current local variables and using +/// textlock. +/// +/// @param use_sandbox when TRUE, use the sandbox. char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) { char_u *retval; @@ -940,11 +932,10 @@ char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) return retval; } -/* - * Top level evaluation function, returning a number. - * Evaluates "expr" silently. - * Returns -1 for an error. - */ +/// Top level evaluation function, returning a number. +/// Evaluates "expr" silently. +/// +/// @return -1 for an error. varnumber_T eval_to_number(char_u *expr) { typval_T rettv; @@ -964,9 +955,10 @@ varnumber_T eval_to_number(char_u *expr) return retval; } -// Top level evaluation function. -// Returns an allocated typval_T with the result. -// Returns NULL when there is an error. +/// Top level evaluation function. +/// +/// @return an allocated typval_T with the result or +/// NULL when there is an error. typval_T *eval_expr(char_u *arg) { typval_T *tv = xmalloc(sizeof(*tv)); @@ -976,11 +968,9 @@ typval_T *eval_expr(char_u *arg) return tv; } -/* - * Prepare v: variable "idx" to be used. - * Save the current typeval in "save_tv". - * When not used yet add the variable to the v: hashtable. - */ +/// Prepare v: variable "idx" to be used. +/// Save the current typeval in "save_tv". +/// When not used yet add the variable to the v: hashtable. void prepare_vimvar(int idx, typval_T *save_tv) { *save_tv = vimvars[idx].vv_tv; @@ -989,10 +979,8 @@ void prepare_vimvar(int idx, typval_T *save_tv) } } -/* - * Restore v: variable "idx" to typeval "save_tv". - * When no longer defined, remove the variable from the v: hashtable. - */ +/// Restore v: variable "idx" to typeval "save_tv". +/// When no longer defined, remove the variable from the v: hashtable. void restore_vimvar(int idx, typval_T *save_tv) { hashitem_T *hi; @@ -1019,11 +1007,10 @@ void find_win_for_curbuf(void) } } -/* - * Evaluate an expression to a list with suggestions. - * For the "expr:" part of 'spellsuggest'. - * Returns NULL when there is an error. - */ +/// Evaluate an expression to a list with suggestions. +/// For the "expr:" part of 'spellsuggest'. +/// +/// @return NULL when there is an error. list_T *eval_spell_expr(char_u *badword, char_u *expr) { typval_T save_val; @@ -1087,7 +1074,7 @@ int get_spellword(list_T *const list, const char **ret_word) // Uses argv[0] to argv[argc-1] for the function arguments. argv[argc] // should have type VAR_UNKNOWN. // -// Return OK or FAIL. +// @return OK or FAIL. int call_vim_function(const char_u *func, int argc, typval_T *argv, typval_T *rettv) FUNC_ATTR_NONNULL_ALL { @@ -1225,10 +1212,8 @@ void prof_child_exit(proftime_T *tm) } -/* - * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding - * it in "*cp". Doesn't give error messages. - */ +/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding +/// it in "*cp". Doesn't give error messages. int eval_foldexpr(char_u *arg, int *cp) { typval_T tv; @@ -1269,26 +1254,27 @@ int eval_foldexpr(char_u *arg, int *cp) return (int)retval; } -// ":cons[t] var = expr1" define constant -// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list -// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list void ex_const(exarg_T *eap) { ex_let_const(eap, true); } -// Get a list of lines from a HERE document. The here document is a list of -// lines surrounded by a marker. -// cmd << {marker} -// {line1} -// {line2} -// .... -// {marker} -// -// The {marker} is a string. If the optional 'trim' word is supplied before the -// marker, then the leading indentation before the lines (matching the -// indentation in the 'cmd' line) is stripped. -// Returns a List with {lines} or NULL. +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. static list_T *heredoc_get(exarg_T *eap, char_u *cmd) { char_u *marker; @@ -1386,18 +1372,18 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd) return l; } -// ":let" list all variable values -// ":let var1 var2" list variable values -// ":let var = expr" assignment command. -// ":let var += expr" assignment command. -// ":let var -= expr" assignment command. -// ":let var *= expr" assignment command. -// ":let var /= expr" assignment command. -// ":let var %= expr" assignment command. -// ":let var .= expr" assignment command. -// ":let var ..= expr" assignment command. -// ":let [var1, var2] = expr" unpack list. -// ":let [name, ..., ; lastname] = expr" unpack list. +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. void ex_let(exarg_T *eap) { ex_let_const(eap, false); @@ -1578,13 +1564,12 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, return OK; } -/* - * Skip over assignable variable "var" or list of variables "[var, var]". - * Used for ":let varvar = expr" and ":for varvar in expr". - * For "[var, var]" increment "*var_count" for each variable. - * for "[var, var; var]" set "semicolon". - * Return NULL for an error. - */ +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semicolon) { const char_u *p; @@ -1622,10 +1607,8 @@ static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semic } } -/* - * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, - * l[idx]. - */ +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) { @@ -1635,10 +1618,9 @@ static const char_u *skip_var_one(const char_u *arg) NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); } -/* - * List variables for hashtab "ht" with prefix "prefix". - * If "empty" is TRUE also list NULL strings as empty strings. - */ +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if TRUE also list NULL strings as empty strings. void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) { hashitem_T *hi; @@ -1667,47 +1649,37 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs } } -/* - * List global variables. - */ +/// List global variables. static void list_glob_vars(int *first) { list_hashtable_vars(&globvarht, "", true, first); } -/* - * List buffer variables. - */ +/// List buffer variables. static void list_buf_vars(int *first) { list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); } -/* - * List window variables. - */ +/// List window variables. static void list_win_vars(int *first) { list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); } -/* - * List tab page variables. - */ +/// List tab page variables. static void list_tab_vars(int *first) { list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); } -/* - * List Vim variables. - */ +/// List Vim variables. static void list_vim_vars(int *first) { list_hashtable_vars(&vimvarht, "v:", false, first); } -// List script-local variables, if there is a script. +/// List script-local variables, if there is a script. static void list_script_vars(int *first) { if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { @@ -1715,9 +1687,7 @@ static void list_script_vars(int *first) } } -/* - * List variables in "arg". - */ +/// List variables in "arg". static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) { int error = FALSE; @@ -2380,9 +2350,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co // TODO(ZyX-I): move to eval/executor -/* - * Clear lval "lp" that was filled by get_lval(). - */ +/// Clear lval "lp" that was filled by get_lval(). void clear_lval(lval_T *lp) { xfree(lp->ll_exp_name); @@ -2391,12 +2359,11 @@ void clear_lval(lval_T *lp) // TODO(ZyX-I): move to eval/executor -/* - * Set a variable that was parsed by get_lval() to "rettv". - * "endp" points to just after the parsed name. - * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", - * "%" for "%=", "." for ".=" or "=" for "=". - */ +/// Set a variable that was parsed by get_lval() to "rettv". +/// +/// @param endp points to just after the parsed name. +/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", +/// "%" for "%=", "." for ".=" or "=" for "=". static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, const bool is_const, const char *op) { @@ -2599,12 +2566,12 @@ notify: // TODO(ZyX-I): move to eval/ex_cmds -/* - * Evaluate the expression used in a ":for var in expr" command. - * "arg" points to "var". - * Set "*errp" to TRUE for an error, FALSE otherwise; - * Return a pointer that holds the info. Null when there is an error. - */ +/// Evaluate the expression used in a ":for var in expr" command. +/// "arg" points to "var". +/// +/// @param[out] *errp set to TRUE for an error, FALSE otherwise; +/// +/// @return a pointer that holds the info. Null when there is an error. void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); @@ -2676,12 +2643,11 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Use the first item in a ":for" list. Advance to the next. - * Assign the values to the variable (list). "arg" points to the first one. - * Return TRUE when a valid item was found, FALSE when at end of list or - * something wrong. - */ +/// Use the first item in a ":for" list. Advance to the next. +/// Assign the values to the variable (list). "arg" points to the first one. +/// +/// @return true when a valid item was found, false when at end of list or +/// something wrong. bool next_for_item(void *fi_void, char_u *arg) { forinfo_T *fi = (forinfo_T *)fi_void; @@ -2727,9 +2693,7 @@ bool next_for_item(void *fi_void, char_u *arg) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Free the structure used to store info used by ":for". - */ +/// Free the structure used to store info used by ":for". void free_for_info(void *fi_void) { forinfo_T *fi = (forinfo_T *)fi_void; @@ -3178,9 +3142,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *e return ret; } -/* - * Delete all "menutrans_" variables. - */ +/// Delete all "menutrans_" variables. void del_menutrans_vars(void) { hash_lock(&globvarht); @@ -3202,9 +3164,7 @@ void del_menutrans_vars(void) static char_u *varnamebuf = NULL; static size_t varnamebuflen = 0; -/* - * Function to concatenate a prefix and a variable name. - */ +/// Function to concatenate a prefix and a variable name. char_u *cat_prefix_varname(int prefix, const char_u *name) FUNC_ATTR_NONNULL_ALL { @@ -3222,10 +3182,8 @@ char_u *cat_prefix_varname(int prefix, const char_u *name) return varnamebuf; } -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * (global/buffer/window/built-in) variable names. - */ +/// Function given to ExpandGeneric() to obtain the list of user defined +/// (global/buffer/window/built-in) variable names. char_u *get_user_var_name(expand_T *xp, int idx) { static size_t gdone; @@ -3313,8 +3271,9 @@ char_u *get_user_var_name(expand_T *xp, int idx) // TODO(ZyX-I): move to eval/expressions -/// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. +/// +/// @return TRUE if "pat" matches "text". static int pattern_match(char_u *pat, char_u *text, bool ic) { int matches = 0; @@ -3335,10 +3294,10 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) /// Handle a name followed by "(". Both for just "name(arg)" and for /// "expr->name(arg)". -// +/// /// @param arg Points to "(", will be advanced /// @param basetv "expr" for "expr->name(arg)" -// +/// /// @return OK or FAIL. static int eval_func(char_u **const arg, char_u *const name, const int name_len, typval_T *const rettv, const bool evaluate, typval_T *const basetv) @@ -3399,13 +3358,12 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len, * VAR_UNKNOWN. The function still returns FAIL for a syntax error. */ -/* - * Handle zero level expression. - * This calls eval1() and handles error message and nextcmd. - * Put the result in "rettv" when returning OK and "evaluate" is TRUE. - * Note: "rettv.v_lock" is not set. - * Return OK or FAIL. - */ +/// Handle zero level expression. +/// This calls eval1() and handles error message and nextcmd. +/// Put the result in "rettv" when returning OK and "evaluate" is TRUE. +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; @@ -3438,17 +3396,15 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle top level expression: - * expr2 ? expr1 : expr1 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Note: "rettv.v_lock" is not set. - * - * Return OK or FAIL. - */ +/// Handle top level expression: +/// expr2 ? expr1 : expr1 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. int eval1(char_u **arg, typval_T *rettv, int evaluate) { int result; @@ -3514,15 +3470,13 @@ int eval1(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle first level expression: - * expr2 || expr2 || expr2 logical OR - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle first level expression: +/// expr2 || expr2 || expr2 logical OR +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval2(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3585,15 +3539,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle second level expression: - * expr3 && expr3 && expr3 logical AND - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle second level expression: +/// expr3 && expr3 && expr3 logical AND +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval3(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3656,24 +3608,22 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle third level expression: - * var1 == var2 - * var1 =~ var2 - * var1 != var2 - * var1 !~ var2 - * var1 > var2 - * var1 >= var2 - * var1 < var2 - * var1 <= var2 - * var1 is var2 - * var1 isnot var2 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle third level expression: +/// var1 == var2 +/// var1 =~ var2 +/// var1 != var2 +/// var1 !~ var2 +/// var1 > var2 +/// var1 >= var2 +/// var1 < var2 +/// var1 <= var2 +/// var1 is var2 +/// var1 isnot var2 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval4(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3767,18 +3717,16 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle fourth level expression: - * + number addition - * - number subtraction - * . string concatenation - * .. string concatenation - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle fourth level expression: +/// + number addition +/// - number subtraction +/// . string concatenation +/// .. string concatenation +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval5(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -4324,7 +4272,8 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string) /// Apply the leading "!" and "-" before an eval7 expression to "rettv". /// Adjusts "end_leaderp" until it is at "start_leader". -/// @return OK on success, FAIL on failure. +/// +/// @return OK on success, FAIL on failure. static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, const char_u **const end_leaderp) FUNC_ATTR_NONNULL_ALL @@ -4378,7 +4327,7 @@ static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, /// @param lua_funcname If `rettv` refers to a v:lua function, this must point /// to the name of the Lua function to call (after the /// "v:lua." prefix). -/// @return OK on success, FAIL on failure. +/// @return OK on success, FAIL on failure. static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool evaluate, dict_T *const selfdict, typval_T *const basetv, const char_u *const lua_funcname) @@ -4426,9 +4375,13 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool } /// Evaluate "->method()". +/// /// @param verbose if true, give error messages. -/// @note "*arg" points to the '-'. -/// @return FAIL or OK. @note "*arg" is advanced to after the ')'. +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. +/// +/// @note "*arg" is advanced to after the ')'. static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool evaluate, const bool verbose) FUNC_ATTR_NONNULL_ALL @@ -4465,8 +4418,10 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva } /// Evaluate "->method()" or "->v:lua.method()". -/// @note "*arg" points to the '-'. -/// @return FAIL or OK. "*arg" is advanced to after the ')'. +/// +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. "*arg" is advanced to after the ')'. static int eval_method(char_u **const arg, typval_T *const rettv, const bool evaluate, const bool verbose) FUNC_ATTR_NONNULL_ALL @@ -4539,9 +4494,10 @@ static int eval_method(char_u **const arg, typval_T *const rettv, const bool eva /// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". /// "*arg" points to the '[' or '.'. -/// Returns FAIL or OK. "*arg" is advanced to after the ']'. /// /// @param verbose give error messages +/// +/// @returns FAIL or OK. "*arg" is advanced to after the ']'. static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) { bool empty1 = false; @@ -4846,12 +4802,12 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) /// Get an option value /// -/// @param[in,out] arg Points to the '&' or '+' before the option name. Is +/// @param[in,out] arg Points to the '&' or '+' before the option name. Is /// advanced to the character after the option name. -/// @param[out] rettv Location where result is saved. -/// @param[in] evaluate If not true, rettv is not populated. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. /// -/// @return OK or FAIL. +/// @return OK or FAIL. int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) FUNC_ATTR_NONNULL_ARG(1) { @@ -4910,10 +4866,9 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return ret; } -/* - * Allocate a variable for a string constant. - * Return OK or FAIL. - */ +/// Allocate a variable for a string constant. +/// +/// @return OK or FAIL. static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; @@ -5050,10 +5005,9 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } -/* - * Allocate a variable for a 'str''ing' constant. - * Return OK or FAIL. - */ +/// Allocate a variable for a 'str''ing' constant. +/// +/// @return OK or FAIL. static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; @@ -5106,7 +5060,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } -/// @return the function name of the partial. +/// @return the function name of the partial. char_u *partial_name(partial_T *pt) { if (pt->pt_name != NULL) { @@ -5145,7 +5099,8 @@ void partial_unref(partial_T *pt) } /// Allocate a variable for a List and fill it from "*arg". -/// Return OK or FAIL. +/// +/// @return OK or FAIL. static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; @@ -5283,7 +5238,8 @@ int get_copyID(void) /// Do garbage collection for lists and dicts. /// /// @param testing true if called from test_garbagecollect_now(). -/// @returns true if some memory was freed. +/// +/// @return true if some memory was freed. bool garbage_collect(bool testing) { bool abort = false; @@ -5461,10 +5417,11 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// @note This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// -/// @param copyID Free lists/dictionaries that don't have this ID. -/// @return true, if something was freed. +/// @param copyID Free lists/dictionaries that don't have this ID. +/// +/// @return true, if something was freed. static int free_unref_items(int copyID) { bool did_free = false; @@ -5699,7 +5656,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack /// Mark all lists and dicts referenced in given mark /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_in_fmark(fmark_T fm, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5713,7 +5670,7 @@ static inline bool set_ref_in_fmark(fmark_T fm, int copyID) /// Mark all lists and dicts referenced in given list and the list itself /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_list(list_T *list, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5729,7 +5686,7 @@ static inline bool set_ref_list(list_T *list, int copyID) /// Mark all lists and dicts referenced in given dict and the dict itself /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_dict(dict_T *dict, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5744,8 +5701,9 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) } -// Get the key for *{key: val} into "tv" and advance "arg". -// Return FAIL when there is no valid key. +/// Get the key for *{key: val} into "tv" and advance "arg". +/// +/// @return FAIL when there is no valid key. static int get_literal_key(char_u **arg, typval_T *tv) FUNC_ATTR_NONNULL_ALL { @@ -5763,9 +5721,10 @@ static int get_literal_key(char_u **arg, typval_T *tv) return OK; } -// Allocate a variable for a Dictionary and fill it from "*arg". -// "literal" is true for *{key: val} -// Return OK or FAIL. Returns NOTDONE for {expr}. +/// Allocate a variable for a Dictionary and fill it from "*arg". +/// "literal" is true for *{key: val} +/// +/// @return OK or FAIL. Returns NOTDONE for {expr}. static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, bool literal) { dict_T *d = NULL; @@ -5875,10 +5834,10 @@ failret: /// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to /// make sure this always uses a decimal point. /// -/// @param[in] text String to convert. -/// @param[out] ret_value Location where conversion result is saved. +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. /// -/// @return Length of the text that was consumed. +/// @return Length of the text that was consumed. size_t string2float(const char *const text, float_T *const ret_value) FUNC_ATTR_NONNULL_ALL { @@ -5905,9 +5864,9 @@ size_t string2float(const char *const text, float_T *const ret_value) /// /// If the environment variable was not set, silently assume it is empty. /// -/// @param arg Points to the '$'. It is advanced to after the name. -/// @return FAIL if the name is invalid. +/// @param arg Points to the '$'. It is advanced to after the name. /// +/// @return FAIL if the name is invalid. static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *name; @@ -5956,7 +5915,7 @@ void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv) } } -// Prepare "gap" for an assert error and add the sourcing position. +/// Prepare "gap" for an assert error and add the sourcing position. void prepare_assert_error(garray_T *gap) { char buf[NUMBUFLEN]; @@ -5977,8 +5936,8 @@ void prepare_assert_error(garray_T *gap) } } -// Append "p[clen]" to "gap", escaping unprintable characters. -// Changes NL to \n, CR to \r, etc. +/// Append "p[clen]" to "gap", escaping unprintable characters. +/// Changes NL to \n, CR to \r, etc. static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) FUNC_ATTR_NONNULL_ALL { @@ -6016,8 +5975,8 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) } } -// Append "str" to "gap", escaping unprintable characters. -// Changes NL to \n, CR to \r, etc. +/// Append "str" to "gap", escaping unprintable characters. +/// Changes NL to \n, CR to \r, etc. static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) FUNC_ATTR_NONNULL_ARG(1) { @@ -6051,7 +6010,7 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) } } -// Fill "gap" with information about an assert error. +/// Fill "gap" with information about an assert error. void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, typval_T *got_tv, assert_type_T atype) { @@ -6094,7 +6053,7 @@ void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typ } } -// Add an assert error to v:errors. +/// Add an assert error to v:errors. void assert_error(garray_T *gap) { struct vimvar *vp = &vimvars[VV_ERRORS]; @@ -6268,7 +6227,7 @@ int assert_inrange(typval_T *argvars) return 0; } -// Common for assert_true() and assert_false(). +/// Common for assert_true() and assert_false(). int assert_bool(typval_T *argvars, bool is_true) FUNC_ATTR_NONNULL_ALL { @@ -6442,9 +6401,7 @@ win_T *find_win_by_nr_or_id(typval_T *vp) return find_win_by_nr(vp, NULL); } -/* - * Implementation of map() and filter(). - */ +/// Implementation of map() and filter(). void filter_map(typval_T *argvars, typval_T *rettv, int map) { typval_T *expr; @@ -6806,7 +6763,7 @@ theend: xfree(trans_name); } -/// Returns buffer options, variables and other attributes in a dictionary. +/// @return buffer options, variables and other attributes in a dictionary. dict_T *get_buffer_info(buf_T *buf) { dict_T *const dict = tv_dict_alloc(); @@ -6850,12 +6807,12 @@ dict_T *get_buffer_info(buf_T *buf) /// /// @note Unlike tv_get_lnum(), this one supports only "$" special string. /// -/// @param[in] tv Object to get value from. Is expected to be a number or +/// @param[in] tv Object to get value from. Is expected to be a number or /// a special string "$". -/// @param[in] buf Buffer to take last line number from in case tv is "$". May -/// be NULL, in this case "$" results in zero return. +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. /// -/// @return Line number or 0 in case of error. +/// @return Line number or 0 in case of error. linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -6891,8 +6848,8 @@ void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) } } -/// Returns information (variables, options, etc.) about a tab page -/// as a dictionary. +/// @return information (variables, options, etc.) about a tab page +/// as a dictionary. dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { dict_T *const dict = tv_dict_alloc(); @@ -6911,7 +6868,7 @@ dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) return dict; } -/// Returns information about a window as a dictionary. +/// @return information about a window as a dictionary. dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { dict_T *const dict = tv_dict_alloc(); @@ -7064,12 +7021,10 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) } } -/* - * This function is used by f_input() and f_inputdialog() functions. The third - * argument to f_input() specifies the type of completion to use at the - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ +/// This function is used by f_input() and f_inputdialog() functions. The third +/// argument to f_input() specifies the type of completion to use at the +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, const bool secret) FUNC_ATTR_NONNULL_ALL @@ -7201,10 +7156,10 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const /// Turn a dictionary into a list /// -/// @param[in] tv Dictionary to convert. Is checked for actually being -/// a dictionary, will give an error if not. -/// @param[out] rettv Location where result will be saved. -/// @param[in] what What to save in rettv. +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) { if (tv->v_type != VAR_DICT) { @@ -7257,7 +7212,7 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha /// @param[out] cmd Returns the command or executable name. /// @param[out] executable Returns `false` if argv[0] is not executable. /// -/// @returns Result of `shell_build_argv()` if `cmd_tv` is a String. +/// @return Result of `shell_build_argv()` if `cmd_tv` is a String. /// Else, string values of `cmd_tv` copied to a (char **) list with /// argv[0] resolved to full path ($PATHEXT-resolved on Windows). char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) @@ -7321,10 +7276,10 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) /// Fill a dictionary with all applicable maparg() like dictionaries /// -/// @param dict The dictionary to be filled -/// @param mp The maphash that contains the mapping information -/// @param buffer_value The "buffer" value -/// @param compatible True for compatible with old maparg() dict +/// @param dict The dictionary to be filled +/// @param mp The maphash that contains the mapping information +/// @param buffer_value The "buffer" value +/// @param compatible True for compatible with old maparg() dict void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value, bool compatible) FUNC_ATTR_NONNULL_ALL @@ -7621,7 +7576,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) return list; } -// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. +/// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist) { proftime_T wait_time; @@ -7886,7 +7841,7 @@ void add_timer_info_all(typval_T *rettv) }) } -// invoked on the main loop +/// invoked on the main loop void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; @@ -7972,8 +7927,8 @@ void timer_stop(timer_T *timer) time_watcher_close(&timer->tw, timer_close_cb); } -// This will be run on the main loop after the last timer_due_cb, so at this -// point it is safe to free the callback. +/// This will be run on the main loop after the last timer_due_cb, so at this +/// point it is safe to free the callback. static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; @@ -8235,8 +8190,10 @@ int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) } /// Convert the specified character index of line 'lnum' in buffer 'buf' to a -/// byte index. Works only for loaded buffers. Returns -1 on failure. +/// byte index. Works only for loaded buffers. /// The index of the first byte and the first character is zero. +/// +/// @return -1 on failure. int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) { if (buf == NULL || buf->b_ml.ml_mfp == NULL) { @@ -8400,8 +8357,9 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret /// When "fnump" is NULL there is no file number, only 3 items. /// Note that the column is passed on as-is, the caller may want to decrement /// it to use 1 for the first column. -/// Return FAIL when conversion is not possible, doesn't check the position for -/// validity. +/// +/// @return FAIL when conversion is not possible, doesn't check the position for +/// validity. int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) { list_T *l; @@ -8463,11 +8421,10 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c return OK; } -/* - * Get the length of an environment variable name. - * Advance "arg" to the first character after the name. - * Return 0 for error. - */ +/// Get the length of an environment variable name. +/// Advance "arg" to the first character after the name. +/// +/// @return 0 for error. static int get_env_len(const char_u **arg) { int len; @@ -8484,9 +8441,11 @@ static int get_env_len(const char_u **arg) return len; } -// Get the length of the name of a function or internal variable. -// "arg" is advanced to the first non-white character after the name. -// Return 0 if something is wrong. +/// Get the length of the name of a function or internal variable. +/// +/// @param arg is advanced to the first non-white character after the name. +/// +/// @return 0 if something is wrong. int get_id_len(const char **const arg) { int len; @@ -8514,15 +8473,15 @@ int get_id_len(const char **const arg) return len; } -/* - * Get the length of the name of a variable or function. - * Only the name is recognized, does not handle ".key" or "[idx]". - * "arg" is advanced to the first non-white character after the name. - * Return -1 if curly braces expansion failed. - * Return 0 if something else is wrong. - * If the name contains 'magic' {}'s, expand them and return the - * expanded name in an allocated string via 'alias' - caller must free. - */ +/// Get the length of the name of a variable or function. +/// Only the name is recognized, does not handle ".key" or "[idx]". +/// +/// @param arg is advanced to the first non-white character after the name. +/// If the name contains 'magic' {}'s, expand them and return the +/// expanded name in an allocated string via 'alias' - caller must free. +/// +/// @return -1 if curly braces expansion failed or +/// 0 if something else is wrong. int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose) { int len; @@ -8579,12 +8538,15 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo return len; } -// Find the end of a variable or function name, taking care of magic braces. -// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the -// start and end of the first magic braces item. -// "flags" can have FNE_INCL_BR and FNE_CHECK_START. -// Return a pointer to just after the name. Equal to "arg" if there is no -// valid name. +/// Find the end of a variable or function name, taking care of magic braces. +/// +/// @param expr_start if not NULL, then `expr_start` and `expr_end` are set to the +/// start and end of the first magic braces item. +/// +/// @param flags can have FNE_INCL_BR and FNE_CHECK_START. +/// +/// @return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const char_u **expr_end, int flags) { @@ -8662,19 +8624,17 @@ const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const return p; } -/* - * Expands out the 'magic' {}'s in a variable/function name. - * Note that this can call itself recursively, to deal with - * constructs like foo{bar}{baz}{bam} - * The four pointer arguments point to "foo{expre}ss{ion}bar" - * "in_start" ^ - * "expr_start" ^ - * "expr_end" ^ - * "in_end" ^ - * - * Returns a new allocated string, which the caller must free. - * Returns NULL for failure. - */ +/// Expands out the 'magic' {}'s in a variable/function name. +/// Note that this can call itself recursively, to deal with +/// constructs like foo{bar}{baz}{bam} +/// The four pointer arguments point to "foo{expre}ss{ion}bar" +/// "in_start" ^ +/// "expr_start" ^ +/// "expr_end" ^ +/// "in_end" ^ +/// +/// @return a new allocated string, which the caller must free or +/// NULL for failure. static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) { @@ -8721,44 +8681,36 @@ static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, ch return retval; } -/* - * Return TRUE if character "c" can be used in a variable or function name. - * Does not include '{' or '}' for magic braces. - */ +/// @return TRUE if character "c" can be used in a variable or function name. +/// Does not include '{' or '}' for magic braces. int eval_isnamec(int c) { return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; } -/* - * Return TRUE if character "c" can be used as the first character in a - * variable or function name (excluding '{' and '}'). - */ +/// @return TRUE if character "c" can be used as the first character in a +/// variable or function name (excluding '{' and '}'). int eval_isnamec1(int c) { return ASCII_ISALPHA(c) || c == '_'; } -/* - * Get number v: variable value. - */ +/// Get number v: variable value. varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE { return vimvars[idx].vv_nr; } -// Get string v: variable value. Uses a static buffer, can only be used once. -// If the String variable has never been set, return an empty string. -// Never returns NULL; +/// Get string v: variable value. Uses a static buffer, can only be used once. +/// If the String variable has never been set, return an empty string. +/// Never returns NULL; char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } -/* - * Get List v: variable value. Caller must take care of reference count when - * needed. - */ +/// Get List v: variable value. Caller must take care of reference count when +/// needed. list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE { return vimvars[idx].vv_list; @@ -8771,9 +8723,7 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE return vimvars[idx].vv_dict; } -/* - * Set v:char to character "c". - */ +/// Set v:char to character "c". void set_vim_var_char(int c) { char buf[MB_MAXBYTES + 1]; @@ -8782,10 +8732,9 @@ void set_vim_var_char(int c) set_vim_var_string(VV_CHAR, buf, -1); } -/* - * Set v:count to "count" and v:count1 to "count1". - * When "set_prevcount" is TRUE first set v:prevcount from v:count. - */ +/// Set v:count to "count" and v:count1 to "count1". +/// +/// @param set_prevcount if TRUE, first set v:prevcount from v:count. void set_vcount(long count, long count1, int set_prevcount) { if (set_prevcount) { @@ -8893,9 +8842,7 @@ void set_argv_var(char **argv, int argc) set_vim_var_list(VV_ARGV, l); } -/* - * Set v:register if needed. - */ +/// Set v:register if needed. void set_reg_var(int c) { char regname; @@ -8911,12 +8858,10 @@ void set_reg_var(int c) } } -/* - * Get or set v:exception. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:exception! Does not - * take care of memory allocations. - */ +/// Get or set v:exception. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:exception! Does not +/// take care of memory allocations. char_u *v_exception(char_u *oldval) { if (oldval == NULL) { @@ -8927,12 +8872,10 @@ char_u *v_exception(char_u *oldval) return NULL; } -/* - * Get or set v:throwpoint. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:throwpoint! Does not - * take care of memory allocations. - */ +/// Get or set v:throwpoint. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:throwpoint! Does not +/// take care of memory allocations. char_u *v_throwpoint(char_u *oldval) { if (oldval == NULL) { @@ -8943,12 +8886,10 @@ char_u *v_throwpoint(char_u *oldval) return NULL; } -/* - * Set v:cmdarg. - * If "eap" != NULL, use "eap" to generate the value and return the old value. - * If "oldarg" != NULL, restore the value to "oldarg" and return NULL. - * Must always be called in pairs! - */ +/// Set v:cmdarg. +/// If "eap" != NULL, use "eap" to generate the value and return the old value. +/// If "oldarg" != NULL, restore the value to "oldarg" and return NULL. +/// Must always be called in pairs! char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) { char_u *oldval = vimvars[VV_CMDARG].vv_str; @@ -9213,11 +9154,12 @@ void set_selfdict(typval_T *const rettv, dict_T *const selfdict) make_partial(selfdict, rettv); } -// Find variable "name" in the list of variables. -// Return a pointer to it if found, NULL if not found. -// Careful: "a:0" variables don't have a name. -// When "htp" is not NULL we are writing to the variable, set "htp" to the -// hashtab_T used. +/// Find variable "name" in the list of variables. +/// Careful: "a:0" variables don't have a name. +/// When "htp" is not NULL we are writing to the variable, set "htp" to the +/// hashtab_T used. +/// +/// @return a pointer to it if found, NULL if not found. dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { @@ -9415,11 +9357,10 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var return find_var_ht_dict(name, name_len, varname, &d); } -/* - * Get the string value of a (global/local) variable. - * Note: see tv_get_string() for how long the pointer remains valid. - * Returns NULL when it doesn't exist. - */ +/// @return the string value of a (global/local) variable or +/// NULL when it doesn't exist. +/// +/// @see tv_get_string() for how long the pointer remains valid. char_u *get_var_value(const char *const name) { dictitem_T *v; @@ -9431,10 +9372,8 @@ char_u *get_var_value(const char *const name) return (char_u *)tv_get_string(&v->di_tv); } -/* - * Allocate a new hashtab for a sourced script. It will be used while - * sourcing this script and when executing functions defined in the script. - */ +/// Allocate a new hashtab for a sourced script. It will be used while +/// sourcing this script and when executing functions defined in the script. void new_script_vars(scid_T id) { hashtab_T *ht; @@ -9462,10 +9401,8 @@ void new_script_vars(scid_T id) } } -/* - * Initialize dictionary "dict" as a scope and set variable "dict_var" to - * point to it. - */ +/// Initialize dictionary "dict" as a scope and set variable "dict_var" to +/// point to it. void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); @@ -9481,9 +9418,7 @@ void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) QUEUE_INIT(&dict->watchers); } -/* - * Unreference a dictionary initialized by init_var_dict(). - */ +/// Unreference a dictionary initialized by init_var_dict(). void unref_var_dict(dict_T *dict) { /* Now the dict needs to be freed if no one else is using it, go back to @@ -9492,19 +9427,15 @@ void unref_var_dict(dict_T *dict) tv_dict_unref(dict); } -/* - * Clean up a list of internal variables. - * Frees all allocated variables and the value they contain. - * Clears hashtab "ht", does not free it. - */ +/// Clean up a list of internal variables. +/// Frees all allocated variables and the value they contain. +/// Clears hashtab "ht", does not free it. void vars_clear(hashtab_T *ht) { vars_clear_ext(ht, TRUE); } -/* - * Like vars_clear(), but only free the value if "free_val" is TRUE. - */ +/// Like vars_clear(), but only free the value if "free_val" is TRUE. void vars_clear_ext(hashtab_T *ht, int free_val) { int todo; @@ -9533,10 +9464,8 @@ void vars_clear_ext(hashtab_T *ht, int free_val) ht->ht_used = 0; } -/* - * Delete a variable from hashtab "ht" at item "hi". - * Clear the variable value and free the dictitem. - */ +/// Delete a variable from hashtab "ht" at item "hi". +/// Clear the variable value and free the dictitem. static void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = TV_DICT_HI2DI(hi); @@ -9546,9 +9475,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) xfree(di); } -/* - * List the value of one internal variable. - */ +/// List the value of one internal variable. static void list_one_var(dictitem_T *v, const char *prefix, int *first) { char *const s = encode_tv2echo(&v->di_tv, NULL); @@ -9981,11 +9908,9 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c return ret; } -/* - * ":echo expr1 ..." print each argument separated with a space, add a - * newline at the end. - * ":echon expr1 ..." print each argument plain. - */ +/// ":echo expr1 ..." print each argument separated with a space, add a +/// newline at the end. +/// ":echon expr1 ..." print each argument plain. void ex_echo(exarg_T *eap) { char_u *arg = eap->arg; @@ -10059,21 +9984,17 @@ void ex_echo(exarg_T *eap) } } -/* - * ":echohl {name}". - */ +/// ":echohl {name}". void ex_echohl(exarg_T *eap) { echo_attr = syn_name2attr(eap->arg); } -/* - * ":execute expr1 ..." execute the result of an expression. - * ":echomsg expr1 ..." Print a message - * ":echoerr expr1 ..." Print an error - * Each gets spaces around each argument and a newline at the end for - * echo commands - */ +/// ":execute expr1 ..." execute the result of an expression. +/// ":echomsg expr1 ..." Print a message +/// ":echoerr expr1 ..." Print an error +/// Each gets spaces around each argument and a newline at the end for +/// echo commands void ex_execute(exarg_T *eap) { char_u *arg = eap->arg; @@ -10150,12 +10071,12 @@ void ex_execute(exarg_T *eap) eap->nextcmd = check_nextcmd(arg); } -/* - * Skip over the name of an option: "&option", "&g:option" or "&l:option". - * "arg" points to the "&" or '+' when called, to "option" when returning. - * Returns NULL when no option name found. Otherwise pointer to the char - * after the option name. - */ +/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// +/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// +/// @return NULL when no option name found. Otherwise pointer to the char +/// after the option name. static const char *find_option_end(const char **const arg, int *const opt_flags) { const char *p = *arg; @@ -10218,9 +10139,7 @@ void func_do_profile(ufunc_T *fp) fp->uf_profiling = TRUE; } -/* - * Dump the profiling results for all functions in file "fd". - */ +/// Dump the profiling results for all functions in file "fd". void func_dump_profile(FILE *fd) { hashitem_T *hi; @@ -10340,9 +10259,7 @@ static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *s } } -/* - * Compare function for total time sorting. - */ +/// Compare function for total time sorting. static int prof_total_cmp(const void *s1, const void *s2) { ufunc_T *p1 = *(ufunc_T **)s1; @@ -10350,9 +10267,7 @@ static int prof_total_cmp(const void *s1, const void *s2) return profile_cmp(p1->uf_tm_total, p2->uf_tm_total); } -/* - * Compare function for self time sorting. - */ +/// Compare function for self time sorting. static int prof_self_cmp(const void *s1, const void *s2) { ufunc_T *p1 = *(ufunc_T **)s1; @@ -10434,12 +10349,10 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r return ret; } -/* - * Called when starting to read a function line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ +/// Called when starting to read a function line. +/// "sourcing_lnum" must be correct! +/// When skipping lines it may not actually be executed, but we won't find out +/// until later and we need to store the time now. void func_line_start(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10459,9 +10372,7 @@ void func_line_start(void *cookie) } } -/* - * Called when actually executing a function line. - */ +/// Called when actually executing a function line. void func_line_exec(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10472,9 +10383,7 @@ void func_line_exec(void *cookie) } } -/* - * Called when done with a function line. - */ +/// Called when done with a function line. void func_line_end(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10609,10 +10518,8 @@ int store_session_globals(FILE *fd) return OK; } -/* - * Display script name where an item was last set. - * Should only be invoked when 'verbose' is non-zero. - */ +/// Display script name where an item was last set. +/// Should only be invoked when 'verbose' is non-zero. void last_set_msg(sctx_T script_ctx) { const LastSet last_set = (LastSet){ @@ -10965,7 +10872,8 @@ repeat: /// Perform a substitution on "str" with pattern "pat" and substitute "sub". /// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. /// "flags" can be "g" to do a global substitute. -/// Returns an allocated string, NULL for error. +/// +/// @return an allocated string, NULL for error. char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, char_u *flags) { int sublen; @@ -11301,7 +11209,7 @@ void invoke_prompt_callback(void) tv_clear(&rettv); } -// Return true When the interrupt callback was invoked. +/// @return true when the interrupt callback was invoked. bool invoke_prompt_interrupt(void) { typval_T rettv; -- cgit From 2ea16f73547391d014078b446d032a37c3585efa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 15 Mar 2022 08:03:18 +0800 Subject: vim-patch:8.2.4566: check for existing buffer in session file may not work Problem: Check for existing buffer in session file does not work for files in the home directory. Solution: Use fnamemodify(). (James Cherti, closes vim/vim#9945) Add a test. https://github.com/vim/vim/commit/7d42840033aedf36389208b62e28b4e0b251c199 --- src/nvim/ex_session.c | 2 +- src/nvim/testdir/test_mksession.vim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index ca07174543..e398c1ee64 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -365,7 +365,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr // edit that buffer, to not lose folding information (:edit resets // folds in other buffers) if (fprintf(fd, - "if bufexists(\"%s\") | buffer %s | else | edit %s | endif\n" + "if bufexists(fnamemodify(\"%s\", \":p\")) | buffer %s | else | edit %s | endif\n" // Fixup :terminal buffer name. #7836 "if &buftype ==# 'terminal'\n" " silent file %s\n" diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index a8e50af510..798cb9e54f 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -219,6 +219,7 @@ func Test_mksession_one_buffer_two_windows() let count1 = 0 let count2 = 0 let count2buf = 0 + let bufexists = 0 for line in lines if line =~ 'edit \f*Xtest1$' let count1 += 1 @@ -229,10 +230,14 @@ func Test_mksession_one_buffer_two_windows() if line =~ 'buffer \f\{-}Xtest2' let count2buf += 1 endif + if line =~ 'bufexists(fnamemodify(.*, ":p")' + let bufexists += 1 + endif endfor call assert_equal(1, count1, 'Xtest1 count') call assert_equal(2, count2, 'Xtest2 count') call assert_equal(2, count2buf, 'Xtest2 buffer count') + call assert_equal(2, bufexists) close bwipe! -- cgit From 6906c5759da28f1aaac33c479035b3d06a1699d0 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 13 Mar 2022 18:01:44 +0000 Subject: vim-patch:8.2.4555: getmousepos() returns the wrong column Problem: getmousepos() returns the wrong column. (Ernie Rael) Solution: Limit to the text size, not the number of bytes. https://github.com/vim/vim/commit/986b0fd0c550d9834a3cc45dd87555c13152c391 test_setmouse is N/A; adjust test for Nvim. N/A patches for version.c: vim-patch:8.2.4569: Coverity warning for not using a return value Problem: Coverity warning for not using a return value. Solution: Add "(void)". https://github.com/vim/vim/commit/977525fea662b7f37ad0e052894c1f62b5b03269 --- src/nvim/eval/funcs.c | 7 ++----- src/nvim/testdir/test_functions.vim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index b74f9759ac..3bbb4d557e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3753,18 +3753,15 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { - char_u *p; int count; mouse_comp_pos(wp, &row, &col, &line); - // limit to text length plus one - p = ml_get_buf(wp->w_buffer, line, false); - count = (int)STRLEN(p); + // limit to text size plus one + count = linetabsize(ml_get_buf(wp->w_buffer, line, false)); if (col > count) { col = count; } - column = col + 1; } } diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 994d74601a..25e2301de3 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1762,6 +1762,36 @@ func Test_getcurpos_setpos() call assert_equal([0, 0, 0, 0, 0], getcurpos(1999)) endfunc +func Test_getmousepos() + enew! + call setline(1, "\t\t\t1234") + " call test_setmouse(1, 25) + call nvim_input_mouse('left', 'press', '', 0, 0, 24) + call getchar() " wait for and consume the mouse press + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 25, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 25, + \ line: 1, + \ column: 25, + \ }, getmousepos()) + " call test_setmouse(1, 50) + call nvim_input_mouse('left', 'press', '', 0, 0, 49) + call getchar() " wait for and consume the mouse press + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 50, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 50, + \ line: 1, + \ column: 29, + \ }, getmousepos()) + bwipe! +endfunc + func HasDefault(msg = 'msg') return a:msg endfunc -- cgit From 4a8b6bde019ea63a7ad74fbf7defc0156497f2e5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sun, 13 Mar 2022 18:32:10 +0000 Subject: vim-patch:8.2.4559: getmousepos() returns the screen column Problem: getmousepos() returns the screen column. (Ernie Rael) Solution: Return the text column, as documented. https://github.com/vim/vim/commit/533870a98501fac2b51ef4bc489fac3a055a41a9 Re-introduce vcol2col, which was removed in 71b1f4e for being unused. Move it to mouse.c (like in v8.1.2062, which hasn't been ported yet). --- src/nvim/eval/funcs.c | 14 ++++---------- src/nvim/mouse.c | 16 ++++++++++++++++ src/nvim/testdir/test_functions.vim | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 3bbb4d557e..edb254e15f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3734,7 +3734,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T winid = 0; varnumber_T winrow = 0; varnumber_T wincol = 0; - linenr_T line = 0; + linenr_T lnum = 0; varnumber_T column = 0; tv_dict_alloc_ret(rettv); @@ -3753,14 +3753,8 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { - int count; - - mouse_comp_pos(wp, &row, &col, &line); - - // limit to text size plus one - count = linetabsize(ml_get_buf(wp->w_buffer, line, false)); - if (col > count) { - col = count; + if (!mouse_comp_pos(wp, &row, &col, &lnum)) { + col = vcol2col(wp, lnum, col); } column = col + 1; } @@ -3769,7 +3763,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(d, S_LEN("winid"), winid); tv_dict_add_nr(d, S_LEN("winrow"), winrow); tv_dict_add_nr(d, S_LEN("wincol"), wincol); - tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)line); + tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)lnum); tv_dict_add_nr(d, S_LEN("column"), column); } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 5d007fb173..6b27ce9d7b 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -535,6 +535,22 @@ static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) return NULL; } +/// Convert a virtual (screen) column to a character column. +/// The first column is one. +colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + // try to advance to the specified column + char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false); + char_u *const line = ptr; + colnr_T count = 0; + while (count < vcol && *ptr != NUL) { + count += win_lbr_chartabsize(wp, line, ptr, count, NULL); + MB_PTR_ADV(ptr); + } + return (colnr_T)(ptr - line); +} + /// Set UI mouse depending on current mode and 'mouse'. /// /// Emits mouse_on/mouse_off UI event (unless 'mouse' is empty). diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 25e2301de3..d83606882c 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1765,6 +1765,18 @@ endfunc func Test_getmousepos() enew! call setline(1, "\t\t\t1234") + " call test_setmouse(1, 1) + call nvim_input_mouse('left', 'press', '', 0, 0, 0) + call getchar() " wait for and consume the mouse press + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 1, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 1, + \ line: 1, + \ column: 1, + \ }, getmousepos()) " call test_setmouse(1, 25) call nvim_input_mouse('left', 'press', '', 0, 0, 24) call getchar() " wait for and consume the mouse press @@ -1775,7 +1787,7 @@ func Test_getmousepos() \ winrow: 1, \ wincol: 25, \ line: 1, - \ column: 25, + \ column: 4, \ }, getmousepos()) " call test_setmouse(1, 50) call nvim_input_mouse('left', 'press', '', 0, 0, 49) @@ -1787,7 +1799,7 @@ func Test_getmousepos() \ winrow: 1, \ wincol: 50, \ line: 1, - \ column: 29, + \ column: 8, \ }, getmousepos()) bwipe! endfunc -- cgit From 716df377b4bbf1ec64c1115ccc652b5b24797869 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Tue, 15 Mar 2022 10:25:06 +0000 Subject: vim-patch:8.2.4568: getmousepos() does not compute the column below the last line Problem: getmousepos() does not compute the column below the last line. Solution: Also compute the column when the mouse is below the last line. (Sean Dewar, closes vim/vim#9946) https://github.com/vim/vim/commit/10792feebd237aee89270669e509e85cafdfac60 test_setmouse is N/A. --- src/nvim/eval/funcs.c | 5 ++--- src/nvim/testdir/test_functions.vim | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index edb254e15f..738ed7f85e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3753,9 +3753,8 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { - if (!mouse_comp_pos(wp, &row, &col, &lnum)) { - col = vcol2col(wp, lnum, col); - } + (void)mouse_comp_pos(wp, &row, &col, &lnum); + col = vcol2col(wp, lnum, col); column = col + 1; } } diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index d83606882c..c2b5653a29 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1801,6 +1801,33 @@ func Test_getmousepos() \ line: 1, \ column: 8, \ }, getmousepos()) + + " If the mouse is positioned past the last buffer line, "line" and "column" + " should act like it's positioned on the last buffer line. + " call test_setmouse(2, 25) + call nvim_input_mouse('left', 'press', '', 0, 1, 24) + call getchar() " wait for and consume the mouse press + call assert_equal(#{ + \ screenrow: 2, + \ screencol: 25, + \ winid: win_getid(), + \ winrow: 2, + \ wincol: 25, + \ line: 1, + \ column: 4, + \ }, getmousepos()) + " call test_setmouse(2, 50) + call nvim_input_mouse('left', 'press', '', 0, 1, 49) + call getchar() " wait for and consume the mouse press + call assert_equal(#{ + \ screenrow: 2, + \ screencol: 50, + \ winid: win_getid(), + \ winrow: 2, + \ wincol: 50, + \ line: 1, + \ column: 8, + \ }, getmousepos()) bwipe! endfunc -- cgit From 2aa473593f1bb7c7ae35fd2030f1f8c37500f289 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor: convert function comments to doxygen format --- src/nvim/ex_eval.c | 315 ++++++++++++++++++++++------------------------------- 1 file changed, 128 insertions(+), 187 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 25b6aa7d8a..3c7c635d98 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -88,27 +88,26 @@ */ static int cause_abort = FALSE; -// Return true when immediately aborting on error, or when an interrupt -// occurred or an exception was thrown but not caught. Use for ":{range}call" -// to check whether an aborted function that does not handle a range itself -// should be called again for the next line in the range. Also used for -// cancelling expression evaluation after a function call caused an immediate -// abort. Note that the first emsg() call temporarily resets "force_abort" -// until the throw point for error messages has been reached. That is, during -// cancellation of an expression evaluation after an aborting function call or -// due to a parsing error, aborting() always returns the same value. -// "got_int" is also set by calling interrupt(). +/// @return true when immediately aborting on error, or when an interrupt +/// occurred or an exception was thrown but not caught. +/// +/// Use for ":{range}call" to check whether an aborted function that does not +/// handle a range itself should be called again for the next line in the range. +/// Also used for cancelling expression evaluation after a function call caused +/// an immediate abort. Note that the first emsg() call temporarily resets +/// "force_abort" until the throw point for error messages has been reached. +/// That is, during cancellation of an expression evaluation after an aborting +/// function call or due to a parsing error, aborting() always returns the same +/// value. "got_int" is also set by calling interrupt(). int aborting(void) { return (did_emsg && force_abort) || got_int || current_exception; } -/* - * The value of "force_abort" is temporarily reset by the first emsg() call - * during an expression evaluation, and "cause_abort" is used instead. It might - * be necessary to restore "force_abort" even before the throw point for the - * error message has been reached. update_force_abort() should be called then. - */ +/// The value of "force_abort" is temporarily reset by the first emsg() call +/// during an expression evaluation, and "cause_abort" is used instead. It might +/// be necessary to restore "force_abort" even before the throw point for the +/// error message has been reached. update_force_abort() should be called then. void update_force_abort(void) { if (cause_abort) { @@ -116,23 +115,19 @@ void update_force_abort(void) } } -/* - * Return TRUE if a command with a subcommand resulting in "retcode" should - * abort the script processing. Can be used to suppress an autocommand after - * execution of a failing subcommand as long as the error message has not been - * displayed and actually caused the abortion. - */ +/// @return TRUE if a command with a subcommand resulting in "retcode" should +/// abort the script processing. Can be used to suppress an autocommand after +/// execution of a failing subcommand as long as the error message has not been +/// displayed and actually caused the abortion. int should_abort(int retcode) { return (retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting(); } -/* - * Return TRUE if a function with the "abort" flag should not be considered - * ended on an error. This means that parsing commands is continued in order - * to find finally clauses to be executed, and that some errors in skipped - * commands are still reported. - */ +/// @return TRUE if a function with the "abort" flag should not be considered +/// ended on an error. This means that parsing commands is continued in order +/// to find finally clauses to be executed, and that some errors in skipped +/// commands are still reported. int aborted_in_try(void) { // This function is only called after an error. In this case, "force_abort" @@ -140,13 +135,15 @@ int aborted_in_try(void) return force_abort; } -// cause_errthrow(): Cause a throw of an error exception if appropriate. -// Return true if the error message should not be displayed by emsg(). -// Sets "ignore", if the emsg() call should be ignored completely. -// -// When several messages appear in the same command, the first is usually the -// most specific one and used as the exception value. The "severe" flag can be -// set to true, if a later but severer message should be used instead. +/// cause_errthrow(): Cause a throw of an error exception if appropriate. +/// +/// @return true if the error message should not be displayed by emsg(). +/// +/// Sets "ignore", if the emsg() call should be ignored completely. +/// +/// When several messages appear in the same command, the first is usually the +/// most specific one and used as the exception value. The "severe" flag can be +/// set to true, if a later but severer message should be used instead. bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore) FUNC_ATTR_NONNULL_ALL { @@ -279,9 +276,7 @@ bool cause_errthrow(const char_u *mesg, bool severe, bool *ignore) } } -/* - * Free a "msg_list" and the messages it contains. - */ +/// Free a "msg_list" and the messages it contains. static void free_msglist(struct msglist *l) { struct msglist *messages, *next; @@ -295,21 +290,17 @@ static void free_msglist(struct msglist *l) } } -/* - * Free global "*msg_list" and the messages it contains, then set "*msg_list" - * to NULL. - */ +/// Free global "*msg_list" and the messages it contains, then set "*msg_list" +/// to NULL. void free_global_msglist(void) { free_msglist(*msg_list); *msg_list = NULL; } -/* - * Throw the message specified in the call to cause_errthrow() above as an - * error exception. If cstack is NULL, postpone the throw until do_cmdline() - * has returned (see do_one_cmd()). - */ +/// Throw the message specified in the call to cause_errthrow() above as an +/// error exception. If cstack is NULL, postpone the throw until do_cmdline() +/// has returned (see do_one_cmd()). void do_errthrow(cstack_T *cstack, char_u *cmdname) { /* @@ -339,11 +330,11 @@ void do_errthrow(cstack_T *cstack, char_u *cmdname) *msg_list = NULL; } -/* - * do_intthrow(): Replace the current exception by an interrupt or interrupt - * exception if appropriate. Return TRUE if the current exception is discarded, - * FALSE otherwise. - */ +/// do_intthrow(): Replace the current exception by an interrupt or interrupt +/// exception if appropriate. +/// +/// @return TRUE if the current exception is discarded or, +/// FALSE otherwise. int do_intthrow(cstack_T *cstack) { // If no interrupt occurred or no try conditional is active and no exception @@ -386,7 +377,7 @@ int do_intthrow(cstack_T *cstack) return true; } -// Get an exception message that is to be stored in current_exception->value. +/// Get an exception message that is to be stored in current_exception->value. char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int *should_free) { char *ret, *mesg; @@ -445,10 +436,12 @@ char *get_exception_string(void *value, except_type_T type, char_u *cmdname, int } -// Throw a new exception. Return FAIL when out of memory or it was tried to -// throw an illegal user exception. "value" is the exception string for a -// user or interrupt exception, or points to a message list in case of an -// error exception. +/// Throw a new exception. "value" is the exception string for a +/// user or interrupt exception, or points to a message list in case of an +/// error exception. +/// +/// @return FAIL when out of memory or it was tried to throw an illegal user +/// exception. static int throw_exception(void *value, except_type_T type, char_u *cmdname) { except_T *excp; @@ -524,10 +517,8 @@ fail: return FAIL; } -/* - * Discard an exception. "was_finished" is set when the exception has been - * caught and the catch clause has been ended normally. - */ +/// Discard an exception. "was_finished" is set when the exception has been +/// caught and the catch clause has been ended normally. static void discard_exception(except_T *excp, bool was_finished) { char_u *saved_IObuff; @@ -579,9 +570,7 @@ static void discard_exception(except_T *excp, bool was_finished) xfree(excp); } -/* - * Discard the exception currently being thrown. - */ +/// Discard the exception currently being thrown. void discard_current_exception(void) { if (current_exception != NULL) { @@ -592,9 +581,7 @@ void discard_current_exception(void) need_rethrow = false; } -/* - * Put an exception on the caught stack. - */ +/// Put an exception on the caught stack. static void catch_exception(except_T *excp) { excp->caught = caught_stack; @@ -640,9 +627,7 @@ static void catch_exception(except_T *excp) } } -/* - * Remove an exception from the caught stack. - */ +/// Remove an exception from the caught stack. static void finish_exception(except_T *excp) { if (excp != caught_stack) { @@ -682,13 +667,11 @@ static void finish_exception(except_T *excp) #define RP_RESUME 1 #define RP_DISCARD 2 -/* - * Report information about something pending in a finally clause if required by - * the 'verbose' option or when debugging. "action" tells whether something is - * made pending or something pending is resumed or discarded. "pending" tells - * what is pending. "value" specifies the return value for a pending ":return" - * or the exception value for a pending exception. - */ +/// Report information about something pending in a finally clause if required by +/// the 'verbose' option or when debugging. "action" tells whether something is +/// made pending or something pending is resumed or discarded. "pending" tells +/// what is pending. "value" specifies the return value for a pending ":return" +/// or the exception value for a pending exception. static void report_pending(int action, int pending, void *value) { char *mesg; @@ -764,10 +747,8 @@ static void report_pending(int action, int pending, void *value) } } -/* - * If something is made pending in a finally clause, report it if required by - * the 'verbose' option or when debugging. - */ +/// If something is made pending in a finally clause, report it if required by +/// the 'verbose' option or when debugging. void report_make_pending(int pending, void *value) { if (p_verbose >= 14 || debug_break_level > 0) { @@ -781,10 +762,8 @@ void report_make_pending(int pending, void *value) } } -/* - * If something pending in a finally clause is resumed at the ":endtry", report - * it if required by the 'verbose' option or when debugging. - */ +/// If something pending in a finally clause is resumed at the ":endtry", report +/// it if required by the 'verbose' option or when debugging. void report_resume_pending(int pending, void *value) { if (p_verbose >= 14 || debug_break_level > 0) { @@ -798,10 +777,8 @@ void report_resume_pending(int pending, void *value) } } -/* - * If something pending in a finally clause is discarded, report it if required - * by the 'verbose' option or when debugging. - */ +/// If something pending in a finally clause is discarded, report it if required +/// by the 'verbose' option or when debugging. void report_discard_pending(int pending, void *value) { if (p_verbose >= 14 || debug_break_level > 0) { @@ -815,7 +792,7 @@ void report_discard_pending(int pending, void *value) } } -// ":eval". +/// Handle ":eval". void ex_eval(exarg_T *eap) { typval_T tv; @@ -825,9 +802,7 @@ void ex_eval(exarg_T *eap) } } -/* - * ":if". - */ +/// Handle ":if". void ex_if(exarg_T *eap) { int skip; @@ -856,9 +831,7 @@ void ex_if(exarg_T *eap) } } -/* - * ":endif". - */ +/// Handle ":endif". void ex_endif(exarg_T *eap) { did_endif = true; @@ -883,9 +856,7 @@ void ex_endif(exarg_T *eap) } } -/* - * ":else" and ":elseif". - */ +/// Handle ":else" and ":elseif". void ex_else(exarg_T *eap) { int result; @@ -958,9 +929,7 @@ void ex_else(exarg_T *eap) } } -/* - * Handle ":while" and ":for". - */ +/// Handle ":while" and ":for". void ex_while(exarg_T *eap) { bool error; @@ -1041,9 +1010,7 @@ void ex_while(exarg_T *eap) } } -/* - * ":continue" - */ +/// Handle ":continue" void ex_continue(exarg_T *eap) { int idx; @@ -1075,9 +1042,7 @@ void ex_continue(exarg_T *eap) } } -/* - * ":break" - */ +/// Handle ":break" void ex_break(exarg_T *eap) { int idx; @@ -1098,9 +1063,7 @@ void ex_break(exarg_T *eap) } } -/* - * ":endwhile" and ":endfor" - */ +/// Handle ":endwhile" and ":endfor" void ex_endwhile(exarg_T *eap) { cstack_T *const cstack = eap->cstack; @@ -1175,9 +1138,7 @@ void ex_endwhile(exarg_T *eap) } -/* - * ":throw expr" - */ +/// Handle ":throw expr" void ex_throw(exarg_T *eap) { const char *arg = (const char *)eap->arg; @@ -1202,11 +1163,9 @@ void ex_throw(exarg_T *eap) } } -/* - * Throw the current exception through the specified cstack. Common routine - * for ":throw" (user exception) and error and interrupt exceptions. Also - * used for rethrowing an uncaught exception. - */ +/// Throw the current exception through the specified cstack. Common routine +/// for ":throw" (user exception) and error and interrupt exceptions. Also +/// used for rethrowing an uncaught exception. void do_throw(cstack_T *cstack) { int idx; @@ -1263,9 +1222,7 @@ void do_throw(cstack_T *cstack) } } -/* - * ":try" - */ +/// Handle ":try" void ex_try(exarg_T *eap) { int skip; @@ -1315,9 +1272,7 @@ void ex_try(exarg_T *eap) } } -/* - * ":catch /{pattern}/" and ":catch" - */ +/// Handle ":catch /{pattern}/" and ":catch" void ex_catch(exarg_T *eap) { int idx = 0; @@ -1471,9 +1426,7 @@ void ex_catch(exarg_T *eap) } } -/* - * ":finally" - */ +/// Handle ":finally" void ex_finally(exarg_T *eap) { int idx; @@ -1595,9 +1548,7 @@ void ex_finally(exarg_T *eap) } } -/* - * ":endtry" - */ +/// Handle ":endtry" void ex_endtry(exarg_T *eap) { int idx; @@ -1784,14 +1735,12 @@ void ex_endtry(exarg_T *eap) * error/interrupt/exception state. */ -/* - * This function works a bit like ex_finally() except that there was not - * actually an extra try block around the part that failed and an error or - * interrupt has not (yet) been converted to an exception. This function - * saves the error/interrupt/ exception state and prepares for the call to - * do_cmdline() that is going to be made for the cleanup autocommand - * execution. - */ +/// This function works a bit like ex_finally() except that there was not +/// actually an extra try block around the part that failed and an error or +/// interrupt has not (yet) been converted to an exception. This function +/// saves the error/interrupt/ exception state and prepares for the call to +/// do_cmdline() that is going to be made for the cleanup autocommand +/// execution. void enter_cleanup(cleanup_T *csp) { int pending = CSTP_NONE; @@ -1834,21 +1783,19 @@ void enter_cleanup(cleanup_T *csp) } } -/* - * See comment above enter_cleanup() for how this function is used. - * - * This function is a bit like ex_endtry() except that there was not actually - * an extra try block around the part that failed and an error or interrupt - * had not (yet) been converted to an exception when the cleanup autocommand - * sequence was invoked. - * - * This function has to be called with the address of the cleanup_T structure - * filled by enter_cleanup() as an argument; it restores the error/interrupt/ - * exception state saved by that function - except there was an aborting - * error, an interrupt or an uncaught exception during execution of the - * cleanup autocommands. In the latter case, the saved error/interrupt/ - * exception state is discarded. - */ +/// This function is a bit like ex_endtry() except that there was not actually +/// an extra try block around the part that failed and an error or interrupt +/// had not (yet) been converted to an exception when the cleanup autocommand +/// sequence was invoked. +/// +/// See comment above enter_cleanup() for how this function is used. +/// +/// This function has to be called with the address of the cleanup_T structure +/// filled by enter_cleanup() as an argument; it restores the error/interrupt/ +/// exception state saved by that function - except there was an aborting +/// error, an interrupt or an uncaught exception during execution of the +/// cleanup autocommands. In the latter case, the saved error/interrupt/ +/// exception state is discarded. void leave_cleanup(cleanup_T *csp) { int pending = csp->pending; @@ -1913,22 +1860,25 @@ void leave_cleanup(cleanup_T *csp) } -/* - * Make conditionals inactive and discard what's pending in finally clauses - * until the conditional type searched for or a try conditional not in its - * finally clause is reached. If this is in an active catch clause, finish - * the caught exception. - * Return the cstack index where the search stopped. - * Values used for "searched_cond" are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0, - * the latter meaning the innermost try conditional not in its finally clause. - * "inclusive" tells whether the conditional searched for should be made - * inactive itself (a try conditional not in its finally clause possibly find - * before is always made inactive). If "inclusive" is TRUE and - * "searched_cond" is CSF_TRY|CSF_SILENT, the saved former value of - * "emsg_silent", if reset when the try conditional finally reached was - * entered, is restored (used by ex_endtry()). This is normally done only - * when such a try conditional is left. - */ +/// Make conditionals inactive and discard what's pending in finally clauses +/// until the conditional type searched for or a try conditional not in its +/// finally clause is reached. If this is in an active catch clause, finish +/// the caught exception. +/// +/// +/// @param searched_cond Possible values are (CSF_WHILE | CSF_FOR) or CSF_TRY or 0, +/// the latter meaning the innermost try conditional not +/// in its finally clause. +/// @param inclusive tells whether the conditional searched for should be made +/// inactive itself (a try conditional not in its finally +/// clause possibly find before is always made inactive). +/// +/// If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT, the saved +/// former value of "emsg_silent", if reset when the try conditional finally +/// reached was entered, is restored (used by ex_endtry()). This is normally +/// done only when such a try conditional is left. +/// +/// @return the cstack index where the search stopped. int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) { int idx; @@ -2037,9 +1987,7 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) return idx; } -/* - * Return an appropriate error message for a missing endwhile/endfor/endif. - */ +/// @return an appropriate error message for a missing endwhile/endfor/endif. static char *get_end_emsg(cstack_T *cstack) { if (cstack->cs_flags[cstack->cs_idx] & CSF_WHILE) { @@ -2052,13 +2000,11 @@ static char *get_end_emsg(cstack_T *cstack) } -/* - * Rewind conditionals until index "idx" is reached. "cond_type" and - * "cond_level" specify a conditional type and the address of a level variable - * which is to be decremented with each skipped conditional of the specified - * type. - * Also free "for info" structures where needed. - */ +/// Rewind conditionals until index "idx" is reached. "cond_type" and +/// "cond_level" specify a conditional type and the address of a level variable +/// which is to be decremented with each skipped conditional of the specified +/// type. +/// Also free "for info" structures where needed. void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_level) { while (cstack->cs_idx > idx) { @@ -2072,17 +2018,13 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev } } -/* - * ":endfunction" when not after a ":function" - */ +/// Handle ":endfunction" when not after a ":function" void ex_endfunction(exarg_T *eap) { emsg(_("E193: :endfunction not inside a function")); } -/* - * Return TRUE if the string "p" looks like a ":while" or ":for" command. - */ +/// @return TRUE if the string "p" looks like a ":while" or ":for" command. int has_loop_cmd(char_u *p) { int len; @@ -2104,4 +2046,3 @@ int has_loop_cmd(char_u *p) } return FALSE; } - -- cgit From 794d2744f33562326172801ddd729853e7135347 Mon Sep 17 00:00:00 2001 From: hlpr98 Date: Sat, 12 Mar 2022 21:08:29 +0100 Subject: feat(ui): implement ui_client event handlers --- src/nvim/CMakeLists.txt | 5 ++ src/nvim/api/ui_events.in.h | 2 +- src/nvim/generators/c_grammar.lua | 1 + src/nvim/generators/gen_api_ui_events.lua | 62 +++++++++++++++- src/nvim/map.c | 1 + src/nvim/map.h | 2 + src/nvim/ui_client.c | 114 ++++++++++++++++++++++++++++++ src/nvim/ui_client.h | 4 ++ 8 files changed, 187 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/nvim/CMakeLists.txt mode change 100644 => 100755 src/nvim/generators/gen_api_ui_events.lua (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt old mode 100644 new mode 100755 index 96e5d1e77c..34d112a756 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,6 +39,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) +set(GENERATED_UI_EVENTS_REDRAW ${GENERATED_DIR}/ui_events_redraw.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) @@ -271,6 +272,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" + "${GENERATED_UI_EVENTS_REDRAW}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -368,6 +370,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_REDRAW} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS} @@ -375,6 +378,8 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} + ${GENERATED_UI_EVENTS_REDRAW} + DEPENDS ${API_UI_EVENTS_GENERATOR} ${GENERATOR_C_GRAMMAR} diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 03fe5c5058..78826072a7 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -84,7 +84,7 @@ void grid_clear(Integer grid) void grid_cursor_goto(Integer grid, Integer row, Integer col) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_line(Integer grid, Integer row, Integer col_start, Array data) - FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY; + FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY FUNC_API_CLIENT_IMPL; void grid_scroll(Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index f35817c466..70a7be86b5 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -49,6 +49,7 @@ local c_proto = Ct( (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) * + (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1) * fill * P(';') ) diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua old mode 100644 new mode 100755 index 3cb117d8b5..3cb63c3837 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -3,15 +3,16 @@ local mpack = require('mpack') local nvimdir = arg[1] package.path = nvimdir .. '/?.lua;' .. package.path -assert(#arg == 7) +assert(#arg == 8) local input = io.open(arg[2], 'rb') local proto_output = io.open(arg[3], 'wb') local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') local bridge_output = io.open(arg[6], 'wb') local metadata_output = io.open(arg[7], 'wb') +local redraw_output = io.open(arg[8], 'wb') -local c_grammar = require('generators.c_grammar') +c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) local function write_signature(output, ev, prefix, notype) @@ -50,6 +51,35 @@ local function write_arglist(output, ev, need_copy) end end +function extract_and_write_arglist(output, ev) + local hlattrs_args_count = 0 + for j = 1, #ev.parameters do + local param = ev.parameters[j] + local kind = param[1] + output:write(' '..kind..' arg_'..j..' = ') + if kind == 'HlAttrs' then + -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs + output:write('redraw_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + hlattrs_args_count = hlattrs_args_count + 1 + elseif kind == 'Object' then + output:write('args.items['..(j-1)..'];\n') + else + output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') + end + end +end + +function call_ui_event_method(output, ev) + output:write(' ui_call_'..ev.name..'(') + for j = 1, #ev.parameters do + output:write('arg_'..j) + if j ~= #ev.parameters then + output:write(', ') + end + end + output:write(');\n') +end + for i = 1, #events do local ev = events[i] assert(ev.return_type == 'void') @@ -160,12 +190,38 @@ for i = 1, #events do call_output:write(";\n") call_output:write("}\n\n") end + + if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then + redraw_output:write('void ui_redraw_event_'..ev.name..'(Array args)\n{\n') + extract_and_write_arglist(redraw_output, ev) + call_ui_event_method(redraw_output, ev) + redraw_output:write('}\n\n') + end end +-- Generate the map_init method for redraw handlers +redraw_output:write([[ +void redraw_methods_table_init(void) +{ + +]]) + +for i = 1, #events do + local fn = events[i] + if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then + redraw_output:write(' add_redraw_event_handler('.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, '.. + '(ApiRedrawWrapper) ui_redraw_event_'..fn.name..');\n') + end +end + +redraw_output:write('\n}\n\n') + proto_output:close() call_output:close() remote_output:close() -bridge_output:close() +redraw_output:close() -- don't expose internal attributes like "impl_name" in public metadata local exported_attributes = {'name', 'parameters', diff --git a/src/nvim/map.c b/src/nvim/map.c index 4e39eb8c07..260a27c485 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -179,6 +179,7 @@ MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) +MAP_IMPL(String, ApiRedrawWrapper, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 00f72386a7..0fa3fbd83d 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -8,6 +8,7 @@ #include "nvim/extmark_defs.h" #include "nvim/highlight_defs.h" #include "nvim/map_defs.h" +#include "nvim/ui_client.h" #if defined(__NetBSD__) # undef uint64_t @@ -48,6 +49,7 @@ MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) +MAP_DECLS(String, ApiRedrawWrapper) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 4a435aac4d..b23ceefb6f 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -6,11 +6,22 @@ #include #include "nvim/vim.h" +#include "nvim/log.h" +#include "nvim/map.h" #include "nvim/ui_client.h" #include "nvim/api/private/helpers.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/api/private/dispatch.h" #include "nvim/ui.h" +#include "nvim/highlight.h" +#include "nvim/screen.h" + +static Map(String, ApiRedrawWrapper) redraw_methods = MAP_INIT; + +static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) +{ + map_put(String, ApiRedrawWrapper)(&redraw_methods, method, handler); +} void ui_client_init(uint64_t chan) { @@ -68,3 +79,106 @@ void ui_client_execute(uint64_t chan) getout(0); } + +/// @param name Redraw method name +/// @param name_len name size (includes terminating NUL) +ApiRedrawWrapper get_redraw_event_handler(const char *name, size_t name_len, Error *error) +{ + String m = { .data = (char *)name, .size = name_len }; + ApiRedrawWrapper rv = + map_get(String, ApiRedrawWrapper)(&redraw_methods, m); + + if (!rv) { + api_set_error(error, kErrorTypeException, "Invalid method: %.*s", + m.size > 0 ? (int)m.size : (int)sizeof(""), + m.size > 0 ? m.data : ""); + } + return rv; +} + +static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) +{ + Error err = ERROR_INIT; + Dict(highlight) dict = { 0 }; + if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, redraw_dict, &err)) { + // TODO(bfredl): log "err" + return HLATTRS_INIT; + } + return dict2hlattrs(&dict, true, NULL, &err); +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +#include "ui_events_redraw.generated.h" +#endif + +void ui_redraw_event_grid_line(Array args) +{ + Integer grid = args.items[0].data.integer; + Integer row = args.items[1].data.integer; + Integer startcol = args.items[2].data.integer; + Array cells = args.items[3].data.array; + Integer endcol, clearcol, clearattr; + // TODO(hlpr98): Accomodate other LineFlags when included in grid_line + LineFlags lineflags = 0; + schar_T *chunk; + sattr_T *attrs; + size_t size_of_cells = cells.size; + size_t no_of_cells = size_of_cells; + endcol = startcol; + + // checking if clearcol > endcol + if (!STRCMP(cells.items[size_of_cells-1].data.array + .items[0].data.string.data, " ") + && cells.items[size_of_cells-1].data.array.size == 3) { + no_of_cells = size_of_cells - 1; + } + + // getting endcol + for (size_t i = 0; i < no_of_cells; i++) { + endcol++; + if (cells.items[i].data.array.size == 3) { + endcol += cells.items[i].data.array.items[2].data.integer - 1; + } + } + + if (!STRCMP(cells.items[size_of_cells-1].data.array + .items[0].data.string.data, " ") + && cells.items[size_of_cells-1].data.array.size == 3) { + clearattr = cells.items[size_of_cells-1].data.array.items[1].data.integer; + clearcol = endcol + cells.items[size_of_cells-1].data.array + .items[2].data.integer; + } else { + clearattr = 0; + clearcol = endcol; + } + + size_t ncells = (size_t)(endcol - startcol); + chunk = xmalloc(ncells * sizeof(schar_T) + 1); + attrs = xmalloc(ncells * sizeof(sattr_T) + 1); + + size_t j = 0; + size_t k = 0; + for (size_t i = 0; i < no_of_cells; i++) { + STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); + if (cells.items[i].data.array.size == 3) { + // repeat present + for (size_t i_intr = 1; + i_intr < (size_t)cells.items[i].data.array.items[2].data.integer; + i_intr++) { + STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); + attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + } + } else if (cells.items[i].data.array.size == 2) { + // repeat = 1 but attrs != last_hl + attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + } + if (j > k) { + // attrs == last_hl + attrs[k] = attrs[k-1]; + k++; + } + } + + ui_call_raw_line(grid, row, startcol, endcol, clearcol, clearattr, lineflags, + (const schar_T *)chunk, (const sattr_T *)attrs); +} diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index 067f78d5c5..a120f2b334 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,7 +3,11 @@ #include "nvim/api/private/defs.h" +typedef void (*ApiRedrawWrapper)(Array args); + #ifdef INCLUDE_GENERATED_DECLARATIONS #include "ui_client.h.generated.h" +#include "ui_events_redraw.h.generated.h" #endif + #endif // NVIM_UI_CLIENT_H -- cgit From 534edce3c4972d1c8da44fbcf60e7946c09a5612 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 12 Mar 2022 23:17:32 +0100 Subject: feat(ui): invoke ui client handlers --- src/nvim/main.c | 12 ++++++++---- src/nvim/msgpack_rpc/channel.c | 8 ++------ src/nvim/ui_client.c | 39 ++++++++++++++++++--------------------- 3 files changed, 28 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nvim/main.c b/src/nvim/main.c index 95ef306745..d67b47e82c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -112,7 +112,6 @@ static const char *err_too_many_args = N_("Too many edit arguments"); static const char *err_extra_cmd = N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"); - void event_init(void) { loop_init(&main_loop, NULL); @@ -344,6 +343,12 @@ int main(int argc, char **argv) TIME_MSG("init screen for UI"); } + if (ui_client_channel_id) { + ui_client_init(ui_client_channel_id); + ui_client_execute(ui_client_channel_id); + abort(); // unreachable + } + init_default_mappings(); // Default mappings. TIME_MSG("init default mappings"); @@ -840,9 +845,8 @@ static void remote_request(mparm_T *params, int remote_args, exit(1); } - ui_client_init(chan); - ui_client_execute(chan); - abort(); // unreachable + ui_client_channel_id = chan; + return; } Array args = ARRAY_DICT_INIT; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index f4e836fa81..48ecd5d0ea 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -547,12 +547,8 @@ void rpc_close(Channel *channel) channel->rpc.closed = true; channel_decref(channel); - if (channel->id == ui_client_channel_id) { - // TODO(bfredl): handle this in ui_client, where os_exit() is safe - exit(0); - } - - if (channel->streamtype == kChannelStreamStdio) { + if (channel->streamtype == kChannelStreamStdio + || channel->id == ui_client_channel_id) { multiqueue_put(main_loop.fast_events, exit_event, 0); } } diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index b23ceefb6f..c13bfc2d07 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -26,21 +26,21 @@ static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) void ui_client_init(uint64_t chan) { Array args = ARRAY_DICT_INIT; - int width = 80; - int height = 25; + int width = Columns; + int height = Rows; Dictionary opts = ARRAY_DICT_INIT; PUT(opts, "rgb", BOOLEAN_OBJ(true)); PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true)); PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true)); - // TODO(bfredl): use the size of the client UI ADD(args, INTEGER_OBJ((int)width)); ADD(args, INTEGER_OBJ((int)height)); ADD(args, DICTIONARY_OBJ(opts)); rpc_send_event(chan, "nvim_ui_attach", args); msgpack_rpc_add_redraw(); // GAME! + redraw_methods_table_init(); ui_client_channel_id = chan; } @@ -61,9 +61,22 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) { for (size_t i = 0; i < args.size; i++) { Array call = args.items[i].data.array; - char *method_name = call.items[0].data.string.data; + String name = call.items[0].data.string; + + ApiRedrawWrapper handler = map_get(String, ApiRedrawWrapper)(&redraw_methods, name); + if (!handler) { + ELOG("No redraw handler by name: %s", name.size ? name.data : ""); + continue; + } + + // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); + + DLOG("Invoke redraw handler by name: %s", name.data); + for (size_t j = 1; j < call.size; j++) { + Array internal_call_args = call.items[j].data.array; + handler(internal_call_args); + } - fprintf(stderr, "%s: %zu\n", method_name, call.size-1); } return NIL; } @@ -80,22 +93,6 @@ void ui_client_execute(uint64_t chan) getout(0); } -/// @param name Redraw method name -/// @param name_len name size (includes terminating NUL) -ApiRedrawWrapper get_redraw_event_handler(const char *name, size_t name_len, Error *error) -{ - String m = { .data = (char *)name, .size = name_len }; - ApiRedrawWrapper rv = - map_get(String, ApiRedrawWrapper)(&redraw_methods, m); - - if (!rv) { - api_set_error(error, kErrorTypeException, "Invalid method: %.*s", - m.size > 0 ? (int)m.size : (int)sizeof(""), - m.size > 0 ? m.data : ""); - } - return rv; -} - static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) { Error err = ERROR_INIT; -- cgit From ca23f2ed308b46665e5c8e677c4012cfa7453490 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 13 Mar 2022 14:57:57 +0100 Subject: refactor(ui): use "ui_client" instead of "redraw" as general prefix --- src/nvim/CMakeLists.txt | 9 ++++---- src/nvim/generators/gen_api_ui_events.lua | 32 ++++++++++++++-------------- src/nvim/map.c | 2 +- src/nvim/map.h | 2 +- src/nvim/ui_client.c | 35 ++++++++++++++----------------- src/nvim/ui_client.h | 4 ++-- 6 files changed, 40 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 34d112a756..8e17f94abc 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -39,7 +39,7 @@ set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h) set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h) set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h) -set(GENERATED_UI_EVENTS_REDRAW ${GENERATED_DIR}/ui_events_redraw.generated.h) +set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h) set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) @@ -272,7 +272,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" "${GENERATED_KEYSETS}" - "${GENERATED_UI_EVENTS_REDRAW}" + "${GENERATED_UI_EVENTS_CLIENT}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -370,7 +370,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} - ${GENERATED_UI_EVENTS_REDRAW} + ${GENERATED_UI_EVENTS_CLIENT} COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h ${GENERATED_UI_EVENTS} @@ -378,8 +378,7 @@ add_custom_command( ${GENERATED_UI_EVENTS_REMOTE} ${GENERATED_UI_EVENTS_BRIDGE} ${GENERATED_UI_EVENTS_METADATA} - ${GENERATED_UI_EVENTS_REDRAW} - + ${GENERATED_UI_EVENTS_CLIENT} DEPENDS ${API_UI_EVENTS_GENERATOR} ${GENERATOR_C_GRAMMAR} diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 3cb63c3837..cdf4538e9f 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -10,9 +10,9 @@ local call_output = io.open(arg[4], 'wb') local remote_output = io.open(arg[5], 'wb') local bridge_output = io.open(arg[6], 'wb') local metadata_output = io.open(arg[7], 'wb') -local redraw_output = io.open(arg[8], 'wb') +local client_output = io.open(arg[8], 'wb') -c_grammar = require('generators.c_grammar') +local c_grammar = require('generators.c_grammar') local events = c_grammar.grammar:match(input:read('*all')) local function write_signature(output, ev, prefix, notype) @@ -51,7 +51,7 @@ local function write_arglist(output, ev, need_copy) end end -function extract_and_write_arglist(output, ev) +local function extract_and_write_arglist(output, ev) local hlattrs_args_count = 0 for j = 1, #ev.parameters do local param = ev.parameters[j] @@ -59,7 +59,7 @@ function extract_and_write_arglist(output, ev) output:write(' '..kind..' arg_'..j..' = ') if kind == 'HlAttrs' then -- The first HlAttrs argument is rgb_attrs and second is cterm_attrs - output:write('redraw_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') + output:write('ui_client_dict2hlattrs(args.items['..(j-1)..'].data.dictionary, '..(hlattrs_args_count == 0 and 'true' or 'false')..');\n') hlattrs_args_count = hlattrs_args_count + 1 elseif kind == 'Object' then output:write('args.items['..(j-1)..'];\n') @@ -69,7 +69,7 @@ function extract_and_write_arglist(output, ev) end end -function call_ui_event_method(output, ev) +local function call_ui_event_method(output, ev) output:write(' ui_call_'..ev.name..'(') for j = 1, #ev.parameters do output:write('arg_'..j) @@ -192,16 +192,16 @@ for i = 1, #events do end if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then - redraw_output:write('void ui_redraw_event_'..ev.name..'(Array args)\n{\n') - extract_and_write_arglist(redraw_output, ev) - call_ui_event_method(redraw_output, ev) - redraw_output:write('}\n\n') + client_output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + extract_and_write_arglist(client_output, ev) + call_ui_event_method(client_output, ev) + client_output:write('}\n\n') end end --- Generate the map_init method for redraw handlers -redraw_output:write([[ -void redraw_methods_table_init(void) +-- Generate the map_init method for client handlers +client_output:write([[ +void ui_client_methods_table_init(void) { ]]) @@ -209,19 +209,19 @@ void redraw_methods_table_init(void) for i = 1, #events do local fn = events[i] if (not fn.noexport) and ((not fn.remote_only) or fn.client_impl) then - redraw_output:write(' add_redraw_event_handler('.. + client_output:write(' add_ui_client_event_handler('.. '(String) {.data = "'..fn.name..'", '.. '.size = sizeof("'..fn.name..'") - 1}, '.. - '(ApiRedrawWrapper) ui_redraw_event_'..fn.name..');\n') + '(UIClientHandler) ui_client_event_'..fn.name..');\n') end end -redraw_output:write('\n}\n\n') +client_output:write('\n}\n\n') proto_output:close() call_output:close() remote_output:close() -redraw_output:close() +client_output:close() -- don't expose internal attributes like "impl_name" in public metadata local exported_attributes = {'name', 'parameters', diff --git a/src/nvim/map.c b/src/nvim/map.c index 260a27c485..b3f48ad5d6 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -179,7 +179,7 @@ MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) MAP_IMPL(String, handle_T, 0) MAP_IMPL(String, int, DEFAULT_INITIALIZER) MAP_IMPL(int, String, DEFAULT_INITIALIZER) -MAP_IMPL(String, ApiRedrawWrapper, NULL) +MAP_IMPL(String, UIClientHandler, NULL) MAP_IMPL(ColorKey, ColorItem, COLOR_ITEM_INITIALIZER) diff --git a/src/nvim/map.h b/src/nvim/map.h index 0fa3fbd83d..693ef50127 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -49,7 +49,7 @@ MAP_DECLS(HlEntry, int) MAP_DECLS(String, handle_T) MAP_DECLS(String, int) MAP_DECLS(int, String) -MAP_DECLS(String, ApiRedrawWrapper) +MAP_DECLS(String, UIClientHandler) MAP_DECLS(ColorKey, ColorItem) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index c13bfc2d07..daae257027 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -16,11 +16,11 @@ #include "nvim/highlight.h" #include "nvim/screen.h" -static Map(String, ApiRedrawWrapper) redraw_methods = MAP_INIT; +static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; -static void add_redraw_event_handler(String method, ApiRedrawWrapper handler) +static void add_ui_client_event_handler(String method, UIClientHandler handler) { - map_put(String, ApiRedrawWrapper)(&redraw_methods, method, handler); + map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); } void ui_client_init(uint64_t chan) @@ -40,17 +40,16 @@ void ui_client_init(uint64_t chan) rpc_send_event(chan, "nvim_ui_attach", args); msgpack_rpc_add_redraw(); // GAME! - redraw_methods_table_init(); + // TODO(bfredl): use a keyset instead + ui_client_methods_table_init(); ui_client_channel_id = chan; } /// Handler for "redraw" events sent by the NVIM server /// -/// This is just a stub. The mentioned functionality will be implemented. -/// -/// This function will be called by handle_request (in msgpack_rpc/channle.c) +/// This function will be called by handle_request (in msgpack_rpc/channel.c) /// The individual ui_events sent by the server are individually handled -/// by their respective handlers defined in ui_events_redraw.generated.h +/// by their respective handlers defined in ui_events_client.generated.h /// /// @note The "flush" event is called only once and only after handling all /// the other events @@ -63,21 +62,19 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) Array call = args.items[i].data.array; String name = call.items[0].data.string; - ApiRedrawWrapper handler = map_get(String, ApiRedrawWrapper)(&redraw_methods, name); + UIClientHandler handler = map_get(String, UIClientHandler)(&ui_client_handlers, name); if (!handler) { - ELOG("No redraw handler by name: %s", name.size ? name.data : ""); + ELOG("No ui client handler for %s", name.size ? name.data : ""); continue; } // fprintf(stderr, "%s: %zu\n", name.data, call.size-1); - - DLOG("Invoke redraw handler by name: %s", name.data); + DLOG("Invoke ui client handler for %s", name.data); for (size_t j = 1; j < call.size; j++) { - Array internal_call_args = call.items[j].data.array; - handler(internal_call_args); + handler(call.items[j].data.array); } - } + return NIL; } @@ -93,11 +90,11 @@ void ui_client_execute(uint64_t chan) getout(0); } -static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) +static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) { Error err = ERROR_INIT; Dict(highlight) dict = { 0 }; - if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, redraw_dict, &err)) { + if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) { // TODO(bfredl): log "err" return HLATTRS_INIT; } @@ -105,10 +102,10 @@ static HlAttrs redraw_dict2hlattrs(Dictionary redraw_dict, bool rgb) } #ifdef INCLUDE_GENERATED_DECLARATIONS -#include "ui_events_redraw.generated.h" +#include "ui_events_client.generated.h" #endif -void ui_redraw_event_grid_line(Array args) +void ui_client_event_grid_line(Array args) { Integer grid = args.items[0].data.integer; Integer row = args.items[1].data.integer; diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h index a120f2b334..253deecc52 100644 --- a/src/nvim/ui_client.h +++ b/src/nvim/ui_client.h @@ -3,11 +3,11 @@ #include "nvim/api/private/defs.h" -typedef void (*ApiRedrawWrapper)(Array args); +typedef void (*UIClientHandler)(Array args); #ifdef INCLUDE_GENERATED_DECLARATIONS #include "ui_client.h.generated.h" -#include "ui_events_redraw.h.generated.h" +#include "ui_events_client.h.generated.h" #endif #endif // NVIM_UI_CLIENT_H -- cgit From c0b4d931e12910f67cc3eade664247ea2d2bb913 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 13 Mar 2022 16:02:53 +0100 Subject: refactor(ui): make ui_client_event_grid_line typesafe --- src/nvim/api/ui_events.in.h | 2 +- src/nvim/ui_client.c | 131 +++++++++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 78826072a7..db348359eb 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -78,7 +78,7 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, void hl_group_set(String name, Integer id) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL; void grid_resize(Integer grid, Integer width, Integer height) - FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; + FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL; void grid_clear(Integer grid) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; void grid_cursor_goto(Integer grid, Integer row, Integer col) diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index daae257027..3914a4e199 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -18,6 +18,11 @@ static Map(String, UIClientHandler) ui_client_handlers = MAP_INIT; +// Temporary buffer for converting a single grid_line event +static size_t buf_size = 0; +static schar_T *buf_char = NULL; +static sattr_T *buf_attr = NULL; + static void add_ui_client_event_handler(String method, UIClientHandler handler) { map_put(String, UIClientHandler)(&ui_client_handlers, method, handler); @@ -105,74 +110,98 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) #include "ui_events_client.generated.h" #endif +void ui_client_event_grid_resize(Array args) +{ + // TODO: typesafe! + Integer grid = args.items[0].data.integer; + Integer width = args.items[1].data.integer; + Integer height = args.items[2].data.integer; + ui_call_grid_resize(grid, width, height); + + if (buf_size < (size_t)width) { + xfree(buf_char); + xfree(buf_attr); + buf_size = (size_t)width; + buf_char = xmalloc(buf_size * sizeof(schar_T)); + buf_attr = xmalloc(buf_size * sizeof(sattr_T)); + } +} + void ui_client_event_grid_line(Array args) { + if (args.size < 4 + || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[2].type != kObjectTypeInteger + || args.items[3].type != kObjectTypeArray) { + goto error; + } + Integer grid = args.items[0].data.integer; Integer row = args.items[1].data.integer; Integer startcol = args.items[2].data.integer; Array cells = args.items[3].data.array; - Integer endcol, clearcol, clearattr; + + Integer endcol, clearcol; // TODO(hlpr98): Accomodate other LineFlags when included in grid_line LineFlags lineflags = 0; - schar_T *chunk; - sattr_T *attrs; - size_t size_of_cells = cells.size; - size_t no_of_cells = size_of_cells; endcol = startcol; - // checking if clearcol > endcol - if (!STRCMP(cells.items[size_of_cells-1].data.array - .items[0].data.string.data, " ") - && cells.items[size_of_cells-1].data.array.size == 3) { - no_of_cells = size_of_cells - 1; - } + size_t j = 0; + int cur_attr = 0; + int clear_attr = 0; + int clear_width = 0; + for (size_t i = 0; i < cells.size; i++) { + if (cells.items[i].type != kObjectTypeArray) { + goto error; + } + Array cell = cells.items[i].data.array; - // getting endcol - for (size_t i = 0; i < no_of_cells; i++) { - endcol++; - if (cells.items[i].data.array.size == 3) { - endcol += cells.items[i].data.array.items[2].data.integer - 1; + if (cell.size < 1 || cell.items[0].type != kObjectTypeString) { + goto error; + } + String sstring = cell.items[0].data.string; + + char *schar = sstring.data; + int repeat = 1; + if (cell.size >= 2) { + if (cell.items[1].type != kObjectTypeInteger + || cell.items[1].data.integer < 0) { + goto error; + } + cur_attr = (int)cell.items[1].data.integer; } - } - if (!STRCMP(cells.items[size_of_cells-1].data.array - .items[0].data.string.data, " ") - && cells.items[size_of_cells-1].data.array.size == 3) { - clearattr = cells.items[size_of_cells-1].data.array.items[1].data.integer; - clearcol = endcol + cells.items[size_of_cells-1].data.array - .items[2].data.integer; - } else { - clearattr = 0; - clearcol = endcol; - } + if (cell.size >= 3) { + if (cell.items[2].type != kObjectTypeInteger + || cell.items[2].data.integer < 0) { + goto error; + } + repeat = (int)cell.items[2].data.integer; + } - size_t ncells = (size_t)(endcol - startcol); - chunk = xmalloc(ncells * sizeof(schar_T) + 1); - attrs = xmalloc(ncells * sizeof(sattr_T) + 1); + if (i == cells.size - 1 && sstring.size == 1 && sstring.data[0] == ' ' && repeat > 1) { + clear_width = repeat; + break; + } - size_t j = 0; - size_t k = 0; - for (size_t i = 0; i < no_of_cells; i++) { - STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); - if (cells.items[i].data.array.size == 3) { - // repeat present - for (size_t i_intr = 1; - i_intr < (size_t)cells.items[i].data.array.items[2].data.integer; - i_intr++) { - STRCPY(chunk[j++], cells.items[i].data.array.items[0].data.string.data); - attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; + for (int r = 0; r < repeat; r++) { + if (j >= buf_size) { + goto error; // _YIKES_ } - } else if (cells.items[i].data.array.size == 2) { - // repeat = 1 but attrs != last_hl - attrs[k++] = (sattr_T)cells.items[i].data.array.items[1].data.integer; - } - if (j > k) { - // attrs == last_hl - attrs[k] = attrs[k-1]; - k++; + STRCPY(buf_char[j], schar); + buf_attr[j++] = cur_attr; } } - ui_call_raw_line(grid, row, startcol, endcol, clearcol, clearattr, lineflags, - (const schar_T *)chunk, (const sattr_T *)attrs); + endcol = startcol + (int)j; + clearcol = endcol + clear_width; + clear_attr = cur_attr; + + ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, + buf_char, buf_attr); + return; + +error: + ELOG("malformatted 'grid_line' event"); } -- cgit From f01d203b70f426c1538813b3bacb4483e914ab44 Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 15 Mar 2022 00:23:14 +0100 Subject: refactor(ui): make auto-generated ui client handlers typesafe --- src/nvim/generators/gen_api_ui_events.lua | 26 ++++++++++++++++++++------ src/nvim/ui_client.c | 15 +++++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index cdf4538e9f..5e70442dce 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -51,8 +51,25 @@ local function write_arglist(output, ev, need_copy) end end -local function extract_and_write_arglist(output, ev) +local function call_ui_event_method(output, ev) + output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') + local hlattrs_args_count = 0 + if #ev.parameters > 0 then + output:write(' if (args.size < '..(#ev.parameters)) + for j = 1, #ev.parameters do + local kind = ev.parameters[j][1] + if kind ~= "Object" then + if kind == 'HlAttrs' then kind = 'Dictionary' end + output:write('\n || args.items['..(j-1)..'].type != kObjectType'..kind..'') + end + end + output:write(') {\n') + output:write(' ELOG("Error handling ui event \''..ev.name..'\'");\n') + output:write(' return;\n') + output:write(' }\n') + end + for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = param[1] @@ -67,9 +84,7 @@ local function extract_and_write_arglist(output, ev) output:write('args.items['..(j-1)..'].data.'..string.lower(kind)..';\n') end end -end -local function call_ui_event_method(output, ev) output:write(' ui_call_'..ev.name..'(') for j = 1, #ev.parameters do output:write('arg_'..j) @@ -78,6 +93,8 @@ local function call_ui_event_method(output, ev) end end output:write(');\n') + + output:write('}\n\n') end for i = 1, #events do @@ -192,10 +209,7 @@ for i = 1, #events do end if (not ev.remote_only) and (not ev.noexport) and (not ev.client_impl) then - client_output:write('void ui_client_event_'..ev.name..'(Array args)\n{\n') - extract_and_write_arglist(client_output, ev) call_ui_event_method(client_output, ev) - client_output:write('}\n\n') end end diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 3914a4e199..4fad3e0709 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -112,7 +112,14 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb) void ui_client_event_grid_resize(Array args) { - // TODO: typesafe! + if (args.size < 3 + || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[2].type != kObjectTypeInteger) { + ELOG("Error handling ui event 'grid_resize'"); + return; + } + Integer grid = args.items[0].data.integer; Integer width = args.items[1].data.integer; Integer height = args.items[2].data.integer; @@ -189,7 +196,7 @@ void ui_client_event_grid_line(Array args) if (j >= buf_size) { goto error; // _YIKES_ } - STRCPY(buf_char[j], schar); + STRLCPY(buf_char[j], schar, sizeof(schar_T)); buf_attr[j++] = cur_attr; } } @@ -199,9 +206,9 @@ void ui_client_event_grid_line(Array args) clear_attr = cur_attr; ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, - buf_char, buf_attr); + (const schar_T *)buf_char, (const sattr_T *)buf_attr); return; error: - ELOG("malformatted 'grid_line' event"); + ELOG("Error handling ui event 'grid_line'"); } -- cgit From 5a8bf31d328ecdb79453bf1eb22ff10aabbe0422 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Tue, 15 Mar 2022 14:46:32 -0700 Subject: vim-patch:8.2.4571: not all gdb files are recognized (#17727) Problem: Not all gdb files are recognized. Solution: Add a few more patterns for gdb. (closes https://github.com/vim/vim/pull/9956) https://github.com/vim/vim/commit/8d5e514d77bd4b1956656ad2be2ce7094bd43a72 --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 5f4a7dac6e..839d160e39 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -196,7 +196,7 @@ let s:filename_checks = { \ 'fstab': ['fstab', 'mtab'], \ 'fusion': ['file.fusion'], \ 'fvwm': ['/.fvwm/file', 'any/.fvwm/file'], - \ 'gdb': ['.gdbinit', 'gdbinit'], + \ 'gdb': ['.gdbinit', 'gdbinit', 'file.gdb', '.config/gdbearlyinit', '.gdbearlyinit'], \ 'gdresource': ['file.tscn', 'file.tres'], \ 'gdscript': ['file.gd'], \ 'gdmo': ['file.mo', 'file.gdmo'], -- cgit From 72ed4a547af83c937517a37213eeb43891230a87 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(fold): convert function comments to doxygen format --- src/nvim/fold.c | 402 ++++++++++++++++++++++---------------------------------- 1 file changed, 156 insertions(+), 246 deletions(-) (limited to 'src') diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 546345eeac..54430d46af 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -121,9 +121,7 @@ static size_t foldendmarkerlen; // Exported folding functions. {{{1 // copyFoldingState() {{{2 -/* - * Copy that folding state from window "wp_from" to window "wp_to". - */ +/// Copy that folding state from window "wp_from" to window "wp_to". void copyFoldingState(win_T *wp_from, win_T *wp_to) { wp_to->w_fold_manual = wp_from->w_fold_manual; @@ -132,9 +130,7 @@ void copyFoldingState(win_T *wp_from, win_T *wp_to) } // hasAnyFolding() {{{2 -/* - * Return TRUE if there may be folded lines in the current window. - */ +/// @return TRUE if there may be folded lines in the current window. int hasAnyFolding(win_T *win) { // very simple now, but can become more complex later @@ -259,9 +255,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp } // foldLevel() {{{2 -/* - * Return fold level at line number "lnum" in the current window. - */ +/// @return fold level at line number "lnum" in the current window. int foldLevel(linenr_T lnum) { // While updating the folds lines between invalid_top and invalid_bot have @@ -283,15 +277,16 @@ int foldLevel(linenr_T lnum) } // lineFolded() {{{2 -// Low level function to check if a line is folded. Doesn't use any caching. -// Return true if line is folded. -// Return false if line is not folded. +/// Low level function to check if a line is folded. Doesn't use any caching. +/// +/// @return true if line is folded or, +/// false if line is not folded. bool lineFolded(win_T *const win, const linenr_T lnum) { return fold_info(win, lnum).fi_lines != 0; } -/// fold_info() {{{2 +// fold_info() {{{2 /// /// Count the number of lines that are folded at line number "lnum". /// Normally "lnum" is the first line of a possible fold, and the returned @@ -316,61 +311,49 @@ foldinfo_T fold_info(win_T *win, linenr_T lnum) } // foldmethodIsManual() {{{2 -/* - * Return TRUE if 'foldmethod' is "manual" - */ +/// @return TRUE if 'foldmethod' is "manual" int foldmethodIsManual(win_T *wp) { return wp->w_p_fdm[3] == 'u'; } // foldmethodIsIndent() {{{2 -/* - * Return TRUE if 'foldmethod' is "indent" - */ +/// @return TRUE if 'foldmethod' is "indent" int foldmethodIsIndent(win_T *wp) { return wp->w_p_fdm[0] == 'i'; } // foldmethodIsExpr() {{{2 -/* - * Return TRUE if 'foldmethod' is "expr" - */ +/// @return TRUE if 'foldmethod' is "expr" int foldmethodIsExpr(win_T *wp) { return wp->w_p_fdm[1] == 'x'; } // foldmethodIsMarker() {{{2 -/* - * Return TRUE if 'foldmethod' is "marker" - */ +/// @return TRUE if 'foldmethod' is "marker" int foldmethodIsMarker(win_T *wp) { return wp->w_p_fdm[2] == 'r'; } // foldmethodIsSyntax() {{{2 -/* - * Return TRUE if 'foldmethod' is "syntax" - */ +/// @return TRUE if 'foldmethod' is "syntax" int foldmethodIsSyntax(win_T *wp) { return wp->w_p_fdm[0] == 's'; } // foldmethodIsDiff() {{{2 -/* - * Return TRUE if 'foldmethod' is "diff" - */ +/// @return TRUE if 'foldmethod' is "diff" int foldmethodIsDiff(win_T *wp) { return wp->w_p_fdm[0] == 'd'; } // closeFold() {{{2 -/// Close fold for current window at line "lnum". +/// Close fold for current window at position "pos". /// Repeat "count" times. void closeFold(pos_T pos, long count) { @@ -378,9 +361,7 @@ void closeFold(pos_T pos, long count) } // closeFoldRecurse() {{{2 -/* - * Close fold for current window at line "lnum" recursively. - */ +/// Close fold for current window at position `pos` recursively. void closeFoldRecurse(pos_T pos) { (void)setManualFold(pos, false, true, NULL); @@ -427,28 +408,22 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha } // openFold() {{{2 -/* - * Open fold for current window at line "lnum". - * Repeat "count" times. - */ +/// Open fold for current window at position "pos". +/// Repeat "count" times. void openFold(pos_T pos, long count) { setFoldRepeat(pos, count, true); } // openFoldRecurse() {{{2 -/* - * Open fold for current window at line "lnum" recursively. - */ +/// Open fold for current window at position `pos` recursively. void openFoldRecurse(pos_T pos) { (void)setManualFold(pos, true, true, NULL); } // foldOpenCursor() {{{2 -/* - * Open folds until the cursor line is not in a closed fold. - */ +/// Open folds until the cursor line is not in a closed fold. void foldOpenCursor(void) { int done; @@ -466,9 +441,7 @@ void foldOpenCursor(void) } // newFoldLevel() {{{2 -/* - * Set new foldlevel for current window. - */ +/// Set new foldlevel for current window. void newFoldLevel(void) { newFoldLevelWin(curwin); @@ -505,9 +478,7 @@ static void newFoldLevelWin(win_T *wp) } // foldCheckClose() {{{2 -/* - * Apply 'foldlevel' to all folds that don't contain the cursor. - */ +/// Apply 'foldlevel' to all folds that don't contain the cursor. void foldCheckClose(void) { if (*p_fcl != NUL) { // can only be "all" right now @@ -543,8 +514,8 @@ static int checkCloseRec(garray_T *gap, linenr_T lnum, int level) } // foldCreateAllowed() {{{2 -/// Return TRUE if it's allowed to manually create or delete a fold. -/// Give an error message and return FALSE if not. +/// @return TRUE if it's allowed to manually create or delete a fold or, +/// give an error message and return FALSE if not. int foldManualAllowed(bool create) { if (foldmethodIsManual(curwin) || foldmethodIsMarker(curwin)) { @@ -790,9 +761,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const } // clearFolding() {{{2 -/* - * Remove all folding for window "win". - */ +/// Remove all folding for window "win". void clearFolding(win_T *win) { deleteFoldRecurse(win->w_buffer, &win->w_folds); @@ -800,12 +769,10 @@ void clearFolding(win_T *win) } // foldUpdate() {{{2 -/* - * Update folds for changes in the buffer of a window. - * Note that inserted/deleted lines must have already been taken care of by - * calling foldMarkAdjust(). - * The changes in lines from top to bot (inclusive). - */ +/// Update folds for changes in the buffer of a window. +/// Note that inserted/deleted lines must have already been taken care of by +/// calling foldMarkAdjust(). +/// The changes in lines from top to bot (inclusive). void foldUpdate(win_T *wp, linenr_T top, linenr_T bot) { if (compl_busy || State & INSERT) { @@ -856,12 +823,10 @@ void foldUpdateAfterInsert(void) } // foldUpdateAll() {{{2 -/* - * Update all lines in a window for folding. - * Used when a fold setting changes or after reloading the buffer. - * The actual updating is postponed until fold info is used, to avoid doing - * every time a setting is changed or a syntax item is added. - */ +/// Update all lines in a window for folding. +/// Used when a fold setting changes or after reloading the buffer. +/// The actual updating is postponed until fold info is used, to avoid doing +/// every time a setting is changed or a syntax item is added. void foldUpdateAll(win_T *win) { win->w_foldinvalid = true; @@ -992,21 +957,18 @@ int foldMoveTo(const bool updown, const int dir, const long count) } // foldInitWin() {{{2 -/* - * Init the fold info in a new window. - */ +/// Init the fold info in a new window. void foldInitWin(win_T *new_win) { ga_init(&new_win->w_folds, (int)sizeof(fold_T), 10); } // find_wl_entry() {{{2 -/* - * Find an entry in the win->w_lines[] array for buffer line "lnum". - * Only valid entries are considered (for entries where wl_valid is FALSE the - * line number can be wrong). - * Returns index of entry or -1 if not found. - */ +/// Find an entry in the win->w_lines[] array for buffer line "lnum". +/// Only valid entries are considered (for entries where wl_valid is FALSE the +/// line number can be wrong). +/// +/// @return index of entry or -1 if not found. int find_wl_entry(win_T *win, linenr_T lnum) { int i; @@ -1025,9 +987,7 @@ int find_wl_entry(win_T *win, linenr_T lnum) } // foldAdjustVisual() {{{2 -/* - * Adjust the Visual area to include any fold at the start or end completely. - */ +/// Adjust the Visual area to include any fold at the start or end completely. void foldAdjustVisual(void) { pos_T *start, *end; @@ -1059,9 +1019,7 @@ void foldAdjustVisual(void) } // cursor_foldstart() {{{2 -/* - * Move the cursor to the first line of a closed fold. - */ +/// Move the cursor to the first line of a closed fold. void foldAdjustCursor(void) { (void)hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL); @@ -1069,9 +1027,7 @@ void foldAdjustCursor(void) // Internal functions for "fold_T" {{{1 // cloneFoldGrowArray() {{{2 -/* - * Will "clone" (i.e deep copy) a garray_T of folds. - */ +/// Will "clone" (i.e deep copy) a garray_T of folds. void cloneFoldGrowArray(garray_T *from, garray_T *to) { fold_T *from_p; @@ -1143,9 +1099,7 @@ static bool foldFind(const garray_T *gap, linenr_T lnum, fold_T **fpp) } // foldLevelWin() {{{2 -/* - * Return fold level at line number "lnum" in window "wp". - */ +/// @return fold level at line number "lnum" in window "wp". static int foldLevelWin(win_T *wp, linenr_T lnum) { fold_T *fp; @@ -1169,9 +1123,7 @@ static int foldLevelWin(win_T *wp, linenr_T lnum) } // checkupdate() {{{2 -/* - * Check if the folds in window "wp" are invalid and update them if needed. - */ +/// Check if the folds in window "wp" are invalid and update them if needed. static void checkupdate(win_T *wp) { if (wp->w_foldinvalid) { @@ -1181,10 +1133,8 @@ static void checkupdate(win_T *wp) } // setFoldRepeat() {{{2 -/* - * Open or close fold for current window at line "lnum". - * Repeat "count" times. - */ +/// Open or close fold for current window at position `pos`. +/// Repeat "count" times. static void setFoldRepeat(pos_T pos, long count, int do_open) { int done; @@ -1204,7 +1154,6 @@ static void setFoldRepeat(pos_T pos, long count, int do_open) } // setManualFold() {{{2 -/// /// Open or close the fold in the current window which contains "lnum". /// Also does this for other windows in diff mode when needed. /// @@ -1344,9 +1293,7 @@ static linenr_T setManualFoldWin(win_T *wp, linenr_T lnum, int opening, int recu } // foldOpenNested() {{{2 -/* - * Open all nested folds in fold "fpr" recursively. - */ +/// Open all nested folds in fold "fpr" recursively. static void foldOpenNested(fold_T *fpr) { fold_T *fp; @@ -1359,9 +1306,10 @@ static void foldOpenNested(fold_T *fpr) } // deleteFoldEntry() {{{2 -// Delete fold "idx" from growarray "gap". -// When "recursive" is true also delete all the folds contained in it. -// When "recursive" is false contained folds are moved one level up. +/// Delete fold "idx" from growarray "gap". +/// +/// @param recursive when true, also delete all the folds contained in it. +/// when false, contained folds are moved one level up. static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, const bool recursive) { @@ -1408,9 +1356,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, } // deleteFoldRecurse() {{{2 -/* - * Delete nested folds in a fold. - */ +/// Delete nested folds in a fold. void deleteFoldRecurse(buf_T *bp, garray_T *gap) { #define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested)) @@ -1418,9 +1364,7 @@ void deleteFoldRecurse(buf_T *bp, garray_T *gap) } // foldMarkAdjust() {{{2 -/* - * Update line numbers of folds for inserted/deleted lines. - */ +/// Update line numbers of folds for inserted/deleted lines. void foldMarkAdjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after) { // If deleting marks from line1 to line2, but not deleting all those @@ -1538,10 +1482,8 @@ static void foldMarkAdjustRecurse(win_T *wp, garray_T *gap, linenr_T line1, line } // getDeepestNesting() {{{2 -/* - * Get the lowest 'foldlevel' value that makes the deepest nested fold in the - * current window open. - */ +/// Get the lowest 'foldlevel' value that makes the deepest nested fold in +/// window `wp`. int getDeepestNesting(win_T *wp) { checkupdate(wp); @@ -1633,7 +1575,7 @@ static void checkSmall(win_T *const wp, fold_T *const fp, const linenr_T lnum_of } // setSmallMaybe() {{{2 -// Set small flags in "gap" to kNone. +/// Set small flags in "gap" to kNone. static void setSmallMaybe(garray_T *gap) { fold_T *fp = (fold_T *)gap->ga_data; @@ -1643,10 +1585,8 @@ static void setSmallMaybe(garray_T *gap) } // foldCreateMarkers() {{{2 -/* - * Create a fold from line "start" to line "end" (inclusive) in the current - * window by adding markers. - */ +/// Create a fold from line "start" to line "end" (inclusive) in window `wp` +/// by adding markers. static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) { buf_T *buf = wp->w_buffer; @@ -1672,9 +1612,7 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end) } // foldAddMarker() {{{2 -/* - * Add "marker[markerlen]" in 'commentstring' to line "lnum". - */ +/// Add "marker[markerlen]" in 'commentstring' to position `pos`. static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen) { char_u *cms = buf->b_p_cms; @@ -1731,11 +1669,10 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu } // foldDelMarker() {{{2 -// -// Delete marker "marker[markerlen]" at the end of line "lnum". -// Delete 'commentstring' if it matches. -// If the marker is not found, there is no error message. Could be a missing -// close-marker. +/// Delete marker "marker[markerlen]" at the end of line "lnum". +/// Delete 'commentstring' if it matches. +/// If the marker is not found, there is no error message. Could be a missing +/// close-marker. static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t markerlen) { char_u *newline; @@ -1892,9 +1829,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin } // foldtext_cleanup() {{{2 -/* - * Remove 'foldmarker' and 'commentstring' from "str" (in-place). - */ +/// Remove 'foldmarker' and 'commentstring' from "str" (in-place). void foldtext_cleanup(char_u *str) { char_u *s; @@ -1974,10 +1909,8 @@ void foldtext_cleanup(char_u *str) // Function declarations. {{{2 // foldUpdateIEMS() {{{2 -/* - * Update the folding for window "wp", at least from lines "top" to "bot". - * IEMS = "Indent Expr Marker Syntax" - */ +/// Update the folding for window "wp", at least from lines "top" to "bot". +/// IEMS = "Indent Expr Marker Syntax" static void foldUpdateIEMS(win_T *const wp, linenr_T top, linenr_T bot) { fline_T fline; @@ -2640,9 +2573,7 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *const gap, const int level, } // foldInsert() {{{2 -/* - * Insert a new fold in "gap" at position "i". - */ +/// Insert a new fold in "gap" at position "i". static void foldInsert(garray_T *gap, int i) { fold_T *fp; @@ -2658,13 +2589,11 @@ static void foldInsert(garray_T *gap, int i) } // foldSplit() {{{2 -/* - * Split the "i"th fold in "gap", which starts before "top" and ends below - * "bot" in two pieces, one ending above "top" and the other starting below - * "bot". - * The caller must first have taken care of any nested folds from "top" to - * "bot"! - */ +/// Split the "i"th fold in "gap", which starts before "top" and ends below +/// "bot" in two pieces, one ending above "top" and the other starting below +/// "bot". +/// The caller must first have taken care of any nested folds from "top" to +/// "bot"! static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr_T top, const linenr_T bot) { @@ -2704,24 +2633,22 @@ static void foldSplit(buf_T *buf, garray_T *const gap, const int i, const linenr } // foldRemove() {{{2 -/* - * Remove folds within the range "top" to and including "bot". - * Check for these situations: - * 1 2 3 - * 1 2 3 - * top 2 3 4 5 - * 2 3 4 5 - * bot 2 3 4 5 - * 3 5 6 - * 3 5 6 - * - * 1: not changed - * 2: truncate to stop above "top" - * 3: split in two parts, one stops above "top", other starts below "bot". - * 4: deleted - * 5: made to start below "bot". - * 6: not changed - */ +/// Remove folds within the range "top" to and including "bot". +/// Check for these situations: +/// 1 2 3 +/// 1 2 3 +/// top 2 3 4 5 +/// 2 3 4 5 +/// bot 2 3 4 5 +/// 3 5 6 +/// 3 5 6 +/// +/// 1: not changed +/// 2: truncate to stop above "top" +/// 3: split in two parts, one stops above "top", other starts below "bot". +/// 4: deleted +/// 5: made to start below "bot". +/// 6: not changed static void foldRemove(win_T *const wp, garray_T *gap, linenr_T top, linenr_T bot) { fold_T *fp = NULL; @@ -2786,35 +2713,35 @@ static void foldReverseOrder(garray_T *gap, const linenr_T start_arg, const line } // foldMoveRange() {{{2 -// Move folds within the inclusive range "line1" to "line2" to after "dest" -// require "line1" <= "line2" <= "dest" -// -// There are the following situations for the first fold at or below line1 - 1. -// 1 2 3 4 -// 1 2 3 4 -// line1 2 3 4 -// 2 3 4 5 6 7 -// line2 3 4 5 6 7 -// 3 4 6 7 8 9 -// dest 4 7 8 9 -// 4 7 8 10 -// 4 7 8 10 -// -// In the following descriptions, "moved" means moving in the buffer, *and* in -// the fold array. -// Meanwhile, "shifted" just means moving in the buffer. -// 1. not changed -// 2. truncated above line1 -// 3. length reduced by line2 - line1, folds starting between the end of 3 and -// dest are truncated and shifted up -// 4. internal folds moved (from [line1, line2] to dest) -// 5. moved to dest. -// 6. truncated below line2 and moved. -// 7. length reduced by line2 - dest, folds starting between line2 and dest are -// removed, top is moved down by move_len. -// 8. truncated below dest and shifted up. -// 9. shifted up -// 10. not changed +/// Move folds within the inclusive range "line1" to "line2" to after "dest" +/// require "line1" <= "line2" <= "dest" +/// +/// There are the following situations for the first fold at or below line1 - 1. +/// 1 2 3 4 +/// 1 2 3 4 +/// line1 2 3 4 +/// 2 3 4 5 6 7 +/// line2 3 4 5 6 7 +/// 3 4 6 7 8 9 +/// dest 4 7 8 9 +/// 4 7 8 10 +/// 4 7 8 10 +/// +/// In the following descriptions, "moved" means moving in the buffer, *and* in +/// the fold array. +/// Meanwhile, "shifted" just means moving in the buffer. +/// 1. not changed +/// 2. truncated above line1 +/// 3. length reduced by line2 - line1, folds starting between the end of 3 and +/// dest are truncated and shifted up +/// 4. internal folds moved (from [line1, line2] to dest) +/// 5. moved to dest. +/// 6. truncated below line2 and moved. +/// 7. length reduced by line2 - dest, folds starting between line2 and dest are +/// removed, top is moved down by move_len. +/// 8. truncated below dest and shifted up. +/// 9. shifted up +/// 10. not changed static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end) { // I want to stop *at here*, foldRemove() stops *above* top @@ -2929,13 +2856,11 @@ void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const l #undef FOLD_INDEX // foldMerge() {{{2 -/* - * Merge two adjacent folds (and the nested ones in them). - * This only works correctly when the folds are really adjacent! Thus "fp1" - * must end just above "fp2". - * The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1". - * Fold entry "fp2" in "gap" is deleted. - */ +/// Merge two adjacent folds (and the nested ones in them). +/// This only works correctly when the folds are really adjacent! Thus "fp1" +/// must end just above "fp2". +/// The resulting fold is "fp1", nested folds are moved from "fp2" to "fp1". +/// Fold entry "fp2" in "gap" is deleted. static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) { fold_T *fp3; @@ -2968,11 +2893,10 @@ static void foldMerge(win_T *const wp, fold_T *fp1, garray_T *gap, fold_T *fp2) } // foldlevelIndent() {{{2 -/* - * Low level function to get the foldlevel for the "indent" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ +/// Low level function to get the foldlevel for the "indent" method. +/// Doesn't use any caching. +/// +/// @return a level of -1 if the foldlevel depends on surrounding lines. static void foldlevelIndent(fline_T *flp) { char_u *s; @@ -3000,10 +2924,8 @@ static void foldlevelIndent(fline_T *flp) } // foldlevelDiff() {{{2 -/* - * Low level function to get the foldlevel for the "diff" method. - * Doesn't use any caching. - */ +/// Low level function to get the foldlevel for the "diff" method. +/// Doesn't use any caching. static void foldlevelDiff(fline_T *flp) { if (diff_infold(flp->wp, flp->lnum + flp->off)) { @@ -3014,11 +2936,10 @@ static void foldlevelDiff(fline_T *flp) } // foldlevelExpr() {{{2 -/* - * Low level function to get the foldlevel for the "expr" method. - * Doesn't use any caching. - * Returns a level of -1 if the foldlevel depends on surrounding lines. - */ +/// Low level function to get the foldlevel for the "expr" method. +/// Doesn't use any caching. +/// +/// @return a level of -1 if the foldlevel depends on surrounding lines. static void foldlevelExpr(fline_T *flp) { win_T *win; @@ -3113,11 +3034,9 @@ static void foldlevelExpr(fline_T *flp) } // parseMarker() {{{2 -/* - * Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and - * "foldendmarkerlen". - * Relies on the option value to have been checked for correctness already. - */ +/// Parse 'foldmarker' and set "foldendmarker", "foldstartmarkerlen" and +/// "foldendmarkerlen". +/// Relies on the option value to have been checked for correctness already. static void parseMarker(win_T *wp) { foldendmarker = vim_strchr(wp->w_p_fmr, ','); @@ -3126,15 +3045,13 @@ static void parseMarker(win_T *wp) } // foldlevelMarker() {{{2 -/* - * Low level function to get the foldlevel for the "marker" method. - * "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been - * set before calling this. - * Requires that flp->lvl is set to the fold level of the previous line! - * Careful: This means you can't call this function twice on the same line. - * Doesn't use any caching. - * Sets flp->start when a start marker was found. - */ +/// Low level function to get the foldlevel for the "marker" method. +/// "foldendmarker", "foldstartmarkerlen" and "foldendmarkerlen" must have been +/// set before calling this. +/// Requires that flp->lvl is set to the fold level of the previous line! +/// Careful: This means you can't call this function twice on the same line. +/// Doesn't use any caching. +/// Sets flp->start when a start marker was found. static void foldlevelMarker(fline_T *flp) { char_u *startmarker; @@ -3205,10 +3122,8 @@ static void foldlevelMarker(fline_T *flp) } // foldlevelSyntax() {{{2 -/* - * Low level function to get the foldlevel for the "syntax" method. - * Doesn't use any caching. - */ +/// Low level function to get the foldlevel for the "syntax" method. +/// Doesn't use any caching. static void foldlevelSyntax(fline_T *flp) { linenr_T lnum = flp->lnum + flp->off; @@ -3228,11 +3143,9 @@ static void foldlevelSyntax(fline_T *flp) // functions for storing the fold state in a View {{{1 // put_folds() {{{2 - -/* - * Write commands to "fd" to restore the manual folds in window "wp". - * Return FAIL if writing fails. - */ +/// Write commands to "fd" to restore the manual folds in window "wp". +/// +/// @return FAIL if writing fails. int put_folds(FILE *fd, win_T *wp) { if (foldmethodIsManual(wp)) { @@ -3252,10 +3165,9 @@ int put_folds(FILE *fd, win_T *wp) } // put_folds_recurse() {{{2 -/* - * Write commands to "fd" to recreate manually created folds. - * Returns FAIL when writing failed. - */ +/// Write commands to "fd" to recreate manually created folds. +/// +/// @return FAIL when writing failed. static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) { fold_T *fp = (fold_T *)gap->ga_data; @@ -3276,10 +3188,9 @@ static int put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) } // put_foldopen_recurse() {{{2 -/* - * Write commands to "fd" to open and close manually opened/closed folds. - * Returns FAIL when writing failed. - */ +/// Write commands to "fd" to open and close manually opened/closed folds. +/// +/// @return FAIL when writing failed. static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off) { int level; @@ -3325,10 +3236,9 @@ static int put_foldopen_recurse(FILE *fd, win_T *wp, garray_T *gap, linenr_T off } // put_fold_open_close() {{{2 -/* - * Write the open or close command to "fd". - * Returns FAIL when writing failed. - */ +/// Write the open or close command to "fd". +/// +/// @return FAIL when writing failed. static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) { if (fprintf(fd, "%" PRId64, (int64_t)(fp->fd_top + off)) < 0 -- cgit From 1784842899c557f26f0aa969f17f3c6a2e079daa Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(ex_docmd): convert function comments to doxygen format --- src/nvim/ex_docmd.c | 670 +++++++++++++++++++--------------------------------- 1 file changed, 244 insertions(+), 426 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 09cf6601ee..76bf4d60c1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -249,8 +249,9 @@ void do_exmode(void) msg_scroll = save_msg_scroll; } -// Print the executed command for when 'verbose' is set. -// When "lnum" is 0 only print the command. +/// Print the executed command for when 'verbose' is set. +/// +/// @param lnum if 0, only print the command. static void msg_verbose_cmd(linenr_T lnum, char_u *cmd) FUNC_ATTR_NONNULL_ALL { @@ -270,9 +271,7 @@ static void msg_verbose_cmd(linenr_T lnum, char_u *cmd) no_wait_return--; } -/* - * Execute a simple command line. Used for translated commands like "*". - */ +/// Execute a simple command line. Used for translated commands like "*". int do_cmdline_cmd(const char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, @@ -951,9 +950,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) return retval; } -/* - * Obtain a line when inside a ":while" or ":for" loop. - */ +/// Obtain a line when inside a ":while" or ":for" loop. static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) { struct loop_cookie *cp = (struct loop_cookie *)cookie; @@ -985,9 +982,7 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) return vim_strsave(wp->line); } -/* - * Store a line in "gap" so that a ":while" loop can execute it again. - */ +/// Store a line in "gap" so that a ":while" loop can execute it again. static void store_loop_line(garray_T *gap, char_u *line) { wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap); @@ -1037,11 +1032,10 @@ void *getline_cookie(LineGetter fgetline, void *cookie) return cp; } -/* - * Helper function to apply an offset for buffer commands, i.e. ":bdelete", - * ":bwipeout", etc. - * Returns the buffer number. - */ +/// Helper function to apply an offset for buffer commands, i.e. ":bdelete", +/// ":bwipeout", etc. +/// +/// @return the buffer number. static int compute_buffer_local_count(int addr_type, int lnum, int offset) { buf_T *buf; @@ -1083,8 +1077,8 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) return buf->b_fnum; } -// Return the window number of "win". -// When "win" is NULL return the number of windows. +/// @return the window number of "win" or, +/// the number of windows if "win" is NULL static int current_win_nr(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -2048,18 +2042,20 @@ char *ex_errmsg(const char *const msg, const char_u *const arg) return ex_error_buf; } -// Parse and skip over command modifiers: -// - update eap->cmd -// - store flags in "cmdmod". -// - Set ex_pressedreturn for an empty command line. -// - set msg_silent for ":silent" -// - set 'eventignore' to "all" for ":noautocmd" -// - set p_verbose for ":verbose" -// - Increment "sandbox" for ":sandbox" -// When "skip_only" is true the global variables are not changed, except for -// "cmdmod". -// Return FAIL when the command is not to be executed. -// May set "errormsg" to an error message. +/// Parse and skip over command modifiers: +/// - update eap->cmd +/// - store flags in "cmdmod". +/// - Set ex_pressedreturn for an empty command line. +/// - set msg_silent for ":silent" +/// - set 'eventignore' to "all" for ":noautocmd" +/// - set p_verbose for ":verbose" +/// - Increment "sandbox" for ":sandbox" +/// +/// @param skip_only if true, the global variables are not changed, except for +/// "cmdmod". +/// @param[out] errormsg potential error message. +/// +/// @return FAIL when the command is not to be executed. int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) { char_u *p; @@ -2318,7 +2314,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) return OK; } -// Undo and free contents of "cmdmod". +/// Undo and free contents of "cmdmod". static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) FUNC_ATTR_NONNULL_ALL { @@ -2357,9 +2353,10 @@ static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) } -// Parse the address range, if any, in "eap". -// May set the last search pattern, unless "silent" is true. -// Return FAIL and set "errormsg" or return OK. +/// Parse the address range, if any, in "eap". +/// May set the last search pattern, unless "silent" is true. +/// +/// @return FAIL and set "errormsg" or return OK. int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) FUNC_ATTR_NONNULL_ALL { @@ -2551,11 +2548,9 @@ int checkforcmd(char_u **pp, char *cmd, int len) return FALSE; } -/* - * Append "cmd" to the error message in IObuff. - * Takes care of limiting the length and handling 0xa0, which would be - * invisible otherwise. - */ +/// Append "cmd" to the error message in IObuff. +/// Takes care of limiting the length and handling 0xa0, which would be +/// invisible otherwise. static void append_command(char_u *cmd) { char_u *s = cmd; @@ -2575,11 +2570,12 @@ static void append_command(char_u *cmd) *d = NUL; } -// Find an Ex command by its name, either built-in or user. -// Start of the name can be found at eap->cmd. -// Sets eap->cmdidx and returns a pointer to char after the command name. -// "full" is set to TRUE if the whole command name matched. -// Returns NULL for an ambiguous user command. +/// Find an Ex command by its name, either built-in or user. +/// Start of the name can be found at eap->cmd. +/// Sets eap->cmdidx and returns a pointer to char after the command name. +/// "full" is set to TRUE if the whole command name matched. +/// +/// @return NULL for an ambiguous user command. static char_u *find_command(exarg_T *eap, int *full) FUNC_ATTR_NONNULL_ARG(1) { @@ -2833,10 +2829,8 @@ static struct cmdmod { { "vertical", 4, false }, }; -/* - * Return length of a command modifier (including optional count). - * Return zero when it's not a modifier. - */ +/// @return length of a command modifier (including optional count) or, +/// zero when it's not a modifier. int modifier_len(char_u *cmd) { char_u *p = cmd; @@ -2860,11 +2854,9 @@ int modifier_len(char_u *cmd) return 0; } -/* - * Return > 0 if an Ex command "name" exists. - * Return 2 if there is an exact match. - * Return 3 if there is an ambiguous match. - */ +/// @return > 0 if an Ex command "name" exists or, +/// 2 if there is an exact match or, +/// 3 if there is an ambiguous match. int cmd_exists(const char *const name) { exarg_T ea; @@ -2901,7 +2893,7 @@ int cmd_exists(const char *const name) return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1); } -// "fullcommand" function +/// "fullcommand" function void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { exarg_T ea; @@ -4191,9 +4183,7 @@ error: return lnum; } -/* - * Get flags from an Ex command argument. - */ +/// Get flags from an Ex command argument. static void get_flags(exarg_T *eap) { while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) { @@ -4228,10 +4218,9 @@ static void ex_script_ni(exarg_T *eap) } } -/* - * Check range in Ex command for validity. - * Return NULL when valid, error message when invalid. - */ +/// Check range in Ex command for validity. +/// +/// @return NULL when valid, error message when invalid. static char *invalid_range(exarg_T *eap) { buf_T *buf; @@ -4317,9 +4306,7 @@ static char *invalid_range(exarg_T *eap) return NULL; } -/* - * Correct the range for zero line number, if required. - */ +/// Correct the range for zero line number, if required. static void correct_range(exarg_T *eap) { if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed @@ -4333,10 +4320,8 @@ static void correct_range(exarg_T *eap) } -/* - * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the - * pattern. Otherwise return eap->arg. - */ +/// For a ":vimgrep" or ":vimgrepadd" command return a pointer past the +/// pattern. Otherwise return eap->arg. static char_u *skip_grep_pat(exarg_T *eap) { char_u *p = eap->arg; @@ -4353,10 +4338,8 @@ static char_u *skip_grep_pat(exarg_T *eap) return p; } -/* - * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option - * in the command line, so that things like % get expanded. - */ +/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option +/// in the command line, so that things like % get expanded. static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) { char_u *new_cmdline; @@ -4424,9 +4407,10 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) return p; } -// Expand file name in Ex command argument. -// When an error is detected, "errormsgp" is set to a non-NULL pointer. -// Return FAIL for failure, OK otherwise. +/// Expand file name in Ex command argument. +/// When an error is detected, "errormsgp" is set to a non-NULL pointer. +/// +/// @return FAIL for failure, OK otherwise. int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) { int has_wildcards; // need to expand wildcards @@ -4594,13 +4578,12 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) return OK; } -/* - * Replace part of the command line, keeping eap->cmd, eap->arg and - * eap->nextcmd correct. - * "src" points to the part that is to be replaced, of length "srclen". - * "repl" is the replacement string. - * Returns a pointer to the character after the replaced string. - */ +/// Replace part of the command line, keeping eap->cmd, eap->arg and +/// eap->nextcmd correct. +/// "src" points to the part that is to be replaced, of length "srclen". +/// "repl" is the replacement string. +/// +/// @return a pointer to the character after the replaced string. static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl, char_u **cmdlinep) { @@ -4646,9 +4629,7 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re return src; } -/* - * Check for '|' to separate commands and '"' to start comments. - */ +/// Check for '|' to separate commands and '"' to start comments. void separate_nextcmd(exarg_T *eap) { char_u *p; @@ -4703,9 +4684,7 @@ void separate_nextcmd(exarg_T *eap) } } -/* - * get + command from ex argument - */ +/// get + command from ex argument static char_u *getargcmd(char_u **argp) { char_u *arg = *argp; @@ -4762,10 +4741,9 @@ int get_bad_opt(const char_u *p, exarg_T *eap) return OK; } -/* - * Get "++opt=arg" argument. - * Return FAIL or OK. - */ +/// Get "++opt=arg" argument. +/// +/// @return FAIL or OK. static int getargopt(exarg_T *eap) { char_u *arg = eap->arg + 2; @@ -4845,8 +4823,9 @@ static int getargopt(exarg_T *eap) } /// Handle the argument for a tabpage related ex command. -/// Returns a tabpage number. /// When an error is encountered then eap->errmsg is set. +/// +/// @return a tabpage number. static int get_tabpage_arg(exarg_T *eap) { int tab_number = 0; @@ -4935,17 +4914,13 @@ theend: return tab_number; } -/* - * ":abbreviate" and friends. - */ +/// ":abbreviate" and friends. static void ex_abbreviate(exarg_T *eap) { do_exmap(eap, TRUE); // almost the same as mapping } -/* - * ":map" and friends. - */ +/// ":map" and friends. static void ex_map(exarg_T *eap) { /* @@ -4960,25 +4935,19 @@ static void ex_map(exarg_T *eap) do_exmap(eap, FALSE); } -/* - * ":unmap" and friends. - */ +/// ":unmap" and friends. static void ex_unmap(exarg_T *eap) { do_exmap(eap, FALSE); } -/* - * ":mapclear" and friends. - */ +/// ":mapclear" and friends. static void ex_mapclear(exarg_T *eap) { map_clear_mode(eap->cmd, eap->arg, eap->forceit, false); } -/* - * ":abclear" and friends. - */ +/// ":abclear" and friends. static void ex_abclear(exarg_T *eap) { map_clear_mode(eap->cmd, eap->arg, true, true); @@ -4998,9 +4967,7 @@ static void ex_autocmd(exarg_T *eap) } } -/* - * ":doautocmd": Apply the automatic commands to the current buffer. - */ +/// ":doautocmd": Apply the automatic commands to the current buffer. static void ex_doautocmd(exarg_T *eap) { char_u *arg = eap->arg; @@ -5014,11 +4981,9 @@ static void ex_doautocmd(exarg_T *eap) } } -/* - * :[N]bunload[!] [N] [bufname] unload buffer - * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list - * :[N]bwipeout[!] [N] [bufname] delete buffer really - */ +/// :[N]bunload[!] [N] [bufname] unload buffer +/// :[N]bdelete[!] [N] [bufname] delete buffer from buffer list +/// :[N]bwipeout[!] [N] [bufname] delete buffer really static void ex_bunload(exarg_T *eap) { eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete ? DOBUF_DEL @@ -5028,10 +4993,8 @@ static void ex_bunload(exarg_T *eap) eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit); } -/* - * :[N]buffer [N] to buffer N - * :[N]sbuffer [N] to buffer N - */ +/// :[N]buffer [N] to buffer N +/// :[N]sbuffer [N] to buffer N static void ex_buffer(exarg_T *eap) { if (*eap->arg) { @@ -5048,10 +5011,8 @@ static void ex_buffer(exarg_T *eap) } } -/* - * :[N]bmodified [N] to next mod. buffer - * :[N]sbmodified [N] to next mod. buffer - */ +/// :[N]bmodified [N] to next mod. buffer +/// :[N]sbmodified [N] to next mod. buffer static void ex_bmodified(exarg_T *eap) { goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2); @@ -5060,10 +5021,8 @@ static void ex_bmodified(exarg_T *eap) } } -/* - * :[N]bnext [N] to next buffer - * :[N]sbnext [N] split and to next buffer - */ +/// :[N]bnext [N] to next buffer +/// :[N]sbnext [N] split and to next buffer static void ex_bnext(exarg_T *eap) { goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2); @@ -5072,12 +5031,10 @@ static void ex_bnext(exarg_T *eap) } } -/* - * :[N]bNext [N] to previous buffer - * :[N]bprevious [N] to previous buffer - * :[N]sbNext [N] split and to previous buffer - * :[N]sbprevious [N] split and to previous buffer - */ +/// :[N]bNext [N] to previous buffer +/// :[N]bprevious [N] to previous buffer +/// :[N]sbNext [N] split and to previous buffer +/// :[N]sbprevious [N] split and to previous buffer static void ex_bprevious(exarg_T *eap) { goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2); @@ -5086,12 +5043,10 @@ static void ex_bprevious(exarg_T *eap) } } -/* - * :brewind to first buffer - * :bfirst to first buffer - * :sbrewind split and to first buffer - * :sbfirst split and to first buffer - */ +/// :brewind to first buffer +/// :bfirst to first buffer +/// :sbrewind split and to first buffer +/// :sbfirst split and to first buffer static void ex_brewind(exarg_T *eap) { goto_buffer(eap, DOBUF_FIRST, FORWARD, 0); @@ -5100,10 +5055,8 @@ static void ex_brewind(exarg_T *eap) } } -/* - * :blast to last buffer - * :sblast split and to last buffer - */ +/// :blast to last buffer +/// :sblast split and to last buffer static void ex_blast(exarg_T *eap) { goto_buffer(eap, DOBUF_LAST, BACKWARD, 0); @@ -5117,10 +5070,8 @@ int ends_excmd(int c) FUNC_ATTR_CONST return c == NUL || c == '|' || c == '"' || c == '\n'; } -/* - * Return the next command, after the first '|' or '\n'. - * Return NULL if not found. - */ +/// @return the next command, after the first '|' or '\n' or, +/// NULL if not found. char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { @@ -5133,7 +5084,8 @@ char_u *find_nextcmd(const char_u *p) } /// Check if *p is a separator between Ex commands, skipping over white space. -/// Return NULL if it isn't, the following character if it is. +/// +/// @return NULL if it isn't, the following character if it is. char_u *check_nextcmd(char_u *p) { char_u *s = skipwhite(p); @@ -5149,9 +5101,10 @@ char_u *check_nextcmd(char_u *p) /// - and this is the last window /// - and forceit not used /// - and not repeated twice on a row -/// @return FAIL and give error message if 'message' TRUE, return OK otherwise /// /// @param message when FALSE check only, no messages +/// +/// @return FAIL and give error message if 'message' TRUE, return OK otherwise static int check_more(int message, bool forceit) { int n = ARGCOUNT - curwin->w_arg_idx - 1; @@ -5179,9 +5132,7 @@ static int check_more(int message, bool forceit) return OK; } -/* - * Function given to ExpandGeneric() to obtain the list of command names. - */ +/// Function given to ExpandGeneric() to obtain the list of command names. char_u *get_command_name(expand_T *xp, int idx) { if (idx >= CMD_SIZE) { @@ -5692,9 +5643,7 @@ invalid_count: static char e_complete_used_without_nargs[] = N_("E1208: -complete used without -nargs"); -/* - * ":command ..." - */ +/// ":command ..." static void ex_command(exarg_T *eap) { char_u *name; @@ -5748,10 +5697,8 @@ static void ex_command(exarg_T *eap) } } -/* - * ":comclear" - * Clear all user commands, global and for current buffer. - */ +/// ":comclear" +/// Clear all user commands, global and for current buffer. void ex_comclear(exarg_T *eap) { uc_clear(&ucmds); @@ -5767,9 +5714,7 @@ void free_ucmd(ucmd_T *cmd) NLUA_CLEAR_REF(cmd->uc_luaref); } -/* - * Clear all user commands for "gap". - */ +/// Clear all user commands for "gap". void uc_clear(garray_T *gap) { GA_DEEP_CLEAR(gap, ucmd_T, free_ucmd); @@ -5830,6 +5775,7 @@ static void ex_delcommand(exarg_T *eap) /// @param[out] start Start of the split /// @param[out] end End of the split /// @param[in] length Length of the string +/// /// @return false if it's the last split (don't call again), true otherwise (call again). bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int length) { @@ -5845,9 +5791,7 @@ bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int l return false; } -/* - * split and quote args for - */ +/// split and quote args for static char_u *uc_split_args(char_u *arg, size_t *lenp) { char_u *buf; @@ -6360,17 +6304,14 @@ static char_u *expand_user_command_name(int idx) { return get_user_commands(NULL, idx - CMD_SIZE); } -/* - * Function given to ExpandGeneric() to obtain the list of user address type names. - */ + +/// Function given to ExpandGeneric() to obtain the list of user address type names. char_u *get_user_cmd_addr_type(expand_T *xp, int idx) { return (char_u *)addr_type_complete[idx].name; } -/* - * Function given to ExpandGeneric() to obtain the list of user command names. - */ +/// Function given to ExpandGeneric() to obtain the list of user command names. char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -6387,9 +6328,10 @@ char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) return NULL; } -// Get the name of user command "idx". "cmdidx" can be CMD_USER or -// CMD_USER_BUF. -// Returns NULL if the command is not found. +/// Get the name of user command "idx". "cmdidx" can be CMD_USER or +/// CMD_USER_BUF. +/// +/// @return NULL if the command is not found. static char_u *get_user_command_name(int idx, int cmdidx) { if (cmdidx == CMD_USER && idx < ucmds.ga_len) { @@ -6406,10 +6348,8 @@ static char_u *get_user_command_name(int idx, int cmdidx) return NULL; } -/* - * Function given to ExpandGeneric() to obtain the list of user command - * attributes. - */ +/// Function given to ExpandGeneric() to obtain the list of user command +/// attributes. char_u *get_user_cmd_flags(expand_T *xp, int idx) { static char *user_cmd_flags[] = { "addr", "bang", "bar", @@ -6422,9 +6362,7 @@ char_u *get_user_cmd_flags(expand_T *xp, int idx) return (char_u *)user_cmd_flags[idx]; } -/* - * Function given to ExpandGeneric() to obtain the list of values for -nargs. - */ +/// Function given to ExpandGeneric() to obtain the list of values for -nargs. char_u *get_user_cmd_nargs(expand_T *xp, int idx) { static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" }; @@ -6435,9 +6373,7 @@ char_u *get_user_cmd_nargs(expand_T *xp, int idx) return (char_u *)user_cmd_nargs[idx]; } -/* - * Function given to ExpandGeneric() to obtain the list of values for -complete. - */ +/// Function given to ExpandGeneric() to obtain the list of values for -complete. char_u *get_user_cmd_complete(expand_T *xp, int idx) { if (idx >= (int)ARRAY_SIZE(command_complete)) { @@ -6451,9 +6387,7 @@ char_u *get_user_cmd_complete(expand_T *xp, int idx) } } -/* - * Parse address type argument - */ +/// Parse address type argument int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) FUNC_ATTR_NONNULL_ALL { @@ -6480,13 +6414,12 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) return OK; } -/* - * Parse a completion argument "value[vallen]". - * The detected completion goes in "*complp", argument type in "*argt". - * When there is an argument, for function and user defined completion, it's - * copied to allocated memory and stored in "*compl_arg". - * Returns FAIL if something is wrong. - */ +/// Parse a completion argument "value[vallen]". +/// The detected completion goes in "*complp", argument type in "*argt". +/// When there is an argument, for function and user defined completion, it's +/// copied to allocated memory and stored in "*compl_arg". +/// +/// @return FAIL if something is wrong. int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, char_u **compl_arg) FUNC_ATTR_NONNULL_ALL @@ -6591,10 +6524,8 @@ static void ex_highlight(exarg_T *eap) } -/* - * Call this function if we thought we were going to exit, but we won't - * (because of an error). May need to restore the terminal mode. - */ +/// Call this function if we thought we were going to exit, but we won't +/// (because of an error). May need to restore the terminal mode. void not_exiting(void) { exiting = false; @@ -6629,8 +6560,8 @@ bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit) return false; } -// ":quit": quit current window, quit Vim if the last window is closed. -// ":{nr}quit": quit window {nr} +/// ":quit": quit current window, quit Vim if the last window is closed. +/// ":{nr}quit": quit window {nr} static void ex_quit(exarg_T *eap) { if (cmdwin_type != 0) { @@ -6730,9 +6661,7 @@ static void ex_quit_all(exarg_T *eap) not_exiting(); } -/* - * ":close": close current window, unless it is the last one - */ +/// ":close": close current window, unless it is the last one static void ex_close(exarg_T *eap) { win_T *win = NULL; @@ -6758,9 +6687,7 @@ static void ex_close(exarg_T *eap) } } -/* - * ":pclose": Close any preview window. - */ +/// ":pclose": Close any preview window. static void ex_pclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { @@ -6811,10 +6738,8 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp) } } -/* - * ":tabclose": close current tab page, unless it is the last one. - * ":tabclose N": close tab page N. - */ +/// ":tabclose": close current tab page, unless it is the last one. +/// ":tabclose N": close tab page N. static void ex_tabclose(exarg_T *eap) { tabpage_T *tp; @@ -6875,9 +6800,7 @@ static void ex_tabonly(exarg_T *eap) } } -/* - * Close the current tab page. - */ +/// Close the current tab page. void tabpage_close(int forceit) { // First close all the windows but the current one. If that worked then @@ -6893,12 +6816,10 @@ void tabpage_close(int forceit) } } -/* - * Close tab page "tp", which is not the current tab page. - * Note that autocommands may make "tp" invalid. - * Also takes care of the tab pages line disappearing when closing the - * last-but-one tab page. - */ +/// Close tab page "tp", which is not the current tab page. +/// Note that autocommands may make "tp" invalid. +/// Also takes care of the tab pages line disappearing when closing the +/// last-but-one tab page. void tabpage_close_other(tabpage_T *tp, int forceit) { int done = 0; @@ -6926,9 +6847,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) } } -/* - * ":only". - */ +/// ":only". static void ex_only(exarg_T *eap) { win_T *wp; @@ -6952,10 +6871,8 @@ static void ex_only(exarg_T *eap) close_others(TRUE, eap->forceit); } -/* - * ":all" and ":sall". - * Also used for ":tab drop file ..." after setting the argument list. - */ +/// ":all" and ":sall". +/// Also used for ":tab drop file ..." after setting the argument list. void ex_all(exarg_T *eap) { if (eap->addr_count == 0) { @@ -7010,7 +6927,7 @@ static void ex_stop(exarg_T *eap) apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL); } -// ":exit", ":xit" and ":wq": Write file and quit the current window. +/// ":exit", ":xit" and ":wq": Write file and quit the current window. static void ex_exit(exarg_T *eap) { if (cmdwin_type != 0) { @@ -7046,9 +6963,7 @@ static void ex_exit(exarg_T *eap) } } -/* - * ":print", ":list", ":number". - */ +/// ":print", ":list", ":number". static void ex_print(exarg_T *eap) { if (curbuf->b_ml.ml_flags & ML_EMPTY) { @@ -7078,29 +6993,23 @@ static void ex_goto(exarg_T *eap) goto_byte(eap->line2); } -/* - * Clear an argument list: free all file names and reset it to zero entries. - */ +/// Clear an argument list: free all file names and reset it to zero entries. void alist_clear(alist_T *al) { #define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME); } -/* - * Init an argument list. - */ +/// Init an argument list. void alist_init(alist_T *al) { ga_init(&al->al_ga, (int)sizeof(aentry_T), 5); } -/* - * Remove a reference from an argument list. - * Ignored when the argument list is the global one. - * If the argument list is no longer used by any window, free it. - */ +/// Remove a reference from an argument list. +/// Ignored when the argument list is the global one. +/// If the argument list is no longer used by any window, free it. void alist_unlink(alist_T *al) { if (al != &global_alist && --al->al_refcount <= 0) { @@ -7109,9 +7018,7 @@ void alist_unlink(alist_T *al) } } -/* - * Create a new argument list and use it for the current window. - */ +/// Create a new argument list and use it for the current window. void alist_new(void) { curwin->w_alist = xmalloc(sizeof(*curwin->w_alist)); @@ -7121,11 +7028,10 @@ void alist_new(void) } #if !defined(UNIX) -/* - * Expand the file names in the global argument list. - * If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer - * numbers to be re-used. - */ + +/// Expand the file names in the global argument list. +/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer +/// numbers to be re-used. void alist_expand(int *fnum_list, int fnum_len) { char_u **old_arg_files; @@ -7156,10 +7062,8 @@ void alist_expand(int *fnum_list, int fnum_len) } #endif -/* - * Set the argument list for the current window. - * Takes over the allocated files[] and the allocated fnames in it. - */ +/// Set the argument list for the current window. +/// Takes over the allocated files[] and the allocated fnames in it. void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len) { int i; @@ -7223,9 +7127,8 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum) } #if defined(BACKSLASH_IN_FILENAME) -/* - * Adjust slashes in file names. Called after 'shellslash' was set. - */ + +/// Adjust slashes in file names. Called after 'shellslash' was set. void alist_slash_adjust(void) { for (int i = 0; i < GARGCOUNT; ++i) { @@ -7271,27 +7174,23 @@ static void ex_recover(exarg_T *eap) recoverymode = false; } -/* - * Command modifier used in a wrong way. - */ +/// Command modifier used in a wrong way. static void ex_wrongmodifier(exarg_T *eap) { eap->errmsg = e_invcmd; } -/* - * :sview [+command] file split window with new file, read-only - * :split [[+command] file] split window with current or new file - * :vsplit [[+command] file] split window vertically with current or new file - * :new [[+command] file] split window with no or new file - * :vnew [[+command] file] split vertically window with no or new file - * :sfind [+command] file split window with file in 'path' - * - * :tabedit open new Tab page with empty window - * :tabedit [+command] file open new Tab page and edit "file" - * :tabnew [[+command] file] just like :tabedit - * :tabfind [+command] file open new Tab page and find "file" - */ +/// :sview [+command] file split window with new file, read-only +/// :split [[+command] file] split window with current or new file +/// :vsplit [[+command] file] split window vertically with current or new file +/// :new [[+command] file] split window with no or new file +/// :vnew [[+command] file] split vertically window with no or new file +/// :sfind [+command] file split window with file in 'path' +/// +/// :tabedit open new Tab page with empty window +/// :tabedit [+command] file open new Tab page and edit "file" +/// :tabnew [[+command] file] just like :tabedit +/// :tabfind [+command] file open new Tab page and find "file" void ex_splitview(exarg_T *eap) { win_T *old_curwin = curwin; @@ -7354,9 +7253,7 @@ theend: xfree(fname); } -/* - * Open a new tab page. - */ +/// Open a new tab page. void tabpage_new(void) { exarg_T ea; @@ -7368,9 +7265,7 @@ void tabpage_new(void) ex_splitview(&ea); } -/* - * :tabnext command - */ +/// :tabnext command static void ex_tabnext(exarg_T *eap) { int tab_number; @@ -7417,9 +7312,7 @@ static void ex_tabnext(exarg_T *eap) } } -/* - * :tabmove command - */ +/// :tabmove command static void ex_tabmove(exarg_T *eap) { int tab_number = get_tabpage_arg(eap); @@ -7428,9 +7321,7 @@ static void ex_tabmove(exarg_T *eap) } } -/* - * :tabs command: List tabs and their contents. - */ +/// :tabs command: List tabs and their contents. static void ex_tabs(exarg_T *eap) { int tabcount = 1; @@ -7476,10 +7367,8 @@ static void ex_tabs(exarg_T *eap) } -/* - * ":mode": - * If no argument given, get the screen size and redraw. - */ +/// ":mode": +/// If no argument given, get the screen size and redraw. static void ex_mode(exarg_T *eap) { if (*eap->arg == NUL) { @@ -7490,10 +7379,8 @@ static void ex_mode(exarg_T *eap) } } -/* - * ":resize". - * set, increment or decrement current window height - */ +/// ":resize". +/// set, increment or decrement current window height static void ex_resize(exarg_T *eap) { int n; @@ -7523,9 +7410,7 @@ static void ex_resize(exarg_T *eap) } } -/* - * ":find [+command] " command. - */ +/// ":find [+command] " command. static void ex_find(exarg_T *eap) { char_u *fname; @@ -7702,11 +7587,9 @@ static void ex_swapname(exarg_T *eap) } } -/* - * ":syncbind" forces all 'scrollbind' windows to have the same relative - * offset. - * (1998-11-02 16:21:01 R. Edward Ralston ) - */ +/// ":syncbind" forces all 'scrollbind' windows to have the same relative +/// offset. +/// (1998-11-02 16:21:01 R. Edward Ralston ) static void ex_syncbind(exarg_T *eap) { win_T *save_curwin = curwin; @@ -7837,7 +7720,7 @@ void free_cd_dir(void) #endif -// Get the previous directory for the given chdir scope. +/// Get the previous directory for the given chdir scope. static char_u *get_prevdir(CdScope scope) { switch (scope) { @@ -8002,9 +7885,7 @@ void ex_cd(exarg_T *eap) } } -/* - * ":pwd". - */ +/// ":pwd". static void ex_pwd(exarg_T *eap) { if (os_dirname(NameBuff, MAXPATHL) == OK) { @@ -8029,9 +7910,7 @@ static void ex_pwd(exarg_T *eap) } } -/* - * ":=". - */ +/// ":=". static void ex_equal(exarg_T *eap) { smsg("%" PRId64, (int64_t)eap->line2); @@ -8062,9 +7941,7 @@ static void ex_sleep(exarg_T *eap) do_sleep(len); } -/* - * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. - */ +/// Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. void do_sleep(long msec) { ui_flush(); // flush before waiting @@ -8100,9 +7977,7 @@ static void do_exmap(exarg_T *eap, int isabbrev) } } -/* - * ":winsize" command (obsolete). - */ +/// ":winsize" command (obsolete). static void ex_winsize(exarg_T *eap) { char_u *arg = eap->arg; @@ -8153,9 +8028,7 @@ static void ex_wincmd(exarg_T *eap) } } -/* - * Handle command that work like operators: ":delete", ":yank", ":>" and ":<". - */ +/// Handle command that work like operators: ":delete", ":yank", ":>" and ":<". static void ex_operators(exarg_T *eap) { oparg_T oa; @@ -8202,9 +8075,7 @@ static void ex_operators(exarg_T *eap) ex_may_print(eap); } -/* - * ":put". - */ +/// ":put". static void ex_put(exarg_T *eap) { // ":0put" works like ":1put!". @@ -8218,9 +8089,7 @@ static void ex_put(exarg_T *eap) PUT_LINE|PUT_CURSLINE); } -/* - * Handle ":copy" and ":move". - */ +/// Handle ":copy" and ":move". static void ex_copymove(exarg_T *eap) { long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1); @@ -8250,9 +8119,7 @@ static void ex_copymove(exarg_T *eap) ex_may_print(eap); } -/* - * Print the current line if flags were given to the Ex command. - */ +/// Print the current line if flags were given to the Ex command. void ex_may_print(exarg_T *eap) { if (eap->flags != 0) { @@ -8272,9 +8139,7 @@ static void ex_submagic(exarg_T *eap) p_magic = magic_save; } -/* - * ":join". - */ +/// ":join". static void ex_join(exarg_T *eap) { curwin->w_cursor.lnum = eap->line1; @@ -8293,9 +8158,7 @@ static void ex_join(exarg_T *eap) ex_may_print(eap); } -/* - * ":[addr]@r": execute register - */ +/// ":[addr]@r": execute register static void ex_at(exarg_T *eap) { int prev_len = typebuf.tb_len; @@ -8331,17 +8194,13 @@ static void ex_at(exarg_T *eap) } } -/* - * ":!". - */ +/// ":!". static void ex_bang(exarg_T *eap) { do_bang(eap->addr_count, eap, eap->forceit, true, true); } -/* - * ":undo". - */ +/// ":undo". static void ex_undo(exarg_T *eap) { if (eap->addr_count == 1) { // :undo 123 @@ -8407,9 +8266,7 @@ static void ex_later(exarg_T *eap) } } -/* - * ":redir": start/stop redirection. - */ +/// ":redir": start/stop redirection. static void ex_redir(exarg_T *eap) { char *mode; @@ -8550,7 +8407,7 @@ static void ex_redrawstatus(exarg_T *eap) ui_flush(); } -// ":redrawtabline": force redraw of the tabline +/// ":redrawtabline": force redraw of the tabline static void ex_redrawtabline(exarg_T *eap FUNC_ATTR_UNUSED) { const int r = RedrawingDisabled; @@ -8624,9 +8481,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode) return fd; } -/* - * ":mark" and ":k". - */ +/// ":mark" and ":k". static void ex_mark(exarg_T *eap) { pos_T pos; @@ -8646,9 +8501,7 @@ static void ex_mark(exarg_T *eap) } } -/* - * Update w_topline, w_leftcol and the cursor position. - */ +/// Update w_topline, w_leftcol and the cursor position. void update_topline_cursor(void) { check_cursor(); // put cursor on valid line @@ -8659,8 +8512,9 @@ void update_topline_cursor(void) update_curswant(); } -// Save the current State and go to Normal mode. -// Return true if the typeahead could be saved. +/// Save the current State and go to Normal mode. +/// +/// @return true if the typeahead could be saved. bool save_current_state(save_state_T *sst) FUNC_ATTR_NONNULL_ALL { @@ -8712,9 +8566,7 @@ void restore_current_state(save_state_T *sst) ui_cursor_shape(); // may show different cursor shape } -/* - * ":normal[!] {commands}": Execute normal mode commands. - */ +/// ":normal[!] {commands}": Execute normal mode commands. static void ex_normal(exarg_T *eap) { if (curbuf->terminal && State & TERM_FOCUS) { @@ -8795,9 +8647,7 @@ static void ex_normal(exarg_T *eap) xfree(arg); } -/* - * ":startinsert", ":startreplace" and ":startgreplace" - */ +/// ":startinsert", ":startreplace" and ":startgreplace" static void ex_startinsert(exarg_T *eap) { if (eap->forceit) { @@ -8834,9 +8684,7 @@ static void ex_startinsert(exarg_T *eap) } } -/* - * ":stopinsert" - */ +/// ":stopinsert" static void ex_stopinsert(exarg_T *eap) { restart_edit = 0; @@ -8844,10 +8692,8 @@ static void ex_stopinsert(exarg_T *eap) clearmode(); } -/* - * Execute normal mode command "cmd". - * "remap" can be REMAP_NONE or REMAP_YES. - */ +/// Execute normal mode command "cmd". +/// "remap" can be REMAP_NONE or REMAP_YES. void exec_normal_cmd(char_u *cmd, int remap, bool silent) { // Stuff the argument into the typeahead buffer. @@ -8880,9 +8726,7 @@ static void ex_checkpath(exarg_T *eap) (linenr_T)1, (linenr_T)MAXLNUM); } -/* - * ":psearch" - */ +/// ":psearch" static void ex_psearch(exarg_T *eap) { g_do_tagpreview = p_pvh; @@ -8945,18 +8789,14 @@ static void ex_findpat(exarg_T *eap) } -/* - * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc. - */ +/// ":ptag", ":ptselect", ":ptjump", ":ptnext", etc. static void ex_ptag(exarg_T *eap) { g_do_tagpreview = p_pvh; // will be reset to 0 in ex_tag_cmd() ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); } -/* - * ":pedit" - */ +/// ":pedit" static void ex_pedit(exarg_T *eap) { win_T *curwin_save = curwin; @@ -8977,9 +8817,7 @@ static void ex_pedit(exarg_T *eap) g_do_tagpreview = 0; } -/* - * ":stag", ":stselect" and ":stjump". - */ +/// ":stag", ":stselect" and ":stjump". static void ex_stag(exarg_T *eap) { postponed_split = -1; @@ -8990,9 +8828,7 @@ static void ex_stag(exarg_T *eap) postponed_split_tab = 0; } -/* - * ":tag", ":tselect", ":tjump", ":tnext", etc. - */ +/// ":tag", ":tselect", ":tjump", ":tnext", etc. static void ex_tag(exarg_T *eap) { ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name); @@ -9061,11 +8897,9 @@ enum { // SPEC_CLIENT, }; -/* - * Check "str" for starting with a special cmdline variable. - * If found return one of the SPEC_ values and set "*usedlen" to the length of - * the variable. Otherwise return -1 and "*usedlen" is unchanged. - */ +/// Check "str" for starting with a special cmdline variable. +/// If found return one of the SPEC_ values and set "*usedlen" to the length of +/// the variable. Otherwise return -1 and "*usedlen" is unchanged. ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) FUNC_ATTR_NONNULL_ALL { @@ -9378,11 +9212,9 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum return result; } -/* - * Concatenate all files in the argument list, separated by spaces, and return - * it in one allocated string. - * Spaces and backslashes in the file names are escaped with a backslash. - */ +/// Concatenate all files in the argument list, separated by spaces, and return +/// it in one allocated string. +/// Spaces and backslashes in the file names are escaped with a backslash. static char_u *arg_all(void) { int len; @@ -9441,11 +9273,9 @@ static char_u *arg_all(void) return retval; } -/* - * Expand the string in "arg". - * - * Returns an allocated string, or NULL for any error. - */ +/// Expand the string in "arg". +/// +/// @return an allocated string, or NULL for any error. char_u *expand_sfile(char_u *arg) { char *errormsg; @@ -9491,9 +9321,7 @@ char_u *expand_sfile(char_u *arg) return result; } -/* - * ":rshada" and ":wshada". - */ +/// ":rshada" and ":wshada". static void ex_shada(exarg_T *eap) { char_u *save_shada; @@ -9510,10 +9338,8 @@ static void ex_shada(exarg_T *eap) p_shada = save_shada; } -/* - * Make a dialog message in "buff[DIALOG_MSG_SIZE]". - * "format" must contain "%s". - */ +/// Make a dialog message in "buff[DIALOG_MSG_SIZE]". +/// "format" must contain "%s". void dialog_msg(char_u *buff, char *format, char_u *fname) { if (fname == NULL) { @@ -9522,9 +9348,7 @@ void dialog_msg(char_u *buff, char *format, char_u *fname) vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname); } -/* - * ":behave {mswin,xterm}" - */ +/// ":behave {mswin,xterm}" static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { @@ -9542,10 +9366,8 @@ static void ex_behave(exarg_T *eap) } } -/* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":behave {mswin,xterm}" command. - */ +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":behave {mswin,xterm}" command. char_u *get_behave_arg(expand_T *xp, int idx) { if (idx == 0) { @@ -9557,8 +9379,8 @@ char_u *get_behave_arg(expand_T *xp, int idx) return NULL; } -// Function given to ExpandGeneric() to obtain the possible arguments of the -// ":messages {clear}" command. +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":messages {clear}" command. char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) { if (idx == 0) { @@ -9579,15 +9401,13 @@ static TriState filetype_detect = kNone; static TriState filetype_plugin = kNone; static TriState filetype_indent = kNone; -/* - * ":filetype [plugin] [indent] {on,off,detect}" - * on: Load the filetype.vim file to install autocommands for file types. - * off: Load the ftoff.vim file to remove all autocommands for file types. - * plugin on: load filetype.vim and ftplugin.vim - * plugin off: load ftplugof.vim - * indent on: load filetype.vim and indent.vim - * indent off: load indoff.vim - */ +/// ":filetype [plugin] [indent] {on,off,detect}" +/// on: Load the filetype.vim file to install autocommands for file types. +/// off: Load the ftoff.vim file to remove all autocommands for file types. +/// plugin on: load filetype.vim and ftplugin.vim +/// plugin off: load ftplugof.vim +/// indent on: load filetype.vim and indent.vim +/// indent off: load indoff.vim static void ex_filetype(exarg_T *eap) { char_u *arg = eap->arg; @@ -9713,18 +9533,16 @@ void set_no_hlsearch(bool flag) set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls); } -/* - * ":nohlsearch" - */ +/// ":nohlsearch" static void ex_nohlsearch(exarg_T *eap) { set_no_hlsearch(true); redraw_all_later(SOME_VALID); } -// ":[N]match {group} {pattern}" -// Sets nextcmd to the start of the next command, if any. Also called when -// skipping commands to find the next command. +/// ":[N]match {group} {pattern}" +/// Sets nextcmd to the start of the next command, if any. Also called when +/// skipping commands to find the next command. static void ex_match(exarg_T *eap) { char_u *p; @@ -9815,8 +9633,8 @@ static void ex_folddo(exarg_T *eap) ml_clearmarked(); // clear rest of the marks } -// Returns true if the supplied Ex cmdidx is for a location list command -// instead of a quickfix command. +/// @return true if the supplied Ex cmdidx is for a location list command +/// instead of a quickfix command. bool is_loclist_cmd(int cmdidx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { -- cgit From 6435a94d62730ccb3d1840d73d7bb80b4ed0871c Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(message): convert function comments to doxygen format --- src/nvim/message.c | 323 ++++++++++++++++++++--------------------------------- 1 file changed, 123 insertions(+), 200 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index b39450cdc6..b3fefbc0f4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -206,11 +206,10 @@ void msg_grid_validate(void) } } -/* - * msg(s) - displays the string 's' on the status line - * When terminal not initialized (yet) mch_errmsg(..) is used. - * return TRUE if wait_return not called - */ +/// Displays the string 's' on the status line +/// When terminal not initialized (yet) mch_errmsg(..) is used. +/// +/// @return TRUE if wait_return not called int msg(char *s) { return msg_attr_keep(s, 0, false, false); @@ -232,7 +231,7 @@ int msg_attr(const char *s, const int attr) return msg_attr_keep(s, attr, false, false); } -/// similar to msg_outtrans_attr, but support newlines and tabs. +/// Similar to msg_outtrans_attr, but support newlines and tabs. void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clear) FUNC_ATTR_NONNULL_ALL { @@ -339,7 +338,8 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) } /// Truncate a string such that it can be printed without causing a scroll. -/// Returns an allocated string or NULL when no truncating is done. +/// +/// @return an allocated string or NULL when no truncating is done. /// /// @param force always truncate char_u *msg_strtrunc(char_u *s, int force) @@ -371,10 +371,8 @@ char_u *msg_strtrunc(char_u *s, int force) return buf; } -/* - * Truncate a string "s" to "buf" with cell width "room". - * "s" and "buf" may be equal. - */ +/// Truncate a string "s" to "buf" with cell width "room". +/// "s" and "buf" may be equal. void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) { size_t room = room_in - 3; // "..." takes 3 chars @@ -498,19 +496,15 @@ int smsg_attr_keep(int attr, const char *s, ...) static int last_sourcing_lnum = 0; static char_u *last_sourcing_name = NULL; -/* - * Reset the last used sourcing name/lnum. Makes sure it is displayed again - * for the next error message; - */ +/// Reset the last used sourcing name/lnum. Makes sure it is displayed again +/// for the next error message; void reset_last_sourcing(void) { XFREE_CLEAR(last_sourcing_name); last_sourcing_lnum = 0; } -/* - * Return TRUE if "sourcing_name" differs from "last_sourcing_name". - */ +/// @return TRUE if "sourcing_name" differs from "last_sourcing_name". static int other_sourcing_name(void) { if (sourcing_name != NULL) { @@ -560,11 +554,9 @@ static char *get_emsg_lnum(void) return NULL; } -/* - * Display name and line number for the source of an error. - * Remember the file name and line number, so that for the next error the info - * is only displayed if it changed. - */ +/// Display name and line number for the source of an error. +/// Remember the file name and line number, so that for the next error the info +/// is only displayed if it changed. void msg_source(int attr) { no_wait_return++; @@ -592,12 +584,10 @@ void msg_source(int attr) --no_wait_return; } -/* - * Return TRUE if not giving error messages right now: - * If "emsg_off" is set: no error messages at the moment. - * If "msg" is in 'debug': do error message but without side effects. - * If "emsg_skip" is set: never do error messages. - */ +/// @return TRUE if not giving error messages right now: +/// If "emsg_off" is set: no error messages at the moment. +/// If "msg" is in 'debug': do error message but without side effects. +/// If "emsg_skip" is set: never do error messages. int emsg_not_now(void) { if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL @@ -840,10 +830,11 @@ void msg_schedule_semsg(const char *const fmt, ...) multiqueue_put(main_loop.events, msg_semsg_event, 1, s); } -// Like msg(), but truncate to a single line if p_shm contains 't', or when -// "force" is true. This truncates in another way as for normal messages. -// Careful: The string may be changed by msg_may_trunc()! -// Returns a pointer to the printed message, if wait_return() not called. +/// Like msg(), but truncate to a single line if p_shm contains 't', or when +/// "force" is true. This truncates in another way as for normal messages. +/// Careful: The string may be changed by msg_may_trunc()! +/// +/// @return a pointer to the printed message, if wait_return() not called. char *msg_trunc_attr(char *s, bool force, int attr) { int n; @@ -863,11 +854,11 @@ char *msg_trunc_attr(char *s, bool force, int attr) return NULL; } -/* - * Check if message "s" should be truncated at the start (for filenames). - * Return a pointer to where the truncated message starts. - * Note: May change the message by replacing a character with '<'. - */ +/// Check if message "s" should be truncated at the start (for filenames). +/// +/// @return a pointer to where the truncated message starts. +/// +/// @note: May change the message by replacing a character with '<'. char_u *msg_may_trunc(bool force, char_u *s) { int room; @@ -967,10 +958,9 @@ static void add_msg_hist(const char *s, int len, int attr, bool multiline) msg_hist_len++; } -/* - * Delete the first (oldest) message from the history. - * Returns FAIL if there are no messages. - */ +/// Delete the first (oldest) message from the history. +/// +/// @return FAIL if there are no messages. int delete_first_msg(void) { struct msg_hist *p; @@ -1056,10 +1046,8 @@ void ex_messages(void *const eap_p) } } -/* - * Call this after prompting the user. This will avoid a hit-return message - * and a delay. - */ +/// Call this after prompting the user. This will avoid a hit-return message +/// and a delay. void msg_end_prompt(void) { msg_ext_clear_later(); @@ -1073,9 +1061,9 @@ void msg_end_prompt(void) /// Wait for the user to hit a key (normally Enter) /// -/// If 'redraw' is true, redraw the entire screen NOT_VALID -/// If 'redraw' is false, do a normal redraw -/// If 'redraw' is -1, don't redraw at all +/// @param redraw if true, redraw the entire screen NOT_VALID +/// if false, do a normal redraw +/// if -1, don't redraw at all void wait_return(int redraw) { int c; @@ -1265,9 +1253,7 @@ void wait_return(int redraw) } } -/* - * Write the hit-return prompt. - */ +/// Write the hit-return prompt. static void hit_return_msg(void) { int save_p_more = p_more; @@ -1288,9 +1274,7 @@ static void hit_return_msg(void) p_more = save_p_more; } -/* - * Set "keep_msg" to "s". Free the old value and check for NULL pointer. - */ +/// Set "keep_msg" to "s". Free the old value and check for NULL pointer. void set_keep_msg(char *s, int attr) { xfree(keep_msg); @@ -1357,9 +1341,7 @@ void msg_ext_set_kind(const char *msg_kind) msg_ext_kind = msg_kind; } -/* - * Prepare for outputting characters in the command line. - */ +/// Prepare for outputting characters in the command line. void msg_start(void) { int did_return = false; @@ -1406,9 +1388,7 @@ void msg_start(void) } } -/* - * Note that the current msg position is where messages start. - */ +/// Note that the current msg position is where messages start. void msg_starthere(void) { lines_left = cmdline_row; @@ -1462,12 +1442,11 @@ static void msg_home_replace_attr(char_u *fname, int attr) xfree(name); } -/* - * Output 'len' characters in 'str' (including NULs) with translation - * if 'len' is -1, output up to a NUL character. - * Use attributes 'attr'. - * Return the number of characters it takes on the screen. - */ +/// Output 'len' characters in 'str' (including NULs) with translation +/// if 'len' is -1, output up to a NUL character. +/// Use attributes 'attr'. +/// +/// @return the number of characters it takes on the screen. int msg_outtrans(char_u *str) { return msg_outtrans_attr(str, 0); @@ -1483,10 +1462,10 @@ int msg_outtrans_len(const char_u *str, int len) return msg_outtrans_len_attr(str, len, 0); } -/* - * Output one character at "p". Return pointer to the next character. - * Handles multi-byte characters. - */ +/// Output one character at "p". +/// Handles multi-byte characters. +/// +/// @return pointer to the next character. char_u *msg_outtrans_one(char_u *p, int attr) { int l; @@ -1752,9 +1731,7 @@ void str2specialbuf(const char *sp, char *buf, size_t len) *buf = NUL; } -/* - * print line for :print or :list command - */ +/// print line for :print or :list command void msg_prt_line(char_u *s, int list) { int c; @@ -1903,8 +1880,9 @@ void msg_prt_line(char_u *s, int list) msg_clr_eos(); } -// Use grid_puts() to output one multi-byte character. -// Return the pointer "s" advanced to the next character. +/// Use grid_puts() to output one multi-byte character. +/// +/// @return the pointer "s" advanced to the next character. static char_u *screen_puts_mbyte(char_u *s, int l, int attr) { int cw; @@ -1936,10 +1914,8 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr) return s + l; } -/* - * Output a string to the screen at position msg_row, msg_col. - * Update msg_row and msg_col for the next message. - */ +/// Output a string to the screen at position msg_row, msg_col. +/// Update msg_row and msg_col for the next message. void msg_puts(const char *s) { msg_puts_attr(s, 0); @@ -1950,11 +1926,9 @@ void msg_puts_title(const char *s) msg_puts_attr(s, HL_ATTR(HLF_T)); } -/* - * Show a message in such a way that it always fits in the line. Cut out a - * part in the middle and replace it with "..." when necessary. - * Does not handle multi-byte characters! - */ +/// Show a message in such a way that it always fits in the line. Cut out a +/// part in the middle and replace it with "..." when necessary. +/// Does not handle multi-byte characters! void msg_outtrans_long_attr(char_u *longstr, int attr) { msg_outtrans_long_len_attr(longstr, (int)STRLEN(longstr), attr); @@ -1974,9 +1948,7 @@ void msg_outtrans_long_len_attr(char_u *longstr, int len, int attr) msg_outtrans_len_attr(longstr + len - slen, slen, attr); } -/* - * Basic function for writing a message with highlight attributes. - */ +/// Basic function for writing a message with highlight attributes. void msg_puts_attr(const char *const s, const int attr) { msg_puts_attr_len(s, -1, attr); @@ -2078,10 +2050,8 @@ static void msg_ext_emit_chunk(void) ADD(msg_ext_chunks, ARRAY_OBJ(chunk)); } -/* - * The display part of msg_puts_attr_len(). - * May be called recursively to display scroll-back text. - */ +/// The display part of msg_puts_attr_len(). +/// May be called recursively to display scroll-back text. static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurse) { const char_u *s = str; @@ -2286,8 +2256,8 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs msg_check(); } -/// Return true when ":filter pattern" was used and "msg" does not match -/// "pattern". +/// @return true when ":filter pattern" was used and "msg" does not match +/// "pattern". bool message_filtered(char_u *msg) { if (cmdmod.filter_regmatch.regprog == NULL) { @@ -2419,9 +2389,7 @@ void msg_reset_scroll(void) msg_scrolled_at_flush = 0; } -/* - * Increment "msg_scrolled". - */ +/// Increment "msg_scrolled". static void inc_msg_scrolled(void) { if (*get_vim_var_str(VV_SCROLLSTART) == NUL) { @@ -2500,9 +2468,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int *sb_col = 0; } -/* - * Finished showing messages, clear the scroll-back text on the next message. - */ +/// Finished showing messages, clear the scroll-back text on the next message. void may_clear_sb_text(void) { do_clear_sb_text = SB_CLEAR_ALL; @@ -2545,9 +2511,7 @@ void clear_sb_text(int all) } } -/* - * "g<" command. - */ +/// "g<" command. void show_sb_text(void) { msgchunk_T *mp; @@ -2563,9 +2527,7 @@ void show_sb_text(void) } } -/* - * Move to the start of screen line in already displayed text. - */ +/// Move to the start of screen line in already displayed text. static msgchunk_T *msg_sb_start(msgchunk_T *mps) { msgchunk_T *mp = mps; @@ -2576,9 +2538,7 @@ static msgchunk_T *msg_sb_start(msgchunk_T *mps) return mp; } -/* - * Mark the last message chunk as finishing the line. - */ +/// Mark the last message chunk as finishing the line. void msg_sb_eol(void) { if (last_msgchunk != NULL) { @@ -2586,10 +2546,9 @@ void msg_sb_eol(void) } } -/* - * Display a screen line from previously displayed text at row "row". - * Returns a pointer to the text for the next line (can be NULL). - */ +/// Display a screen line from previously displayed text at row "row". +/// +/// @return a pointer to the text for the next line (can be NULL). static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) { msgchunk_T *mp = smp; @@ -2612,9 +2571,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp) return mp->sb_next; } -/* - * Output any postponed text for msg_puts_attr_len(). - */ +/// Output any postponed text for msg_puts_attr_len(). static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) { attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); @@ -2635,9 +2592,9 @@ static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr) } } -// Returns TRUE when messages should be printed to stdout/stderr: -// - "batch mode" ("silent mode", -es/-Es) -// - no UI and not embedded +/// @return TRUE when messages should be printed to stdout/stderr: +/// - "batch mode" ("silent mode", -es/-Es) +/// - no UI and not embedded int msg_use_printf(void) { return !embedded_mode && !ui_active(); @@ -2698,13 +2655,12 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen) msg_didout = true; // assume that line is not empty } -/* - * Show the more-prompt and handle the user response. - * This takes care of scrolling back and displaying previously displayed text. - * When at hit-enter prompt "typed_char" is the already typed character, - * otherwise it's NUL. - * Returns TRUE when jumping ahead to "confirm_msg_tail". - */ +/// Show the more-prompt and handle the user response. +/// This takes care of scrolling back and displaying previously displayed text. +/// When at hit-enter prompt "typed_char" is the already typed character, +/// otherwise it's NUL. +/// +/// @return TRUE when jumping ahead to "confirm_msg_tail". static int do_more_prompt(int typed_char) { static bool entered = false; @@ -2962,7 +2918,7 @@ void mch_errmsg(char *str) } } -// Give a message. To be used when the UI is not initialized yet. +/// Give a message. To be used when the UI is not initialized yet. void mch_msg(char *str) { assert(str != NULL); @@ -2977,10 +2933,8 @@ void mch_msg(char *str) } #endif // WIN32 -/* - * Put a character on the screen at the current message position and advance - * to the next position. Only for printable ASCII! - */ +/// Put a character on the screen at the current message position and advance +/// to the next position. Only for printable ASCII! static void msg_screen_putchar(int c, int attr) { attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr); @@ -3013,10 +2967,8 @@ void msg_moremsg(int full) } } -/* - * Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or - * exmode_active. - */ +/// Repeat the message for the current mode: ASKMORE, EXTERNCMD, CONFIRM or +/// exmode_active. void repeat_message(void) { if (State == ASKMORE) { @@ -3041,10 +2993,8 @@ void repeat_message(void) } } -/* - * Clear from current message position to end of screen. - * Skip this when ":silent" was used, no need to clear for redirection. - */ +/// Clear from current message position to end of screen. +/// Skip this when ":silent" was used, no need to clear for redirection. void msg_clr_eos(void) { if (msg_silent == 0) { @@ -3052,11 +3002,9 @@ void msg_clr_eos(void) } } -/* - * Clear from current message position to end of screen. - * Note: msg_col is not updated, so we remember the end of the message - * for msg_check(). - */ +/// Clear from current message position to end of screen. +/// Note: msg_col is not updated, so we remember the end of the message +/// for msg_check(). void msg_clr_eos_force(void) { if (ui_has(kUIMessages)) { @@ -3083,9 +3031,7 @@ void msg_clr_eos_force(void) } } -/* - * Clear the command line. - */ +/// Clear the command line. void msg_clr_cmdline(void) { msg_row = cmdline_row; @@ -3093,11 +3039,10 @@ void msg_clr_cmdline(void) msg_clr_eos_force(); } -/* - * end putting a message on the screen - * call wait_return if the message does not fit in the available space - * return TRUE if wait_return not called. - */ +/// end putting a message on the screen +/// call wait_return if the message does not fit in the available space +/// +/// @return TRUE if wait_return not called. int msg_end(void) { /* @@ -3187,10 +3132,8 @@ bool msg_ext_is_visible(void) return ui_has(kUIMessages) && msg_ext_visible > 0; } -/* - * If the written message runs into the shown command or ruler, we have to - * wait for hit-return and redraw the window later. - */ +/// If the written message runs into the shown command or ruler, we have to +/// wait for hit-return and redraw the window later. void msg_check(void) { if (ui_has(kUIMessages)) { @@ -3202,10 +3145,9 @@ void msg_check(void) } } -/* - * May write a string to the redirection file. - * When "maxlen" is -1 write the whole string, otherwise up to "maxlen" bytes. - */ +/// May write a string to the redirection file. +/// +/// @param maxlen if -1, write the whole string, otherwise up to "maxlen" bytes. static void redir_write(const char *const str, const ptrdiff_t maxlen) { const char_u *s = (char_u *)str; @@ -3290,10 +3232,8 @@ int redirecting(void) || redir_reg || redir_vname || capture_ga != NULL; } -/* - * Before giving verbose message. - * Must always be called paired with verbose_leave()! - */ +/// Before giving verbose message. +/// Must always be called paired with verbose_leave()! void verbose_enter(void) { if (*p_vfile != NUL) { @@ -3301,10 +3241,8 @@ void verbose_enter(void) } } -/* - * After giving verbose message. - * Must always be called paired with verbose_enter()! - */ +/// After giving verbose message. +/// Must always be called paired with verbose_enter()! void verbose_leave(void) { if (*p_vfile != NUL) { @@ -3314,9 +3252,7 @@ void verbose_leave(void) } } -/* - * Like verbose_enter() and set msg_scroll when displaying the message. - */ +/// Like verbose_enter() and set msg_scroll when displaying the message. void verbose_enter_scroll(void) { if (*p_vfile != NUL) { @@ -3327,9 +3263,7 @@ void verbose_enter_scroll(void) } } -/* - * Like verbose_leave() and set cmdline_row when displaying the message. - */ +/// Like verbose_leave() and set cmdline_row when displaying the message. void verbose_leave_scroll(void) { if (*p_vfile != NUL) { @@ -3341,9 +3275,7 @@ void verbose_leave_scroll(void) } } -/* - * Called when 'verbosefile' is set: stop writing to the file. - */ +/// Called when 'verbosefile' is set: stop writing to the file. void verbose_stop(void) { if (verbose_fd != NULL) { @@ -3353,10 +3285,9 @@ void verbose_stop(void) verbose_did_open = FALSE; } -/* - * Open the file 'verbosefile'. - * Return FAIL or OK. - */ +/// Open the file 'verbosefile'. +/// +/// @return FAIL or OK. int verbose_open(void) { if (verbose_fd == NULL && !verbose_did_open) { @@ -3372,10 +3303,8 @@ int verbose_open(void) return OK; } -/* - * Give a warning message (for searching). - * Use 'w' highlighting and may repeat the message after redrawing - */ +/// Give a warning message (for searching). +/// Use 'w' highlighting and may repeat the message after redrawing void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) { // Don't do this for ":silent". @@ -3414,9 +3343,7 @@ void give_warning2(char_u *const message, char_u *const a1, bool hl) give_warning(IObuff, hl); } -/* - * Advance msg cursor to column "col". - */ +/// Advance msg cursor to column "col". void msg_advance(int col) { if (msg_silent != 0) { // nothing to advance to @@ -3626,15 +3553,13 @@ static char_u *console_dialog_alloc(const char_u *message, char_u *buttons, bool return xmalloc(lenhotkey); } -/* - * Format the dialog string, and display it at the bottom of - * the screen. Return a string of hotkey chars (if defined) for - * each 'button'. If a button has no hotkey defined, the first character of - * the button is used. - * The hotkeys can be multi-byte characters, but without combining chars. - * - * Returns an allocated string with hotkeys. - */ +/// Format the dialog string, and display it at the bottom of +/// the screen. Return a string of hotkey chars (if defined) for +/// each 'button'. If a button has no hotkey defined, the first character of +/// the button is used. +/// The hotkeys can be multi-byte characters, but without combining chars. +/// +/// @return an allocated string with hotkeys. static char_u *msg_show_console_dialog(char_u *message, char_u *buttons, int dfltbutton) FUNC_ATTR_NONNULL_RET { @@ -3727,9 +3652,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def *msgp = NUL; } -/* - * Display the ":confirm" message. Also called when screen resized. - */ +/// Display the ":confirm" message. Also called when screen resized. void display_confirm_msg(void) { // Avoid that 'q' at the more prompt truncates the message here. -- cgit From a41321a8a2745b975fd8fa658f65e3c64dbe3846 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(ex_cmds): convert function comments to doxygen format --- src/nvim/ex_cmds.c | 207 ++++++++++++++++++++-------------------------------- src/nvim/ex_cmds2.c | 53 +++++++------- 2 files changed, 109 insertions(+), 151 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index c7910e148d..1b2a9579e4 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -225,9 +225,7 @@ void do_ascii(const exarg_T *const eap) msg((char *)IObuff); } -/* - * ":left", ":center" and ":right": align text. - */ +/// ":left", ":center" and ":right": align text. void ex_align(exarg_T *eap) { pos_T save_curpos; @@ -324,9 +322,7 @@ void ex_align(exarg_T *eap) beginline(BL_WHITE | BL_FIX); } -/* - * Get the length of the current line, excluding trailing white space. - */ +/// @return the length of the current line, excluding trailing white space. static int linelen(int *has_tab) { char_u *line; @@ -452,7 +448,7 @@ static int sort_compare(const void *s1, const void *s2) return result; } -// ":sort". +/// ":sort". void ex_sort(exarg_T *eap) { regmatch_T regmatch; @@ -723,9 +719,7 @@ sortend: } } -/* - * ":retab". - */ +/// ":retab". void ex_retab(exarg_T *eap) { linenr_T lnum; @@ -907,11 +901,9 @@ void ex_retab(exarg_T *eap) u_clearline(); } -/* - * :move command - move lines line1-line2 to line dest - * - * return FAIL for failure, OK otherwise - */ +/// :move command - move lines line1-line2 to line dest +/// +/// @return FAIL for failure, OK otherwise int do_move(linenr_T line1, linenr_T line2, linenr_T dest) { char_u *str; @@ -1062,9 +1054,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) return OK; } -/* - * ":copy" - */ +/// ":copy" void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) { linenr_T count; @@ -1132,11 +1122,9 @@ void free_prev_shellcmd(void) #endif -/* - * Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" - * Bangs in the argument are replaced with the previously entered command. - * Remember the argument. - */ +/// Handle the ":!cmd" command. Also for ":r !cmd" and ":w !cmd" +/// Bangs in the argument are replaced with the previously entered command. +/// Remember the argument. void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out) FUNC_ATTR_NONNULL_ALL { @@ -1684,9 +1672,7 @@ void print_line_no_prefix(linenr_T lnum, int use_number, int list) msg_prt_line(ml_get(lnum), list); } -/* - * Print a text line. Also in silent mode ("ex -s"). - */ +/// Print a text line. Also in silent mode ("ex -s"). void print_line(linenr_T lnum, int use_number, int list) { int save_silent = silent_mode; @@ -1754,9 +1740,7 @@ int rename_buffer(char_u *new_fname) return OK; } -/* - * ":file[!] [fname]". - */ +/// ":file[!] [fname]". void ex_file(exarg_T *eap) { // ":0file" removes the file name. Check for illegal uses ":3file", @@ -1782,9 +1766,7 @@ void ex_file(exarg_T *eap) } } -/* - * ":update". - */ +/// ":update". void ex_update(exarg_T *eap) { if (curbufIsChanged()) { @@ -1792,9 +1774,7 @@ void ex_update(exarg_T *eap) } } -/* - * ":write" and ":saveas". - */ +/// ":write" and ":saveas". void ex_write(exarg_T *eap) { if (eap->cmdidx == CMD_saveas) { @@ -1810,14 +1790,12 @@ void ex_write(exarg_T *eap) } } -/* - * write current buffer to file 'eap->arg' - * if 'eap->append' is TRUE, append to the file - * - * if *eap->arg == NUL write to current file - * - * return FAIL for failure, OK otherwise - */ +/// write current buffer to file 'eap->arg' +/// if 'eap->append' is TRUE, append to the file +/// +/// if *eap->arg == NUL write to current file +/// +/// @return FAIL for failure, OK otherwise int do_write(exarg_T *eap) { int other; @@ -2070,9 +2048,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char_u *fname, char_u *ffname, int return OK; } -/* - * Handle ":wnext", ":wNext" and ":wprevious" commands. - */ +/// Handle ":wnext", ":wNext" and ":wprevious" commands. void ex_wnext(exarg_T *eap) { int i; @@ -2089,9 +2065,7 @@ void ex_wnext(exarg_T *eap) } } -/* - * ":wall", ":wqall" and ":xall": Write all changed files (and exit). - */ +/// ":wall", ":wqall" and ":xall": Write all changed files (and exit). void do_wqall(exarg_T *eap) { int error = 0; @@ -2149,8 +2123,9 @@ void do_wqall(exarg_T *eap) } } -// Check the 'write' option. -// Return true and give a message when it's not st. +/// Check the 'write' option. +/// +/// @return true and give a message when it's not st. bool not_writing(void) { if (p_write) { @@ -2160,11 +2135,9 @@ bool not_writing(void) return true; } -/* - * Check if a buffer is read-only (either 'readonly' option is set or file is - * read-only). Ask for overruling in a dialog. Return TRUE and give an error - * message when the buffer is readonly. - */ +/// Check if a buffer is read-only (either 'readonly' option is set or file is +/// read-only). Ask for overruling in a dialog. Return TRUE and give an error +/// message when the buffer is readonly. static int check_readonly(int *forceit, buf_T *buf) { // Handle a file being readonly when the 'readonly' option is set or when @@ -2205,15 +2178,16 @@ static int check_readonly(int *forceit, buf_T *buf) return FALSE; } -// Try to abandon the current file and edit a new or existing file. -// "fnum" is the number of the file, if zero use "ffname_arg"/"sfname_arg". -// "lnum" is the line number for the cursor in the new file (if non-zero). -// -// Return: -// GETFILE_ERROR for "normal" error, -// GETFILE_NOT_WRITTEN for "not written" error, -// GETFILE_SAME_FILE for success -// GETFILE_OPEN_OTHER for successfully opening another file. +/// Try to abandon the current file and edit a new or existing file. +/// +/// @param fnum the number of the file, if zero use "ffname_arg"/"sfname_arg". +/// @param lnum the line number for the cursor in the new file (if non-zero). +/// +/// @return: +/// GETFILE_ERROR for "normal" error, +/// GETFILE_NOT_WRITTEN for "not written" error, +/// GETFILE_SAME_FILE for success +/// GETFILE_OPEN_OTHER for successfully opening another file. int getfile(int fnum, char_u *ffname_arg, char_u *sfname_arg, int setpm, linenr_T lnum, int forceit) { char_u *ffname = ffname_arg; @@ -2934,9 +2908,7 @@ static void delbuf_msg(char_u *name) static int append_indent = 0; // autoindent for first line -/* - * ":insert" and ":append", also used by ":change" - */ +/// ":insert" and ":append", also used by ":change" void ex_append(exarg_T *eap) { char_u *theline; @@ -3078,9 +3050,7 @@ void ex_append(exarg_T *eap) ex_no_reprint = true; } -/* - * ":change" - */ +/// ":change" void ex_change(exarg_T *eap) { linenr_T lnum; @@ -3247,9 +3217,9 @@ void ex_z(exarg_T *eap) ex_no_reprint = true; } -// Check if the secure flag is set (.exrc or .vimrc in current directory). -// If so, give an error message and return true. -// Otherwise, return false. +/// @return true if the secure flag is set (.exrc or .vimrc in current directory) +/// and also give an error message. +/// Otherwise, return false. bool check_secure(void) { if (secure) { @@ -4533,22 +4503,20 @@ static void global_exe_one(char_u *const cmd, const linenr_T lnum) } } -/* - * Execute a global command of the form: - * - * g/pattern/X : execute X on all lines where pattern matches - * v/pattern/X : execute X on all lines where pattern does not match - * - * where 'X' is an EX command - * - * The command character (as well as the trailing slash) is optional, and - * is assumed to be 'p' if missing. - * - * This is implemented in two passes: first we scan the file for the pattern and - * set a mark for each line that (not) matches. Secondly we execute the command - * for each line that has a mark. This is required because after deleting - * lines we do not know where to search for the next match. - */ +/// Execute a global command of the form: +/// +/// g/pattern/X : execute X on all lines where pattern matches +/// v/pattern/X : execute X on all lines where pattern does not match +/// +/// where 'X' is an EX command +/// +/// The command character (as well as the trailing slash) is optional, and +/// is assumed to be 'p' if missing. +/// +/// This is implemented in two passes: first we scan the file for the pattern and +/// set a mark for each line that (not) matches. Secondly we execute the command +/// for each line that has a mark. This is required because after deleting +/// lines we do not know where to search for the next match. void ex_global(exarg_T *eap) { linenr_T lnum; // line number according to old situation @@ -4764,9 +4732,7 @@ bool prepare_tagpreview(bool undo_sync) } -/* - * ":help": open a read-only window on a help file - */ +/// ":help": open a read-only window on a help file void ex_help(exarg_T *eap) { char_u *arg; @@ -4949,11 +4915,10 @@ erret: } -/* - * In an argument search for a language specifiers in the form "@xx". - * Changes the "@" to NUL if found, and returns a pointer to "xx". - * Returns NULL if not found. - */ +/// In an argument search for a language specifiers in the form "@xx". +/// Changes the "@" to NUL if found, and returns a pointer to "xx". +/// +/// @return NULL if not found. char_u *check_help_lang(char_u *arg) { int len = (int)STRLEN(arg); @@ -5019,10 +4984,8 @@ int help_heuristic(char_u *matched_string, int offset, int wrong_case) return (int)(100 * num_letters + STRLEN(matched_string) + offset); } -/* - * Compare functions for qsort() below, that checks the help heuristics number - * that has been put after the tagname by find_tags(). - */ +/// Compare functions for qsort() below, that checks the help heuristics number +/// that has been put after the tagname by find_tags(). static int help_compare(const void *s1, const void *s2) { char *p1; @@ -5033,10 +4996,10 @@ static int help_compare(const void *s1, const void *s2) return strcmp(p1, p2); } -// Find all help tags matching "arg", sort them and return in matches[], with -// the number of matches in num_matches. -// The matches will be sorted with a "best" match algorithm. -// When "keep_lang" is true try keeping the language of the current buffer. +/// Find all help tags matching "arg", sort them and return in matches[], with +/// the number of matches in num_matches. +/// The matches will be sorted with a "best" match algorithm. +/// When "keep_lang" is true try keeping the language of the current buffer. int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, bool keep_lang) { int i; @@ -5308,10 +5271,8 @@ static void prepare_help_buffer(void) set_buflisted(FALSE); } -/* - * After reading a help file: May cleanup a help buffer when syntax - * highlighting is not used. - */ +/// After reading a help file: May cleanup a help buffer when syntax +/// highlighting is not used. void fix_help_buffer(void) { linenr_T lnum; @@ -5507,17 +5468,13 @@ void fix_help_buffer(void) } } -/* - * ":exusage" - */ +/// ":exusage" void ex_exusage(exarg_T *eap) { do_cmdline_cmd("help ex-cmd-index"); } -/* - * ":viusage" - */ +/// ":viusage" void ex_viusage(exarg_T *eap) { do_cmdline_cmd("help normal-index"); @@ -5826,9 +5783,7 @@ static void helptags_cb(char_u *fname, void *cookie) do_helptags(fname, *(bool *)cookie, true); } -/* - * ":helptags" - */ +/// ":helptags" void ex_helptags(exarg_T *eap) { expand_T xpc; @@ -5857,9 +5812,7 @@ void ex_helptags(exarg_T *eap) } } -/* - * ":helpclose": Close one help window - */ +/// ":helpclose": Close one help window void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { @@ -5873,7 +5826,7 @@ void ex_helpclose(exarg_T *eap) /// Tries to enter to an existing window of given buffer. If no existing buffer /// is found, creates a new split. /// -/// Returns OK/FAIL. +/// @return OK/FAIL. int sub_preview_win(buf_T *preview_buf) { if (preview_buf != NULL) { @@ -6115,9 +6068,11 @@ void ex_substitute(exarg_T *eap) /// Skip over the pattern argument of ":vimgrep /pat/[g][j]". /// Put the start of the pattern in "*s", unless "s" is NULL. -/// If "flags" is not NULL put the flags in it: VGR_GLOBAL, VGR_NOJUMP. -/// If "s" is not NULL terminate the pattern with a NUL. -/// Return a pointer to the char just past the pattern plus flags. +/// +/// @param flags if not NULL, put the flags in it: VGR_GLOBAL, VGR_NOJUMP. +/// @param s if not NULL, terminate the pattern with a NUL. +/// +/// @return a pointer to the char just past the pattern plus flags. char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) { int c; diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 5c040adc1c..fe1dec7298 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -436,8 +436,8 @@ static void script_dump_profile(FILE *fd) } } -/// Return true when a function defined in the current script should be -/// profiled. +/// @return true when a function defined in the current script should be +/// profiled. bool prof_def_func(void) { if (current_sctx.sc_sid > 0) { @@ -492,7 +492,7 @@ void autowrite_all(void) } } -/// Return true if buffer was changed and cannot be abandoned. +/// @return true if buffer was changed and cannot be abandoned. /// For flags use the CCGD_ values. bool check_changed(buf_T *buf, int flags) { @@ -616,7 +616,7 @@ bool dialog_close_terminal(buf_T *buf) return ret == VIM_YES; } -/// Return true if the buffer "buf" can be abandoned, either by making it +/// @return true if the buffer "buf" can be abandoned, either by making it /// hidden, autowriting it or unloading it. bool can_abandon(buf_T *buf, int forceit) { @@ -771,8 +771,8 @@ theend: return ret; } -/// Return FAIL if there is no file name, OK if there is one. -/// Give error message for FAIL. +/// @return FAIL if there is no file name, OK if there is one. +/// Give error message for FAIL. int check_fname(void) { if (curbuf->b_ffname == NULL) { @@ -784,7 +784,7 @@ int check_fname(void) /// Flush the contents of a buffer, unless it has no file name. /// -/// @return FAIL for failure, OK otherwise +/// @return FAIL for failure, OK otherwise int buf_write_all(buf_T *buf, int forceit) { int retval; @@ -808,7 +808,8 @@ int buf_write_all(buf_T *buf, int forceit) /// Isolate one argument, taking backticks. /// Changes the argument in-place, puts a NUL after it. Backticks remain. -/// Return a pointer to the start of the next argument. +/// +/// @return a pointer to the start of the next argument. static char_u *do_one_arg(char_u *str) { char_u *p; @@ -859,7 +860,7 @@ static void get_arglist(garray_T *gap, char_u *str, int escaped) /// Parse a list of arguments (file names), expand them and return in /// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'. /// -/// @return FAIL or OK. +/// @return FAIL or OK. int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) { garray_T ga; @@ -889,7 +890,7 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) /// 0 means before first one /// @param will_edit will edit added argument /// -/// @return FAIL for failure, OK otherwise. +/// @return FAIL for failure, OK otherwise. static int do_arglist(char_u *str, int what, int after, bool will_edit) FUNC_ATTR_NONNULL_ALL { @@ -988,8 +989,8 @@ static void alist_check_arg_idx(void) } } -/// Return true if window "win" is editing the file at the current argument -/// index. +/// @return true if window "win" is editing the file at the current argument +/// index. static bool editing_arg_idx(win_T *win) { return !(win->w_arg_idx >= WARGCOUNT(win) @@ -1717,13 +1718,13 @@ linenr_T *source_breakpoint(void *cookie) return &((struct source_cookie *)cookie)->breakpoint; } -/// Return the address holding the debug tick for a source cookie. +/// @return the address holding the debug tick for a source cookie. int *source_dbg_tick(void *cookie) { return &((struct source_cookie *)cookie)->dbg_tick; } -/// Return the nesting level for a source cookie. +/// @return the nesting level for a source cookie. int source_level(void *cookie) { return ((struct source_cookie *)cookie)->level; @@ -1788,7 +1789,8 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat) /// /// @param name File name of the script. NULL for anonymous :source. /// @param[out] sid_out SID of the new item. -/// @return pointer to the created script item. +/// +/// @return pointer to the created script item. scriptitem_T *new_script_item(char_u *const name, scid_T *const sid_out) { static scid_T last_current_SID = 0; @@ -1896,7 +1898,7 @@ int do_source_str(const char *cmd, const char *traceback_name) /// @param check_other check for .vimrc and _vimrc /// @param is_vimrc DOSO_ value /// -/// @return FAIL if file could not be opened, OK otherwise +/// @return FAIL if file could not be opened, OK otherwise int do_source(char *fname, int check_other, int is_vimrc) { struct source_cookie cookie; @@ -2145,6 +2147,7 @@ theend: /// Check if fname was sourced before to finds its SID. /// If it's new, generate a new SID. +/// /// @param[in] fname file path of script /// @param[out] ret_sctx sctx of this script scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) @@ -2614,9 +2617,9 @@ void do_finish(exarg_T *eap, int reanimate) } -/// Return true when a sourced file had the ":finish" command: Don't give error -/// message for missing ":endif". -/// Return false when not sourcing a file. +/// @return true when a sourced file had the ":finish" command: Don't give error +/// message for missing ":endif". +/// false when not sourcing a file. bool source_finished(LineGetter fgetline, void *cookie) { return getline_equal(fgetline, cookie, getsourceline) @@ -2653,8 +2656,8 @@ static char *get_locale_val(int what) } #endif -// Return true when "lang" starts with a valid language name. -// Rejects NULL, empty string, "C", "C.UTF-8" and others. +/// @return true when "lang" starts with a valid language name. +/// Rejects NULL, empty string, "C", "C.UTF-8" and others. static bool is_valid_mess_lang(char *lang) { return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); @@ -2757,11 +2760,10 @@ void set_lang_var(void) } #ifdef HAVE_WORKING_LIBINTL -/// + /// ":language": Set the language (locale). /// /// @param eap -/// void ex_language(exarg_T *eap) { char *loc; @@ -2870,8 +2872,9 @@ static char_u **locales = NULL; // Array of all available locales # ifndef WIN32 static bool did_init_locales = false; -/// Return an array of strings for all available locales + NULL for the -/// last element. Return NULL in case of error. +/// @return an array of strings for all available locales + NULL for the +/// last element or, +/// NULL in case of error. static char_u **find_locales(void) { garray_T locales_ga; -- cgit From d238b8f6003d34cae7f65ff7585b48a2cd9449fb Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 17 Mar 2022 06:21:24 +0100 Subject: chore: fix typos (#17670) Co-authored-by: zeertzjq --- src/nvim/api/vim.c | 2 +- src/nvim/api/window.c | 2 +- src/nvim/autocmd.c | 2 +- src/nvim/change.c | 2 +- src/nvim/edit.c | 8 ++++---- src/nvim/eval/funcs.c | 6 +++--- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 2 +- src/nvim/os/pty_process_unix.c | 2 +- src/nvim/os/pty_process_win.c | 2 +- src/nvim/search.c | 2 +- src/nvim/terminal.c | 2 +- src/nvim/tui/input.c | 1 + src/nvim/ui_client.c | 2 ++ 14 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3292ee2ef8..0bdccf7a0b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -127,7 +127,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) /// /// Note: Unlike the `:highlight` command which can update a highlight group, /// this function completely replaces the definition. For example: -/// `nvim_set_hl(0, 'Visual', {})` will clear the highlight group 'Visual'. +/// ``nvim_set_hl(0, 'Visual', {})`` will clear the highlight group 'Visual'. /// /// @param ns_id Namespace id for this highlight |nvim_create_namespace()|. /// Use 0 to set a highlight group globally |:highlight|. diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 9c473ff724..be43708604 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -71,7 +71,7 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) } /// Sets the (1,0)-indexed cursor position in the window. |api-indexing| -/// Unlike |win_execute()| this scrolls the window. +/// This scrolls the window even if it is not the current one. /// /// @param window Window handle, or 0 for current window /// @param pos (row, col) tuple representing the new position diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 8e4b043169..df336d8703 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -397,7 +397,7 @@ int augroup_add(char *name) } /// Delete the augroup that matches name. -/// @param stupid_legacy_mode bool: This paremeter determines whether to run the augroup +/// @param stupid_legacy_mode bool: This parameter determines whether to run the augroup /// deletion in the same fashion as `:augroup! {name}` where if there are any remaining /// autocmds left in the augroup, it will change the name of the augroup to `--- DELETED ---` /// but leave the autocmds existing. These are _separate_ augroups, so if you do this for diff --git a/src/nvim/change.c b/src/nvim/change.c index 607414ac3c..6c3dbf72e4 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -408,7 +408,7 @@ void deleted_lines(linenr_T lnum, long count) /// be triggered to display the cursor. void deleted_lines_mark(linenr_T lnum, long count) { - // if we deleted the entire buffer, we need to implicity add a new empty line + // if we deleted the entire buffer, we need to implicitly add a new empty line bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY; mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM, diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 095e082f61..2e36962a4d 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -5116,10 +5116,10 @@ static int ins_complete(int c, bool enable_pum) || ctrl_x_mode == CTRL_X_PATH_PATTERNS || ctrl_x_mode == CTRL_X_PATH_DEFINES) { if (compl_startpos.lnum != curwin->w_cursor.lnum) { - /* line (probably) wrapped, set compl_startpos to the - * first non_blank in the line, if it is not a wordchar - * include it to get a better pattern, but then we don't - * want the "\\<" prefix, check it bellow */ + // line (probably) wrapped, set compl_startpos to the + // first non_blank in the line, if it is not a wordchar + // include it to get a better pattern, but then we don't + // want the "\\<" prefix, check it below. compl_col = (colnr_T)getwhitecols(line); compl_startpos.col = compl_col; compl_startpos.lnum = curwin->w_cursor.lnum; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 738ed7f85e..5b0d6713e2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1500,7 +1500,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Set the cursor position. -/// If 'charcol' is true, then use the column number as a character offet. +/// If 'charcol' is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) { @@ -7781,7 +7781,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "reduce(list, { accumlator, element -> value } [, initial])" function +/// "reduce(list, { accumulator, element -> value } [, initial])" function static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) { @@ -8885,7 +8885,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Set the cursor or mark position. -/// If 'charpos' is TRUE, then use the column number as a character offet. +/// If 'charpos' is TRUE, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_position(typval_T *argvars, typval_T *rettv, bool charpos) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 09cf6601ee..14d0c58cd1 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9655,7 +9655,7 @@ static void ex_filetype(exarg_T *eap) /// Source ftplugin.vim and indent.vim to create the necessary FileType /// autocommands. We do this separately from filetype.vim so that these -/// autocommands will always fire first (and thus can be overriden) while still +/// autocommands will always fire first (and thus can be overridden) while still /// allowing general filetype detection to be disabled in the user's init file. void filetype_plugin_enable(void) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f52f3afe7d..aff356b6a5 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -231,7 +231,7 @@ static int compl_selected; /// |:checkhealth| completion items /// -/// Regenerates on every new command line prompt, to accomodate changes on the +/// Regenerates on every new command line prompt, to accommodate changes on the /// runtime files. typedef struct { garray_T names; // healthcheck names diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 58cb1b8f84..4a49c0b162 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -86,7 +86,7 @@ static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, ioctl(slave, I_PUSH, "ptem"); // ldterm provides most of the termio terminal interface ioctl(slave, I_PUSH, "ldterm"); - // ttcompat compatability with older terminal ioctls + // ttcompat compatibility with older terminal ioctls ioctl(slave, I_PUSH, "ttcompat"); if (termp) { diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index ed9f4636e3..4fb9e30a96 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -307,7 +307,7 @@ static void pty_process_finish2(PtyProcess *ptyproc) /// Build the command line to pass to CreateProcessW. /// /// @param[in] argv Array with string arguments. -/// @param[out] cmd_line Location where saved builded cmd line. +/// @param[out] cmd_line Location where saved built cmd line. /// /// @returns zero on success, or error code of MultiByteToWideChar function. /// diff --git a/src/nvim/search.c b/src/nvim/search.c index cc7c2ecf06..a69bd641f8 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -5689,7 +5689,7 @@ search_line: // we read a line, set "already" to check this "line" later // if depth >= 0 we'll increase files[depth].lnum far - // bellow -- Acevedo + // below -- Acevedo already = aux = p = skipwhite(line); p = find_word_start(p); p = find_word_end(p); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index a76a806b80..5189705a36 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1288,7 +1288,7 @@ static bool send_mouse_event(Terminal *term, int c) return mouse_win == curwin; } - // ignore left release action if it was not proccessed above + // ignore left release action if it was not processed above // to prevent leaving Terminal mode after entering to it using a mouse if (c == K_LEFTRELEASE && mouse_win->w_buffer->terminal == term) { return false; diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index b262fc6c54..26b3dd6e92 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -115,6 +115,7 @@ static void tinput_wait_enqueue(void **argv) { TermInput *input = argv[0]; if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) { + // End streamed paste with an empty string. const String keys = { .data = "", .size = 0 }; String copy = copy_string(keys); multiqueue_put(main_loop.events, tinput_paste_event, 3, diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 4fad3e0709..f11ed45e86 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -52,6 +52,8 @@ void ui_client_init(uint64_t chan) /// Handler for "redraw" events sent by the NVIM server /// +/// This is just a stub. The mentioned functionality will be implemented. +/// /// This function will be called by handle_request (in msgpack_rpc/channel.c) /// The individual ui_events sent by the server are individually handled /// by their respective handlers defined in ui_events_client.generated.h -- cgit From 663616033834c5da3b8f48b0bd0db783fc92db31 Mon Sep 17 00:00:00 2001 From: hlpr98 Date: Wed, 16 Mar 2022 21:01:18 +0100 Subject: feat(ui_client): pass user input to remote server --- src/nvim/tui/input.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 26b3dd6e92..faae921a68 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -19,6 +19,7 @@ # include "nvim/os/os_win_console.h" #endif #include "nvim/event/rstream.h" +#include "nvim/msgpack_rpc/channel.h" #define KEY_BUFFER_SIZE 0xfff @@ -134,7 +135,17 @@ static void tinput_wait_enqueue(void **argv) rbuffer_consumed(input->key_buffer, len); rbuffer_reset(input->key_buffer); } else { - const size_t consumed = input_enqueue(keys); + size_t consumed; + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + ADD(args, STRING_OBJ(copy_string(keys))); + // TODO(bfredl): could be non-blocking now with paste? + Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &err); + consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0; + } else { + consumed = input_enqueue(keys); + } if (consumed) { rbuffer_consumed(input->key_buffer, consumed); } -- cgit From c6640d0d700f977913606277418be546404d5fd7 Mon Sep 17 00:00:00 2001 From: hlpr98 Date: Wed, 16 Mar 2022 21:49:02 +0100 Subject: feat(ui_client): handle resize events --- src/nvim/ui.c | 17 ++++++++++++++++- src/nvim/ui_client.c | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 7c67c058b0..da50f068b7 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -12,6 +12,7 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/cursor_shape.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/diff.h" #include "nvim/event/loop.h" #include "nvim/ex_cmds2.h" @@ -224,7 +225,21 @@ void ui_refresh(void) int save_p_lz = p_lz; p_lz = false; // convince redrawing() to return true ... - screen_resize(width, height); + if (!ui_client_channel_id) { + screen_resize(width, height); + } else { + Array args = ARRAY_DICT_INIT; + Error err = ERROR_INIT; + ADD(args, INTEGER_OBJ((int)width)); + ADD(args, INTEGER_OBJ((int)height)); + rpc_send_call(ui_client_channel_id, "nvim_ui_try_resize", args, &err); + + if (ERROR_SET(&err)) { + ELOG("ui_client resize: %s", err.msg); + } + api_clear_error(&err); + } + p_lz = save_p_lz; if (ext_widgets[kUIMessages]) { diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index f11ed45e86..aa33f2fc73 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -92,6 +92,7 @@ void ui_client_execute(uint64_t chan) { while (true) { loop_poll_events(&main_loop, -1); + multiqueue_process_events(resize_events); } getout(0); -- cgit From 5ab122917474b3f9e88be4ee88bc6d627980cfe0 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 30 Jan 2022 11:57:41 +0600 Subject: feat: add support for global statusline Ref: #9342 Adds the option to have a single global statusline for the current window at the bottom of the screen instead of a statusline at the bottom of every window. Enabled by setting `laststatus = 3`. Due to the fact that statuslines at the bottom of windows are removed when global statusline is enabled, horizontal separators are used instead to separate horizontal splits. The horizontal separator character is configurable through the`horiz` item in `'fillchars'`. Separator connector characters are also used to connect the horizontal and vertical separators together, which are also configurable through the `horizup`, `horizdown`, `vertleft`, `vertright` and `verthoriz` items in `fillchars`. The window separators are highlighted using the `WinSeparator` highlight group, which supersedes `VertSplit` and is linked to `VertSplit` by default in order to maintain backwards compatibility. --- src/nvim/api/vim.c | 2 +- src/nvim/api/win_config.c | 2 +- src/nvim/buffer.c | 4 +- src/nvim/buffer_defs.h | 9 +- src/nvim/eval/funcs.c | 2 +- src/nvim/ex_getln.c | 4 +- src/nvim/ex_session.c | 2 +- src/nvim/highlight_defs.h | 6 +- src/nvim/option.c | 64 ++++--- src/nvim/quickfix.c | 2 +- src/nvim/screen.c | 301 ++++++++++++++++++++++++++------ src/nvim/screen.h | 8 + src/nvim/syntax.c | 3 +- src/nvim/testdir/test_highlight.vim | 4 +- src/nvim/window.c | 334 +++++++++++++++++++++++++----------- 15 files changed, 555 insertions(+), 192 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f4909b0801..2e3d99cdad 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2319,7 +2319,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * maxwidth = (int)opts->maxwidth.data.integer; } else { - maxwidth = use_tabline ? Columns : wp->w_width; + maxwidth = (use_tabline || global_stl_height() > 0) ? Columns : wp->w_width; } char buf[MAXPATHL]; diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index ceb7f71423..d8ccd67bcd 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -131,7 +131,7 @@ /// An empty string can be used to turn off a specific border, for instance, /// [ "", "", "", ">", "", "", "", "<" ] /// will only make vertical borders but not horizontal ones. -/// By default, `FloatBorder` highlight is used, which links to `VertSplit` +/// By default, `FloatBorder` highlight is used, which links to `WinSeparator` /// when not defined. It could also be specified by character: /// [ {"+", "MyCorner"}, {"x", "MyBorder"} ]. /// - noautocmd: If true then no buffer-related autocommand events such as diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 38b045b31c..493c011ad6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5006,8 +5006,8 @@ void ex_buffer_all(exarg_T *eap) wpnext = wp->w_next; if ((wp->w_buffer->b_nwindows > 1 || ((cmdmod.split & WSP_VERT) - ? wp->w_height + wp->w_status_height < Rows - p_ch - - tabline_height() + ? wp->w_height + wp->w_hsep_height + wp->w_status_height < Rows - p_ch + - tabline_height() - global_stl_height() : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin)) && !ONE_WINDOW diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 7ae5df164f..d0f49ec346 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1227,7 +1227,13 @@ struct window_S { struct { int stl; int stlnc; + int horiz; + int horizup; + int horizdown; int vert; + int vertleft; + int vertright; + int verthoriz; int fold; int foldopen; ///< when fold is open int foldclosed; ///< when fold is closed @@ -1273,7 +1279,8 @@ struct window_S { int w_status_height; // number of status lines (0 or 1) int w_wincol; // Leftmost column of window in screen. int w_width; // Width of window, excluding separation. - int w_vsep_width; // Number of separator columns (0 or 1). + int w_hsep_height; // Number of horizontal separator rows (0 or 1) + int w_vsep_width; // Number of vertical separator columns (0 or 1). pos_save_T w_save_cursor; // backup of cursor pos and topline // inner size of window, which can be overridden by external UI diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index c6baa105b0..9a74e4e351 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -3886,7 +3886,7 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) wp = mouse_find_win(&grid, &row, &col); if (wp != NULL) { - int height = wp->w_height + wp->w_status_height; + int height = wp->w_height + wp->w_hsep_height + wp->w_status_height; // The height is adjusted by 1 when there is a bottom border. This is not // necessary for a top border since `row` starts at -1 in that case. if (row < height + wp->w_border_adj[2]) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ed4475eb1a..fccb57c0d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -632,7 +632,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat validate_cursor(); // May redraw the status line to show the cursor position. - if (p_ru && curwin->w_status_height > 0) { + if (p_ru && (curwin->w_status_height > 0 || global_stl_height() > 0)) { curwin->w_redr_status = true; } @@ -3631,7 +3631,7 @@ void compute_cmdrow(void) } else { win_T *wp = lastwin_nofloating(); cmdline_row = wp->w_winrow + wp->w_height - + wp->w_status_height; + + wp->w_hsep_height + wp->w_status_height + global_stl_height(); } lines_left = cmdline_row; } diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index ca07174543..3b985bc329 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -72,7 +72,7 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) n++; // restore height when not full height - if (wp->w_height + wp->w_status_height < topframe->fr_height + if (wp->w_height + wp->w_hsep_height + wp->w_status_height < topframe->fr_height && (fprintf(fd, "exe '%dresize ' . ((&lines * %" PRId64 " + %" PRId64 ") / %" PRId64 ")\n", diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 50a03e0c02..9f6f800fb4 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -70,7 +70,8 @@ typedef enum { HLF_R, // return to continue message and yes/no questions HLF_S, // status lines HLF_SNC, // status lines of not-current windows - HLF_C, // column to separate vertically split windows + HLF_C, // window split separators + HLF_VSP, // VertSplit HLF_T, // Titles for output from ":set all", ":autocmd" etc. HLF_V, // Visual mode HLF_VNC, // Visual mode, autoselecting and not clipboard owner @@ -129,10 +130,11 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_R] = "Question", [HLF_S] = "StatusLine", [HLF_SNC] = "StatusLineNC", - [HLF_C] = "VertSplit", + [HLF_C] = "WinSeparator", [HLF_T] = "Title", [HLF_V] = "Visual", [HLF_VNC] = "VisualNC", + [HLF_VSP] = "VertSplit", [HLF_W] = "WarningMsg", [HLF_WM] = "WildMenu", [HLF_FL] = "Folded", diff --git a/src/nvim/option.c b/src/nvim/option.c index c8e50d4494..3eb854aded 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2910,7 +2910,7 @@ ambw_end: || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) { errmsg = e_invarg; } else { - if (curwin->w_status_height) { + if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; redraw_later(curwin, VALID); } @@ -3553,16 +3553,22 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab *tab; struct chars_tab fcs_tab[] = { - { &wp->w_p_fcs_chars.stl, "stl", ' ' }, - { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, - { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ - { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · - { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, - { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, - { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ - { &wp->w_p_fcs_chars.diff, "diff", '-' }, - { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, - { &wp->w_p_fcs_chars.eob, "eob", '~' }, + { &wp->w_p_fcs_chars.stl, "stl", ' ' }, + { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' }, + { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─ + { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // â”´ + { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬ + { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │ + { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤ + { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├ + { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼ + { &wp->w_p_fcs_chars.fold, "fold", 183 }, // · + { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' }, + { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' }, + { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │ + { &wp->w_p_fcs_chars.diff, "diff", '-' }, + { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' }, + { &wp->w_p_fcs_chars.eob, "eob", '~' }, }; struct chars_tab lcs_tab[] = { { &wp->w_p_lcs_chars.eol, "eol", NUL }, @@ -3589,15 +3595,17 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) varp = &p_fcs; } if (*p_ambw == 'd') { - // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is - // forbidden (TUI limitation?). Set old defaults. - fcs_tab[2].def = '|'; - fcs_tab[6].def = '|'; - fcs_tab[3].def = '-'; - } else { - fcs_tab[2].def = 9474; // │ - fcs_tab[6].def = 9474; // │ - fcs_tab[3].def = 183; // · + // XXX: If ambiwidth=double then some characters take 2 columns, + // which is forbidden (TUI limitation?). Set old defaults. + fcs_tab[2].def = '-'; + fcs_tab[3].def = '-'; + fcs_tab[4].def = '-'; + fcs_tab[5].def = '|'; + fcs_tab[6].def = '|'; + fcs_tab[7].def = '|'; + fcs_tab[8].def = '+'; + fcs_tab[9].def = '-'; + fcs_tab[12].def = '|'; } } @@ -4474,6 +4482,20 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // 'winminwidth' win_setminwidth(); } else if (pp == &p_ls) { + // When switching to global statusline, decrease topframe height + // Also clear the cmdline to remove the ruler if there is one + if (value == 3 && old_value != 3) { + frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + clear_cmdline = true; + } + // When switching from global statusline, increase height of topframe by STATUS_HEIGHT + // in order to to re-add the space that was previously taken by the global statusline + if (old_value == 3 && value != 3) { + frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false); + (void)win_comp_pos(); + } + last_status(false); // (re)set last window status line. } else if (pp == &p_stal) { // (re)set tab page line @@ -5645,7 +5667,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value) void comp_col(void) { - int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW)); + int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW)); sc_col = 0; ru_col = 0; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7e29aed51b..124500d0fc 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3597,7 +3597,7 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp win_setwidth(sz); } } else if (sz != win->w_height - && (win->w_height + win->w_status_height + tabline_height() + && (win->w_height + win->w_hsep_height + win->w_status_height + tabline_height() < cmdline_row)) { win_setheight(sz); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 15fb6901cc..c87763c7a4 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -317,7 +317,8 @@ void update_curbuf(int type) void redraw_buf_status_later(buf_T *buf) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf && wp->w_status_height) { + if (wp->w_buffer == buf + && (wp->w_status_height || (wp == curwin && global_stl_height()))) { wp->w_redr_status = true; if (must_redraw < VALID) { must_redraw = VALID; @@ -335,6 +336,7 @@ void redraw_buf_status_later(buf_T *buf) int update_screen(int type) { static bool did_intro = false; + bool is_stl_global = global_stl_height() > 0; // Don't do anything if the screen structures are (not yet) valid. // A VimResized autocmd can invoke redrawing in the middle of a resize, @@ -417,10 +419,13 @@ int update_screen(int type) if (W_ENDROW(wp) > valid) { wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID); } - if (W_ENDROW(wp) + wp->w_status_height > valid) { + if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) { wp->w_redr_status = true; } } + if (is_stl_global && Rows - p_ch - 1 > valid) { + curwin->w_redr_status = true; + } } msg_grid_set_pos(Rows-p_ch, false); msg_grid_invalid = false; @@ -442,13 +447,15 @@ int update_screen(int type) wp->w_redr_type = REDRAW_TOP; } else { wp->w_redr_type = NOT_VALID; - if (W_ENDROW(wp) + wp->w_status_height - <= msg_scrolled) { - wp->w_redr_status = TRUE; + if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height <= msg_scrolled) { + wp->w_redr_status = true; } } } } + if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) { + curwin->w_redr_status = true; + } redraw_cmdline = true; redraw_tabline = true; } @@ -803,8 +810,11 @@ static void win_update(win_T *wp, Providers *providers) wp->w_lines_valid = 0; } - // Window is zero-height: nothing to draw. + // Window is zero-height: Only need to draw the separator if (wp->w_grid.Rows == 0) { + // draw the horizontal separator below this window + draw_hsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } @@ -812,7 +822,8 @@ static void win_update(win_T *wp, Providers *providers) // Window is zero-width: Only need to draw the separator. if (wp->w_grid.Columns == 0) { // draw the vertical separator right of this window - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_sep_connectors_win(wp); wp->w_redr_type = 0; return; } @@ -1747,7 +1758,9 @@ static void win_update(win_T *wp, Providers *providers) kvi_destroy(line_providers); if (wp->w_redr_type >= REDRAW_TOP) { - draw_vsep_win(wp, 0); + draw_vsep_win(wp); + draw_hsep_win(wp); + draw_sep_connectors_win(wp); } syn_set_timeout(NULL); @@ -4958,10 +4971,15 @@ void rl_mirror(char_u *str) */ void status_redraw_all(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height) { - wp->w_redr_status = true; - redraw_later(wp, VALID); + if (global_stl_height()) { + curwin->w_redr_status = true; + redraw_later(curwin, VALID); + } else { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_status_height) { + wp->w_redr_status = true; + redraw_later(wp, VALID); + } } } } @@ -4975,10 +4993,15 @@ void status_redraw_curbuf(void) /// Marks all status lines of the specified buffer for redraw. void status_redraw_buf(buf_T *buf) { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == buf) { - wp->w_redr_status = true; - redraw_later(wp, VALID); + if (global_stl_height() != 0 && curwin->w_buffer == buf) { + curwin->w_redr_status = true; + redraw_later(curwin, VALID); + } else { + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_status_height != 0 && wp->w_buffer == buf) { + wp->w_redr_status = true; + redraw_later(wp, VALID); + } } } } @@ -5020,10 +5043,8 @@ void win_redraw_last_status(const frame_T *frp) } } -/* - * Draw the verticap separator right of window "wp" starting with line "row". - */ -static void draw_vsep_win(win_T *wp, int row) +/// Draw the vertical separator right of window "wp" +static void draw_vsep_win(win_T *wp) { int hl; int c; @@ -5031,15 +5052,97 @@ static void draw_vsep_win(win_T *wp, int row) if (wp->w_vsep_width) { // draw the vertical separator right of this window c = fillchar_vsep(wp, &hl); - grid_fill(&default_grid, wp->w_winrow + row, W_ENDROW(wp), + grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp), W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } } +/// Draw the horizontal separator below window "wp" +static void draw_hsep_win(win_T *wp) +{ + int hl; + int c; + + if (wp->w_hsep_height) { + // draw the horizontal separator below this window + c = fillchar_hsep(wp, &hl); + grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1, + wp->w_wincol, W_ENDCOL(wp), c, c, hl); + } +} -/* - * Get the length of an item as it will be shown in the status line. - */ +/// Get the separator connector for specified window corner of window "wp" +static int get_corner_sep_connector(win_T *wp, WindowCorner corner) +{ + // It's impossible for windows to be connected neither vertically nor horizontally + // So if they're not vertically connected, assume they're horizontally connected + if (vsep_connected(wp, corner)) { + if (hsep_connected(wp, corner)) { + return wp->w_p_fcs_chars.verthoriz; + } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) { + return wp->w_p_fcs_chars.vertright; + } else { + return wp->w_p_fcs_chars.vertleft; + } + } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) { + return wp->w_p_fcs_chars.horizdown; + } else { + return wp->w_p_fcs_chars.horizup; + } +} + +/// Draw seperator connecting characters on the corners of window "wp" +static void draw_sep_connectors_win(win_T *wp) +{ + // Don't draw separator connectors unless global statusline is enabled and the window has + // either a horizontal or vertical separator + if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) { + return; + } + + int hl = win_hl_attr(wp, HLF_C); + + // Determine which edges of the screen the window is located on so we can avoid drawing separators + // on corners contained in those edges + bool win_at_top; + bool win_at_bottom = wp->w_hsep_height == 0; + bool win_at_left; + bool win_at_right = wp->w_vsep_width == 0; + frame_T *frp; + + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) { + break; + } + } + win_at_top = frp->fr_parent == NULL; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) { + break; + } + } + win_at_left = frp->fr_parent == NULL; + + // Draw the appropriate separator connector in every corner where drawing them is necessary + if (!(win_at_top || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT), + wp->w_winrow - 1, wp->w_wincol - 1, hl); + } + if (!(win_at_top || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT), + wp->w_winrow - 1, W_ENDCOL(wp), hl); + } + if (!(win_at_bottom || win_at_left)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT), + W_ENDROW(wp), wp->w_wincol - 1, hl); + } + if (!(win_at_bottom || win_at_right)) { + grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT), + W_ENDROW(wp), W_ENDCOL(wp), hl); + } +} + +/// Get the length of an item as it will be shown in the status line. static int status_match_len(expand_T *xp, char_u *s) { int len = 0; @@ -5240,7 +5343,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in // Create status line if needed by setting 'laststatus' to 2. // Set 'winminheight' to zero to avoid that the window is // resized. - if (lastwin->w_status_height == 0) { + if (lastwin->w_status_height == 0 && global_stl_height() == 0) { save_p_ls = p_ls; save_p_wmh = p_wmh; p_ls = 2; @@ -5276,12 +5379,15 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char_u **matches, in static void win_redr_status(win_T *wp) { int row; + int col; char_u *p; int len; int fillchar; int attr; + int width; int this_ru_col; - static int busy = FALSE; + bool is_stl_global = global_stl_height() > 0; + static int busy = false; // May get here recursively when 'statusline' (indirectly) // invokes ":redrawstatus". Simply ignore the call then. @@ -5292,9 +5398,9 @@ static void win_redr_status(win_T *wp) } busy = true; - wp->w_redr_status = FALSE; - if (wp->w_status_height == 0) { - // no status line, can only be last window + wp->w_redr_status = false; + if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) { + // no status line, either global statusline is enabled or the window is a last window redraw_cmdline = true; } else if (!redrawing()) { // Don't redraw right now, do it later. Don't update status line when @@ -5305,6 +5411,7 @@ static void win_redr_status(win_T *wp) redraw_custom_statusline(wp); } else { fillchar = fillchar_status(&attr, wp); + width = is_stl_global ? Columns : wp->w_width; get_trans_bufname(wp->w_buffer); p = NameBuff; @@ -5333,9 +5440,9 @@ static void win_redr_status(win_T *wp) // len += (int)STRLEN(p + len); // dead assignment } - this_ru_col = ru_col - (Columns - wp->w_width); - if (this_ru_col < (wp->w_width + 1) / 2) { - this_ru_col = (wp->w_width + 1) / 2; + this_ru_col = ru_col - (Columns - width); + if (this_ru_col < (width + 1) / 2) { + this_ru_col = (width + 1) / 2; } if (this_ru_col <= 1) { p = (char_u *)"<"; // No room for file name! @@ -5360,10 +5467,11 @@ static void win_redr_status(win_T *wp) } } - row = W_ENDROW(wp); - grid_puts(&default_grid, p, row, wp->w_wincol, attr); - grid_fill(&default_grid, row, row + 1, len + wp->w_wincol, - this_ru_col + wp->w_wincol, fillchar, fillchar, attr); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); + col = is_stl_global ? 0 : wp->w_wincol; + grid_puts(&default_grid, p, row, col, attr); + grid_fill(&default_grid, row, row + 1, len + col, + this_ru_col + col, fillchar, fillchar, attr); if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) { @@ -5442,6 +5550,76 @@ bool stl_connected(win_T *wp) return false; } +/// Check if horizontal separator of window "wp" at specified window corner is connected to the +/// horizontal separator of another window +/// Assumes global statusline is enabled +static bool hsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT); + int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) + ? wp->w_winrow - 1 : W_ENDROW(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_ROW && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) { + fr = fr->fr_next; + } + } + } + + return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win)); +} + +/// Check if vertical separator of window "wp" at specified window corner is connected to the +/// vertical separator of another window +static bool vsep_connected(win_T *wp, WindowCorner corner) +{ + bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT); + int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) + ? wp->w_wincol - 1 : W_ENDCOL(wp); + frame_T *fr = wp->w_frame; + + while (fr->fr_parent != NULL) { + if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) { + fr = before ? fr->fr_prev : fr->fr_next; + break; + } + fr = fr->fr_parent; + } + if (fr->fr_parent == NULL) { + return false; + } + while (fr->fr_layout != FR_LEAF) { + fr = fr->fr_child; + if (fr->fr_parent->fr_layout == FR_COL && before) { + while (fr->fr_next != NULL) { + fr = fr->fr_next; + } + } else { + while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) { + fr = fr->fr_next; + } + } + } + + return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win)); +} /// Get the value to show for the language mappings, active 'keymap'. /// @@ -5508,6 +5686,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) int use_sandbox = false; win_T *ewp; int p_crb_save; + bool is_stl_global = global_stl_height() > 0; ScreenGrid *grid = &default_grid; @@ -5529,9 +5708,9 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) maxwidth = Columns; use_sandbox = was_set_insecurely(wp, "tabline", 0); } else { - row = W_ENDROW(wp); + row = is_stl_global ? (Rows - p_ch - 1) : W_ENDROW(wp); fillchar = fillchar_status(&attr, wp); - maxwidth = wp->w_width; + maxwidth = is_stl_global ? Columns : wp->w_width; if (draw_ruler) { stl = p_ruf; @@ -5549,12 +5728,12 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) stl = p_ruf; } } - col = ru_col - (Columns - wp->w_width); - if (col < (wp->w_width + 1) / 2) { - col = (wp->w_width + 1) / 2; + col = ru_col - (Columns - maxwidth); + if (col < (maxwidth + 1) / 2) { + col = (maxwidth + 1) / 2; } - maxwidth = wp->w_width - col; - if (!wp->w_status_height) { + maxwidth = maxwidth - col; + if (!wp->w_status_height && !is_stl_global) { grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling @@ -5572,7 +5751,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL); } - col += wp->w_wincol; + col += is_stl_global ? 0 : wp->w_wincol; } if (maxwidth <= 0) { @@ -7154,10 +7333,10 @@ int showmode(void) clear_showcmd(); } - // If the last window has no status line, the ruler is after the mode - // message and must be redrawn + // If the last window has no status line and global statusline is disabled, + // the ruler is after the mode message and must be redrawn win_T *last = lastwin_nofloating(); - if (redrawing() && last->w_status_height == 0) { + if (redrawing() && last->w_status_height == 0 && global_stl_height() == 0) { win_redr_ruler(last, true); } redraw_cmdline = false; @@ -7472,16 +7651,22 @@ int fillchar_status(int *attr, win_T *wp) return '='; } -/* - * Get the character to use in a separator between vertically split windows. - * Get its attributes in "*attr". - */ +/// Get the character to use in a separator between vertically split windows. +/// Get its attributes in "*attr". static int fillchar_vsep(win_T *wp, int *attr) { *attr = win_hl_attr(wp, HLF_C); return wp->w_p_fcs_chars.vert; } +/// Get the character to use in a separator between horizontally split windows. +/// Get its attributes in "*attr". +static int fillchar_hsep(win_T *wp, int *attr) +{ + *attr = win_hl_attr(wp, HLF_C); + return wp->w_p_fcs_chars.horiz; +} + /* * Return TRUE if redrawing should currently be done. */ @@ -7507,7 +7692,8 @@ void showruler(bool always) if (!always && !redrawing()) { return; } - if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) { + if ((*p_stl != NUL || *curwin->w_p_stl != NUL) + && (curwin->w_status_height || global_stl_height())) { redraw_custom_statusline(curwin); } else { win_redr_ruler(curwin, always); @@ -7526,6 +7712,7 @@ void showruler(bool always) static void win_redr_ruler(win_T *wp, bool always) { + bool is_stl_global = global_stl_height() > 0; static bool did_show_ext_ruler = false; // If 'ruler' off or redrawing disabled, don't do anything @@ -7543,7 +7730,7 @@ static void win_redr_ruler(win_T *wp, bool always) // Don't draw the ruler while doing insert-completion, it might overwrite // the (long) mode message. - if (wp == lastwin && lastwin->w_status_height == 0) { + if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) { if (edit_submode != NULL) { return; } @@ -7598,6 +7785,12 @@ static void win_redr_ruler(win_T *wp, bool always) off = wp->w_wincol; width = wp->w_width; part_of_status = true; + } else if (is_stl_global) { + row = Rows - p_ch - 1; + fillchar = fillchar_status(&attr, wp); + off = 0; + width = Columns; + part_of_status = true; } else { row = Rows - 1; fillchar = ' '; @@ -7637,7 +7830,7 @@ static void win_redr_ruler(win_T *wp, bool always) int i = (int)STRLEN(buffer); get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1); int o = i + vim_strsize(buffer + i + 1); - if (wp->w_status_height == 0) { // can't use last char of screen + if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen o++; } int this_ru_col = ru_col - (Columns - width); diff --git a/src/nvim/screen.h b/src/nvim/screen.h index d704a6eb8a..9e149594e1 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -21,6 +21,14 @@ #define NOT_VALID 40 // buffer needs complete redraw #define CLEAR 50 // screen messed up, clear it +/// corner value flags for hsep_connected and vsep_connected +typedef enum { + WC_TOP_LEFT = 0, + WC_TOP_RIGHT, + WC_BOTTOM_LEFT, + WC_BOTTOM_RIGHT +} WindowCorner; + /// By default, all widows are draw on a single rectangular grid, represented by /// this ScreenGrid instance. In multigrid mode each window will have its own /// grid, then this is only used for global screen elements that hasn't been diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 119f6e811f..ac764e2d99 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6173,6 +6173,7 @@ static const char *highlight_init_both[] = { "TermCursor cterm=reverse gui=reverse", "VertSplit cterm=reverse gui=reverse", "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", + "default link WinSeparator VertSplit", "default link EndOfBuffer NonText", "default link LineNrAbove LineNr", "default link LineNrBelow LineNr", @@ -6183,7 +6184,7 @@ static const char *highlight_init_both[] = { "default link Whitespace NonText", "default link MsgSeparator StatusLine", "default link NormalFloat Pmenu", - "default link FloatBorder VertSplit", + "default link FloatBorder WinSeparator", "default FloatShadow blend=80 guibg=Black", "default FloatShadowThrough blend=100 guibg=Black", "RedrawDebugNormal cterm=reverse gui=reverse", diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index 6971ecd357..aa7b3a225b 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -146,7 +146,7 @@ func Test_highlight_eol_with_cursorline_vertsplit() " 'abcd |abcd ' " ^^^^ ^^^^^^^^^ no highlight " ^ 'Search' highlight - " ^ 'VertSplit' highlight + " ^ 'WinSeparator' highlight let attrs0 = ScreenAttrs(1, 15)[0] call assert_equal(repeat([attrs0[0]], 4), attrs0[0:3]) call assert_equal(repeat([attrs0[0]], 9), attrs0[6:14]) @@ -160,7 +160,7 @@ func Test_highlight_eol_with_cursorline_vertsplit() " 'abcd |abcd ' " ^^^^ underline " ^ 'Search' highlight with underline - " ^ 'VertSplit' highlight + " ^ 'WinSeparator' highlight " ^^^^^^^^^ no highlight " underline diff --git a/src/nvim/window.c b/src/nvim/window.c index e09af7a7bb..8cac9f23a1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -60,7 +60,7 @@ #define NOWIN (win_T *)-1 // non-existing window -#define ROWS_AVAIL (Rows - p_ch - tabline_height()) +#define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height()) /// flags for win_enter_ext() typedef enum { @@ -647,6 +647,7 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) } wp->w_floating = 1; wp->w_status_height = 0; + wp->w_hsep_height = 0; wp->w_vsep_width = 0; win_config_float(wp, fconfig); @@ -956,6 +957,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int before; int minheight; int wmh1; + int hsep_height; bool did_set_fraction = false; if (flags & WSP_TOP) { @@ -1063,6 +1065,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } else { + hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; layout = FR_COL; /* @@ -1071,7 +1074,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) */ // Current window requires at least 1 space. wmh1 = p_wmh == 0 ? 1 : p_wmh; - needed = wmh1 + STATUS_HEIGHT; + needed = wmh1 + hsep_height; if (flags & WSP_ROOM) { needed += p_wh - wmh1; } @@ -1113,15 +1116,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_size = oldwin_height / 2; } - if (new_size > available - minheight - STATUS_HEIGHT) { - new_size = available - minheight - STATUS_HEIGHT; + if (new_size > available - minheight - hsep_height) { + new_size = available - minheight - hsep_height; } if (new_size < wmh1) { new_size = wmh1; } // if it doesn't fit in the current window, need win_equal() - if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) { + if (oldwin_height - new_size - hsep_height < p_wmh) { do_equal = true; } @@ -1134,7 +1137,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) set_fraction(oldwin); did_set_fraction = true; - win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, + win_setheight_win(oldwin->w_height + new_size + hsep_height, oldwin); oldwin_height = oldwin->w_height; if (need_status) { @@ -1151,8 +1154,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_height > new_size - || frp->fr_win->w_height > oldwin_height - new_size - - STATUS_HEIGHT)) { + || frp->fr_win->w_height > oldwin_height - new_size - hsep_height)) { do_equal = true; break; } @@ -1278,13 +1280,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & (WSP_TOP | WSP_BOT)) { // set height and row of new window to full height wp->w_winrow = tabline_height(); - win_new_height(wp, curfrp->fr_height - (p_ls > 0)); - wp->w_status_height = (p_ls > 0); + win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2)); + wp->w_status_height = (p_ls == 1 || p_ls == 2); + wp->w_hsep_height = 0; } else { // height and row of new window is same as current window wp->w_winrow = oldwin->w_winrow; win_new_height(wp, oldwin->w_height); wp->w_status_height = oldwin->w_status_height; + wp->w_hsep_height = oldwin->w_hsep_height; } frp->fr_height = curfrp->fr_height; @@ -1317,6 +1321,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) frame_fix_width(oldwin); frame_fix_width(wp); } else { + bool is_stl_global = global_stl_height() > 0; // width and column of new window is same as current window if (flags & (WSP_TOP | WSP_BOT)) { wp->w_wincol = 0; @@ -1332,28 +1337,53 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // "new_size" of the current window goes to the new window, use // one row for the status line win_new_height(wp, new_size); + if (before) { + wp->w_hsep_height = is_stl_global ? 1 : 0; + } else { + wp->w_hsep_height = oldwin->w_hsep_height; + oldwin->w_hsep_height = is_stl_global ? 1 : 0; + } if (flags & (WSP_TOP | WSP_BOT)) { int new_fr_height = curfrp->fr_height - new_size; - if (!((flags & WSP_BOT) && p_ls == 0)) { + if (!((flags & WSP_BOT) && p_ls == 0) && global_stl_height() == 0) { new_fr_height -= STATUS_HEIGHT; + } else if (global_stl_height() > 0) { + if (flags & WSP_BOT) { + frame_add_hsep(curfrp); + } else { + new_fr_height -= 1; + } } frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false); } else { - win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); + win_new_height(oldwin, oldwin_height - (new_size + + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT))); } + if (before) { // new window above current one wp->w_winrow = oldwin->w_winrow; - wp->w_status_height = STATUS_HEIGHT; - oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + + if (is_stl_global) { + wp->w_status_height = 0; + oldwin->w_winrow += wp->w_height + 1; + } else { + wp->w_status_height = STATUS_HEIGHT; + oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + } } else { // new window below current one - wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; - wp->w_status_height = oldwin->w_status_height; - if (!(flags & WSP_BOT)) { - oldwin->w_status_height = STATUS_HEIGHT; + if (is_stl_global) { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1; + wp->w_status_height = 0; + } else { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; + wp->w_status_height = oldwin->w_status_height; + if (!(flags & WSP_BOT)) { + oldwin->w_status_height = STATUS_HEIGHT; + } } } - if (flags & WSP_BOT) { + if ((flags & WSP_BOT) && global_stl_height() == 0) { frame_add_statusline(curfrp); } frame_fix_height(wp); @@ -1595,10 +1625,10 @@ int make_windows(int count, bool vertical) maxcount = (curwin->w_width + curwin->w_vsep_width - (p_wiw - p_wmw)) / (p_wmw + 1); } else { - // Each window needs at least 'winminheight' lines and a status line. - maxcount = (curwin->w_height - + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); + // Each window needs at least 'winminheight' lines + // If statusline isn't global, each window also needs a statusline + maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height + - (p_wh - p_wmh)) / (p_wmh + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT)); } if (maxcount < 2) { @@ -1693,7 +1723,7 @@ static void win_exchange(long Prenum) * if wp != wp2 * 3. remove wp from the list * 4. insert wp after wp2 - * 5. exchange the status line height and vsep width. + * 5. exchange the status line height, hsep height and vsep width. */ wp2 = curwin->w_prev; frp2 = curwin->w_frame->fr_prev; @@ -1719,6 +1749,9 @@ static void win_exchange(long Prenum) temp = curwin->w_vsep_width; curwin->w_vsep_width = wp->w_vsep_width; wp->w_vsep_width = temp; + temp = curwin->w_hsep_height; + curwin->w_hsep_height = wp->w_hsep_height; + wp->w_hsep_height = temp; frame_fix_height(curwin); frame_fix_height(wp); @@ -1793,10 +1826,13 @@ static void win_rotate(bool upwards, int count) frame_insert(frp->fr_parent->fr_child, frp); } - // exchange status height and vsep width of old and new last window + // exchange status height, hsep height and vsep width of old and new last window n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; + n = wp2->w_hsep_height; + wp2->w_hsep_height = wp1->w_hsep_height; + wp1->w_hsep_height = n; frame_fix_height(wp1); frame_fix_height(wp2); n = wp2->w_vsep_width; @@ -1870,11 +1906,16 @@ void win_move_after(win_T *win1, win_T *win2) // check if there is something to do if (win2->w_next != win1) { - // may need move the status line/vertical separator of the last window + // may need move the status line, horizontal or vertical separator of the last window if (win1 == lastwin) { height = win1->w_prev->w_status_height; win1->w_prev->w_status_height = win1->w_status_height; win1->w_status_height = height; + + height = win1->w_prev->w_hsep_height; + win1->w_prev->w_hsep_height = win1->w_hsep_height; + win1->w_hsep_height = height; + if (win1->w_prev->w_vsep_width == 1) { // Remove the vertical separator from the last-but-one window, // add it to the last window. Adjust the frame widths. @@ -1887,6 +1928,11 @@ void win_move_after(win_T *win1, win_T *win2) height = win1->w_status_height; win1->w_status_height = win2->w_status_height; win2->w_status_height = height; + + height = win1->w_hsep_height; + win1->w_hsep_height = win2->w_hsep_height; + win2->w_hsep_height = height; + if (win1->w_vsep_width == 1) { // Remove the vertical separator from win1, add it to the last // window, win2. Adjust the frame widths. @@ -1950,6 +1996,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int int room = 0; int new_size; int has_next_curwin = 0; + int hsep_height; bool hnc; if (topfr->fr_layout == FR_LEAF) { @@ -2095,19 +2142,22 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int totwincount -= wincount; } } else { // topfr->fr_layout == FR_COL + hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; topfr->fr_width = width; topfr->fr_height = height; if (dir != 'h') { // equalize frame heights // Compute maximum number of windows vertically in this frame. n = frame_minheight(topfr, NOWIN); - // add one for the bottom window if it doesn't have a statusline + // add one for the bottom window if it doesn't have a statusline or separator if (row + height == cmdline_row && p_ls == 0) { + extra_sep = STATUS_HEIGHT; + } else if (global_stl_height() > 0) { extra_sep = 1; } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmh + 1); + totwincount = (n + extra_sep) / (p_wmh + hsep_height); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2142,7 +2192,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmh + 1); + ? extra_sep : 0)) / (p_wmh + hsep_height); } room -= new_size - n; if (room < 0) { @@ -2188,7 +2238,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmh + 1); + / (p_wmh + hsep_height); m = frame_minheight(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -3134,7 +3184,7 @@ static tabpage_T *alt_tabpage(void) /* * Find the left-upper window in frame "frp". */ -static win_T *frame2win(frame_T *frp) +win_T *frame2win(frame_T *frp) { while (frp->fr_win == NULL) { frp = frp->fr_child; @@ -3161,23 +3211,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp) return false; } +/// Check if current window is at the bottom +/// Returns true if there are no windows below current window +static bool is_bottom_win(win_T *wp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + frame_T *frp; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) { + return false; + } + } + return true; +} /// Set a new height for a frame. Recursively sets the height for contained /// frames and windows. Caller must take care of positions. /// /// @param topfirst resize topmost contained frame first. /// @param wfh obey 'winfixheight' when there is a choice; /// may cause the height not to be set. -static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) +void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) FUNC_ATTR_NONNULL_ALL { frame_T *frp; int extra_lines; int h; + win_T *wp; if (topfrp->fr_win != NULL) { // Simple case: just one window. - win_new_height(topfrp->fr_win, - height - topfrp->fr_win->w_status_height); + wp = topfrp->fr_win; + if (is_bottom_win(wp)) { + wp->w_hsep_height = 0; + } + win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height); } else if (topfrp->fr_layout == FR_ROW) { do { // All frames in this row get the same new height. @@ -3333,8 +3400,8 @@ static void frame_add_statusline(frame_T *frp) if (frp->fr_layout == FR_LEAF) { wp = frp->fr_win; if (wp->w_status_height == 0) { - if (wp->w_height > 0) { // don't make it negative - --wp->w_height; + if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative + wp->w_height -= STATUS_HEIGHT; } wp->w_status_height = STATUS_HEIGHT; } @@ -3454,10 +3521,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw topfrp->fr_width = width; } -/* - * Add the vertical separator to windows at the right side of "frp". - * Note: Does not check if there is room! - */ +/// Add the vertical separator to windows at the right side of "frp". +/// Note: Does not check if there is room! static void frame_add_vsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { @@ -3487,6 +3552,37 @@ static void frame_add_vsep(const frame_T *frp) } } +/// Add the horizontal separator to windows at the bottom of "frp". +/// Note: Does not check if there is room or whether the windows have a statusline! +static void frame_add_hsep(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) +{ + win_T *wp; + + if (frp->fr_layout == FR_LEAF) { + wp = frp->fr_win; + if (wp->w_hsep_height == 0) { + if (wp->w_height > 0) { // don't make it negative + wp->w_height++; + } + wp->w_hsep_height = 1; + } + } else if (frp->fr_layout == FR_ROW) { + // Handle all the frames in the row. + FOR_ALL_FRAMES(frp, frp->fr_child) { + frame_add_hsep(frp); + } + } else { + assert(frp->fr_layout == FR_COL); + // Only need to handle the last frame in the column. + frp = frp->fr_child; + while (frp->fr_next != NULL) { + frp = frp->fr_next; + } + frame_add_hsep(frp); + } +} + /* * Set frame width from the window it contains. */ @@ -3501,7 +3597,7 @@ static void frame_fix_width(win_T *wp) static void frame_fix_height(win_T *wp) FUNC_ATTR_NONNULL_ALL { - wp->w_frame->fr_height = wp->w_height + wp->w_status_height; + wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height; } /* @@ -3519,10 +3615,11 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) if (topfrp->fr_win != NULL) { if (topfrp->fr_win == next_curwin) { - m = p_wh + topfrp->fr_win->w_status_height; + m = p_wh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; } else { - // window: minimal height of the window plus status line - m = p_wmh + topfrp->fr_win->w_status_height; + // window: minimal height of the window plus separator column or status line + // depending on whether global statusline is enabled + m = p_wmh + topfrp->fr_win->w_hsep_height + topfrp->fr_win->w_status_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3751,7 +3848,7 @@ static int win_alloc_firstwin(win_T *oldwin) new_frame(curwin); topframe = curwin->w_frame; topframe->fr_width = Columns; - topframe->fr_height = Rows - p_ch; + topframe->fr_height = Rows - p_ch - global_stl_height(); return OK; } @@ -5147,11 +5244,8 @@ void win_size_restore(garray_T *gap) } } -/* - * Update the position for all windows, using the width and height of the - * frames. - * Returns the row just after the last window. - */ +// Update the position for all windows, using the width and height of the frames. +// Returns the row just after the last window and global statusline (if there is one). int win_comp_pos(void) { int row = tabline_height(); @@ -5166,7 +5260,7 @@ int win_comp_pos(void) } } - return row; + return row + global_stl_height(); } void win_reconfig_floats(void) @@ -5200,7 +5294,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_redr_status = true; wp->w_pos_changed = true; } - const int h = wp->w_height + wp->w_status_height; + const int h = wp->w_height + wp->w_hsep_height + wp->w_status_height; *row += h > topfrp->fr_height ? topfrp->fr_height : h; *col += wp->w_width + wp->w_vsep_width; } else { @@ -5249,7 +5343,7 @@ void win_setheight_win(int height, win_T *win) win_config_float(win, win->w_float_config); redraw_later(win, NOT_VALID); } else { - frame_setheight(win->w_frame, height + win->w_status_height); + frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height); // recompute the window positions int row = win_comp_pos(); @@ -5340,8 +5434,8 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } else { win_T *wp = lastwin_nofloating(); - room_cmdline = Rows - p_ch - - (wp->w_winrow + wp->w_height + wp->w_status_height); + room_cmdline = Rows - p_ch - global_stl_height() + - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; } @@ -5703,7 +5797,7 @@ void win_drag_status_line(win_T *dragwin, int offset) } else { // drag down up = false; // Only dragging the last status line can reduce p_ch. - room = Rows - cmdline_row; + room = Rows - cmdline_row - global_stl_height(); if (curfr->fr_next == NULL) { room -= 1; } else { @@ -6344,72 +6438,104 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u return find_file_name_in_path(ptr, len, options, count, rel_fname); } -/// Add or remove a status line for the bottom window(s), according to the +/// Add or remove a status line from window(s), according to the /// value of 'laststatus'. /// /// @param morewin pretend there are two or more windows if true. void last_status(bool morewin) { // Don't make a difference between horizontal or vertical split. - last_status_rec(topframe, (p_ls == 2 - || (p_ls == 1 && (morewin || !one_window())))); + last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window()))), + global_stl_height() > 0); } -static void last_status_rec(frame_T *fr, bool statusline) +// Look for resizable frames and take lines from them to make room for the statusline +static void resize_frame_for_status(frame_T *fr, int resize_amount) +{ + // Find a frame to take a line from. + frame_T *fp = fr; + win_T *wp = fr->fr_win; + int n; + + while (resize_amount > 0) { + while (fp->fr_height <= frame_minheight(fp, NULL)) { + if (fp == topframe) { + emsg(_(e_noroom)); + return; + } + // In a column of frames: go to frame above. If already at + // the top or in a row of frames: go to parent. + if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { + fp = fp->fr_prev; + } else { + fp = fp->fr_parent; + } + } + n = MIN(fp->fr_height - frame_minheight(fp, NULL), resize_amount); + resize_amount -= n; + + if (fp != fr) { + frame_new_height(fp, fp->fr_height - n, false, false); + frame_fix_height(wp); + (void)win_comp_pos(); + } else { + win_new_height(wp, wp->w_height - n); + } + } +} + +static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) { frame_T *fp; win_T *wp; if (fr->fr_layout == FR_LEAF) { wp = fr->fr_win; - if (wp->w_status_height != 0 && !statusline) { - // remove status line - win_new_height(wp, wp->w_height + 1); + bool is_last = is_bottom_win(wp); + + if (is_last) { + if (wp->w_status_height != 0 && (!statusline || is_stl_global)) { + // Remove status line + wp->w_status_height = 0; + win_new_height(wp, wp->w_height + STATUS_HEIGHT); + comp_col(); + } else if (wp->w_status_height == 0 && !is_stl_global && statusline) { + // Add statusline to window if needed + wp->w_status_height = STATUS_HEIGHT; + resize_frame_for_status(fr, STATUS_HEIGHT); + comp_col(); + } + } else if (wp->w_status_height != 0 && is_stl_global) { + // If statusline is global and the window has a statusline, replace it with a horizontal + // separator + if (STATUS_HEIGHT - 1 != 0) { + win_new_height(wp, wp->w_height + STATUS_HEIGHT - 1); + } wp->w_status_height = 0; + wp->w_hsep_height = 1; comp_col(); - } else if (wp->w_status_height == 0 && statusline) { - // Find a frame to take a line from. - fp = fr; - while (fp->fr_height <= frame_minheight(fp, NULL)) { - if (fp == topframe) { - emsg(_(e_noroom)); - return; - } - // In a column of frames: go to frame above. If already at - // the top or in a row of frames: go to parent. - if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { - fp = fp->fr_prev; - } else { - fp = fp->fr_parent; - } - } - wp->w_status_height = 1; - if (fp != fr) { - frame_new_height(fp, fp->fr_height - 1, false, false); - frame_fix_height(wp); - (void)win_comp_pos(); - } else { - win_new_height(wp, wp->w_height - 1); - } + } else if (wp->w_status_height == 0 && !is_stl_global) { + // If statusline isn't global and the window doesn't have a statusline, re-add it + wp->w_status_height = STATUS_HEIGHT; + wp->w_hsep_height = 0; + resize_frame_for_status(fr, STATUS_HEIGHT - 1); comp_col(); - redraw_all_later(SOME_VALID); } - } else if (fr->fr_layout == FR_ROW) { - // vertically split windows, set status line for each one + redraw_all_later(SOME_VALID); + } else if (fr->fr_layout == FR_COL) { + // For a column frame, recursively call this function for all child frames FOR_ALL_FRAMES(fp, fr->fr_child) { - last_status_rec(fp, statusline); + last_status_rec(fp, statusline, is_stl_global); } } else { - // horizontally split window, set status line for last one - for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { + // For a row frame, recursively call this function for all child frames + FOR_ALL_FRAMES(fp, fr->fr_child) { + last_status_rec(fp, statusline, is_stl_global); } - last_status_rec(fp, statusline); } } -/* - * Return the number of lines used by the tab page line. - */ +/// Return the number of lines used by the tab page line. int tabline_height(void) { if (ui_has(kUITabline)) { @@ -6425,10 +6551,14 @@ int tabline_height(void) return 1; } -/* - * Return the minimal number of rows that is needed on the screen to display - * the current number of windows. - */ +/// Return the number of lines used by the global statusline +int global_stl_height(void) +{ + return (p_ls == 3) ? STATUS_HEIGHT : 0; +} + +/// Return the minimal number of rows that is needed on the screen to display +/// the current number of windows. int min_rows(void) { if (firstwin == NULL) { // not initialized yet @@ -6442,7 +6572,7 @@ int min_rows(void) total = n; } } - total += tabline_height(); + total += tabline_height() + global_stl_height(); total += 1; // count the room for the command line return total; } -- cgit From 55b6ade7fee36283dc2853494edf9a5ac2dd4be9 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 16 Mar 2022 22:18:14 +0100 Subject: feat(ui_client): implement async paste handling --- src/nvim/tui/input.c | 12 ++++++++++-- src/nvim/ui_client.c | 2 -- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index faae921a68..917847608a 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -126,8 +126,16 @@ static void tinput_wait_enqueue(void **argv) const String keys = { .data = buf, .size = len }; if (input->paste) { String copy = copy_string(keys); - multiqueue_put(main_loop.events, tinput_paste_event, 3, - copy.data, copy.size, (intptr_t)input->paste); + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(copy_string(keys))); // 'data' + ADD(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + rpc_send_event(ui_client_channel_id, "nvim_paste", args); + } else { + multiqueue_put(main_loop.events, tinput_paste_event, 3, + copy.data, copy.size, (intptr_t)input->paste); + } if (input->paste == 1) { // Paste phase: "continue" input->paste = 2; diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index aa33f2fc73..6e45a28e89 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -52,8 +52,6 @@ void ui_client_init(uint64_t chan) /// Handler for "redraw" events sent by the NVIM server /// -/// This is just a stub. The mentioned functionality will be implemented. -/// /// This function will be called by handle_request (in msgpack_rpc/channel.c) /// The individual ui_events sent by the server are individually handled /// by their respective handlers defined in ui_events_client.generated.h -- cgit From cac90d2de728181edce7ba38fb9ad588d231651b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Mar 2022 03:21:47 +0800 Subject: feat(api, lua): support converting nested Funcref back to LuaRef (#17749) --- src/nvim/api/private/converter.c | 17 ++++++++--------- src/nvim/lua/converter.c | 16 +++++++--------- 2 files changed, 15 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 49e3cf7df7..82ec1ad0d8 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -64,7 +64,14 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ + ufunc_T *fp = find_func(fun); \ + assert(fp != NULL); \ + if (fp->uf_cb == nlua_CFunction_func_call) { \ + LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + kvi_push(edata->stack, LUAREF_OBJ(ref)); \ + } else { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + } \ goto typval_encode_stop_converting_one_item; \ } while (0) @@ -231,14 +238,6 @@ static inline void typval_encode_dict_end(EncodedData *const edata) /// @return The converted value Object vim_to_object(typval_T *obj) { - if (obj->v_type == VAR_FUNC) { - ufunc_T *fp = find_func(obj->vval.v_string); - assert(fp != NULL); - if (fp->uf_cb == nlua_CFunction_func_call) { - LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); - return LUAREF_OBJ(ref); - } - } EncodedData edata; kvi_init(edata.stack); const int evo_ret = encode_vim_to_object(&edata, obj, diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index beea1959e1..0bb224c729 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -476,7 +476,13 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ - TYPVAL_ENCODE_CONV_NIL(tv); \ + ufunc_T *fp = find_func(fun); \ + assert(fp != NULL); \ + if (fp->uf_cb == nlua_CFunction_func_call) { \ + nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ + } else { \ + TYPVAL_ENCODE_CONV_NIL(tv); \ + } \ goto typval_encode_stop_converting_one_item; \ } while (0) @@ -615,14 +621,6 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4); return false; } - if (tv->v_type == VAR_FUNC) { - ufunc_T *fp = find_func(tv->vval.v_string); - assert(fp != NULL); - if (fp->uf_cb == nlua_CFunction_func_call) { - nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); - return true; - } - } if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) { return false; } -- cgit From 00effff56944d5b59440dcdb5e3496d49a76d3e2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 04:47:08 +0000 Subject: vim-patch:8.1.1693: syntax coloring and highlighting is in one big file (#17721) Problem: Syntax coloring and highlighting is in one big file. Solution: Move the highlighting to a separate file. (Yegappan Lakshmanan, closes vim/vim#4674) https://github.com/vim/vim/commit/f9cc9f209ede9f15959e4c2351e970477c139614 Name the new file highlight_group.c instead. Co-authored-by: zeertzjq --- src/nvim/api/extmark.c | 4 +- src/nvim/api/private/helpers.c | 6 +- src/nvim/api/vim.c | 6 +- src/nvim/api/win_config.c | 1 + src/nvim/buffer.c | 1 + src/nvim/cursor_shape.c | 6 +- src/nvim/decoration.c | 2 +- src/nvim/edit.c | 1 + src/nvim/eval.c | 1 + src/nvim/eval/funcs.c | 1 + src/nvim/ex_cmds.c | 1 + src/nvim/ex_docmd.c | 1 + src/nvim/ex_getln.c | 1 + src/nvim/globals.h | 5 + src/nvim/hardcopy.c | 1 + src/nvim/highlight.c | 2 +- src/nvim/highlight_group.c | 2799 +++++++++++++++++++++++++++++++++++++++ src/nvim/highlight_group.h | 19 + src/nvim/main.c | 1 + src/nvim/memory.c | 1 + src/nvim/option.c | 3 +- src/nvim/quickfix.c | 2 +- src/nvim/screen.c | 1 + src/nvim/sign.c | 9 +- src/nvim/syntax.c | 2825 +--------------------------------------- src/nvim/syntax.h | 6 - src/nvim/terminal.c | 2 +- src/nvim/ui_compositor.c | 2 +- src/nvim/window.c | 1 + 29 files changed, 2870 insertions(+), 2841 deletions(-) create mode 100644 src/nvim/highlight_group.c create mode 100644 src/nvim/highlight_group.h (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index e355f82f4d..c02688a815 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -10,10 +10,10 @@ #include "nvim/api/private/helpers.h" #include "nvim/decoration_provider.h" #include "nvim/extmark.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memline.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/extmark.c.generated.h" @@ -856,7 +856,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In int hl_id = 0; if (hl_group.size > 0) { - hl_id = syn_check_group(hl_group.data, (int)hl_group.size); + hl_id = syn_check_group(hl_group.data, hl_group.size); } else { return ns_id; } diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 9f41393c6b..88954a1aa2 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -22,6 +22,7 @@ #include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/lua/executor.h" #include "nvim/map.h" @@ -32,7 +33,6 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/option_defs.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim.h" @@ -1293,7 +1293,7 @@ int object_to_hl_id(Object obj, const char *what, Error *err) { if (obj.type == kObjectTypeString) { String str = obj.data.string; - return str.size ? syn_check_group(str.data, (int)str.size) : 0; + return str.size ? syn_check_group(str.data, str.size) : 0; } else if (obj.type == kObjectTypeInteger) { return MAX((int)obj.data.integer, 0); } else { @@ -1327,7 +1327,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err) String hl = chunk.items[1].data.string; if (hl.size > 0) { // TODO(bfredl): use object_to_hl_id and allow integer - int hl_id = syn_check_group(hl.data, (int)hl.size); + int hl_id = syn_check_group(hl.data, hl.size); attr = hl_id > 0 ? syn_id2attr(hl_id) : 0; } } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index d1ca7662f6..bdeac1a9f4 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -35,6 +35,7 @@ #include "nvim/globals.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" @@ -50,7 +51,6 @@ #include "nvim/popupmnu.h" #include "nvim/screen.h" #include "nvim/state.h" -#include "nvim/syntax.h" #include "nvim/types.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -112,7 +112,7 @@ Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err) Integer nvim_get_hl_id_by_name(String name) FUNC_API_SINCE(7) { - return syn_check_group(name.data, (int)name.size); + return syn_check_group(name.data, name.size); } Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) @@ -147,7 +147,7 @@ Dictionary nvim__get_hl_defs(Integer ns_id, Error *err) void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err) FUNC_API_SINCE(7) { - int hl_id = syn_check_group(name.data, (int)name.size); + int hl_id = syn_check_group(name.data, name.size); int link_id = -1; HlAttrs attrs = dict2hlattrs(val, true, &link_id, err); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index d8ccd67bcd..b0267f5ed0 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -10,6 +10,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/win_config.h" #include "nvim/ascii.h" +#include "nvim/highlight_group.h" #include "nvim/option.h" #include "nvim/screen.h" #include "nvim/strings.h" diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bfaee82311..dbb471a532 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -51,6 +51,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/main.h" diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index cb8c8cddf6..0e4a4bcfb0 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -9,8 +9,8 @@ #include "nvim/charset.h" #include "nvim/cursor_shape.h" #include "nvim/ex_getln.h" +#include "nvim/highlight_group.h" #include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -226,11 +226,11 @@ char *parse_shape_opt(int what) slashp = vim_strchr(p, '/'); if (slashp != NULL && slashp < endp) { // "group/langmap_group" - i = syn_check_group((char *)p, (int)(slashp - p)); + i = syn_check_group((char *)p, (size_t)(slashp - p)); p = slashp + 1; } if (round == 2) { - shape_table[idx].id = syn_check_group((char *)p, (int)(endp - p)); + shape_table[idx].id = syn_check_group((char *)p, (size_t)(endp - p)); shape_table[idx].id_lm = shape_table[idx].id; if (slashp != NULL && slashp < endp) { shape_table[idx].id = i; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 94bf1feeee..fb709d12ff 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -5,10 +5,10 @@ #include "nvim/decoration.h" #include "nvim/extmark.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/move.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 2e36962a4d..c087948810 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -26,6 +26,7 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f4ce6e3b39..af7c3d4985 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -33,6 +33,7 @@ #include "nvim/ex_session.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 9579a996bc..6e98f229b2 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -31,6 +31,7 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/globals.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/indent_c.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 1b2a9579e4..ff803c3553 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -38,6 +38,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/input.h" #include "nvim/log.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 14d0c58cd1..c6089562f3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -38,6 +38,7 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hardcopy.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/input.h" #include "nvim/keymap.h" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index c7a6309958..84fca137d2 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -35,6 +35,7 @@ #include "nvim/getchar.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/indent.h" #include "nvim/keymap.h" diff --git a/src/nvim/globals.h b/src/nvim/globals.h index cbd67afb09..605a2183f3 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -361,6 +361,11 @@ EXTERN int provider_call_nesting INIT(= 0); EXTERN int t_colors INIT(= 256); // int value of T_CCO +// Flags to indicate an additional string for highlight name completion. +EXTERN int include_none INIT(= 0); // when 1 include "None" +EXTERN int include_default INIT(= 0); // when 1 include "default" +EXTERN int include_link INIT(= 0); // when 2 include "link" and "clear" + // When highlight_match is true, highlight a match, starting at the cursor // position. Search_match_lines is the number of lines after the match (0 for // a match within one line), search_match_endcol the column number of the diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 93c8c53e33..575b239f5a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -22,6 +22,7 @@ #include "nvim/fileio.h" #include "nvim/garray.h" #include "nvim/hardcopy.h" +#include "nvim/highlight_group.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index abbda1e9fa..7d91f38d56 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -8,13 +8,13 @@ #include "nvim/decoration_provider.h" #include "nvim/highlight.h" #include "nvim/highlight_defs.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/map.h" #include "nvim/message.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c new file mode 100644 index 0000000000..f342ada3db --- /dev/null +++ b/src/nvim/highlight_group.c @@ -0,0 +1,2799 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// highlight_group.c: code for managing highlight groups + +#include "nvim/autocmd.h" +#include "nvim/api/private/helpers.h" +#include "nvim/charset.h" +#include "nvim/cursor_shape.h" +#include "nvim/ex_docmd.h" +#include "nvim/garray.h" +#include "nvim/highlight.h" +#include "nvim/highlight_group.h" +#include "nvim/lua/executor.h" +#include "nvim/map.h" +#include "nvim/option.h" +#include "nvim/runtime.h" +#include "nvim/screen.h" + +/// \addtogroup SG_SET +/// @{ +#define SG_CTERM 2 // cterm has been set +#define SG_GUI 4 // gui has been set +#define SG_LINK 8 // link has been set +/// @} + +#define MAX_SYN_NAME 200 + +// builtin |highlight-groups| +static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; + +Map(cstr_t, int) highlight_unames = MAP_INIT; + +/// The "term", "cterm" and "gui" arguments can be any combination of the +/// following names, separated by commas (but no spaces!). +static char *(hl_name_table[]) = + { "bold", "standout", "underline", "underlineline", "undercurl", "underdot", + "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; +static int hl_attr_table[] = + { HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, + HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; + +/// Structure that stores information about a highlight group. +/// The ID of a highlight group is also called group ID. It is the index in +/// the highlight_ga array PLUS ONE. +typedef struct { + char_u *sg_name; ///< highlight group name + char *sg_name_u; ///< uppercase of sg_name + bool sg_cleared; ///< "hi clear" was used + int sg_attr; ///< Screen attr @see ATTR_ENTRY + int sg_link; ///< link to this highlight group ID + int sg_deflink; ///< default link; restored in highlight_clear() + int sg_set; ///< combination of flags in \ref SG_SET + sctx_T sg_deflink_sctx; ///< script where the default link was set + sctx_T sg_script_ctx; ///< script in which the group was last set + // for terminal UIs + int sg_cterm; ///< "cterm=" highlighting attr + ///< (combination of \ref HlAttrFlags) + int sg_cterm_fg; ///< terminal fg color number + 1 + int sg_cterm_bg; ///< terminal bg color number + 1 + bool sg_cterm_bold; ///< bold attr was set for light color + // for RGB UIs + int sg_gui; ///< "gui=" highlighting attributes + ///< (combination of \ref HlAttrFlags) + RgbValue sg_rgb_fg; ///< RGB foreground color + RgbValue sg_rgb_bg; ///< RGB background color + RgbValue sg_rgb_sp; ///< RGB special color + char *sg_rgb_fg_name; ///< RGB foreground color name + char *sg_rgb_bg_name; ///< RGB background color name + char *sg_rgb_sp_name; ///< RGB special color name + + int sg_blend; ///< blend level (0-100 inclusive), -1 if unset +} HlGroup; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "highlight_group.c.generated.h" +#endif + +static inline HlGroup *HL_TABLE(void) +{ + return ((HlGroup *)((highlight_ga.ga_data))); +} + +// The default highlight groups. These are compiled-in for fast startup and +// they still work when the runtime files can't be found. +// +// When making changes here, also change runtime/colors/default.vim! + +static const char *highlight_init_both[] = { + "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", + "Cursor guibg=fg guifg=bg", + "lCursor guibg=fg guifg=bg", + "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red", + "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White", + "IncSearch cterm=reverse gui=reverse", + "ModeMsg cterm=bold gui=bold", + "NonText ctermfg=Blue gui=bold guifg=Blue", + "Normal cterm=NONE gui=NONE", + "PmenuSbar ctermbg=Grey guibg=Grey", + "StatusLine cterm=reverse,bold gui=reverse,bold", + "StatusLineNC cterm=reverse gui=reverse", + "TabLineFill cterm=reverse gui=reverse", + "TabLineSel cterm=bold gui=bold", + "TermCursor cterm=reverse gui=reverse", + "VertSplit cterm=reverse gui=reverse", + "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", + "default link WinSeparator VertSplit", + "default link EndOfBuffer NonText", + "default link LineNrAbove LineNr", + "default link LineNrBelow LineNr", + "default link QuickFixLine Search", + "default link CursorLineSign SignColumn", + "default link CursorLineFold FoldColumn", + "default link Substitute Search", + "default link Whitespace NonText", + "default link MsgSeparator StatusLine", + "default link NormalFloat Pmenu", + "default link FloatBorder WinSeparator", + "default FloatShadow blend=80 guibg=Black", + "default FloatShadowThrough blend=100 guibg=Black", + "RedrawDebugNormal cterm=reverse gui=reverse", + "RedrawDebugClear ctermbg=Yellow guibg=Yellow", + "RedrawDebugComposed ctermbg=Green guibg=Green", + "RedrawDebugRecompose ctermbg=Red guibg=Red", + "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", + "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", + "default link String Constant", + "default link Character Constant", + "default link Number Constant", + "default link Boolean Constant", + "default link Float Number", + "default link Function Identifier", + "default link Conditional Statement", + "default link Repeat Statement", + "default link Label Statement", + "default link Operator Statement", + "default link Keyword Statement", + "default link Exception Statement", + "default link Include PreProc", + "default link Define PreProc", + "default link Macro PreProc", + "default link PreCondit PreProc", + "default link StorageClass Type", + "default link Structure Type", + "default link Typedef Type", + "default link Tag Special", + "default link SpecialChar Special", + "default link Delimiter Special", + "default link SpecialComment Special", + "default link Debug Special", + "default DiagnosticError ctermfg=1 guifg=Red", + "default DiagnosticWarn ctermfg=3 guifg=Orange", + "default DiagnosticInfo ctermfg=4 guifg=LightBlue", + "default DiagnosticHint ctermfg=7 guifg=LightGrey", + "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", + "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", + "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", + "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", + "default link DiagnosticVirtualTextError DiagnosticError", + "default link DiagnosticVirtualTextWarn DiagnosticWarn", + "default link DiagnosticVirtualTextInfo DiagnosticInfo", + "default link DiagnosticVirtualTextHint DiagnosticHint", + "default link DiagnosticFloatingError DiagnosticError", + "default link DiagnosticFloatingWarn DiagnosticWarn", + "default link DiagnosticFloatingInfo DiagnosticInfo", + "default link DiagnosticFloatingHint DiagnosticHint", + "default link DiagnosticSignError DiagnosticError", + "default link DiagnosticSignWarn DiagnosticWarn", + "default link DiagnosticSignInfo DiagnosticInfo", + "default link DiagnosticSignHint DiagnosticHint", + NULL +}; + +// Default colors only used with a light background. +static const char *highlight_init_light[] = { + "ColorColumn ctermbg=LightRed guibg=LightRed", + "CursorColumn ctermbg=LightGrey guibg=Grey90", + "CursorLine cterm=underline guibg=Grey90", + "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", + "DiffAdd ctermbg=LightBlue guibg=LightBlue", + "DiffChange ctermbg=LightMagenta guibg=LightMagenta", + "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", + "Directory ctermfg=DarkBlue guifg=Blue", + "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", + "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue", + "LineNr ctermfg=Brown guifg=Brown", + "MatchParen ctermbg=Cyan guibg=Cyan", + "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen", + "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta", + "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey", + "PmenuThumb ctermbg=Black guibg=Black", + "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen", + "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE", + "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", + "SpecialKey ctermfg=DarkBlue guifg=Blue", + "SpellBad ctermbg=LightRed guisp=Red gui=undercurl", + "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl", + "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl", + "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl", + "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey", + "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", + "Visual guibg=LightGrey", + "WarningMsg ctermfg=DarkRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", + "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", + "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", + "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", + "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + NULL +}; + +// Default colors only used with a dark background. +static const char *highlight_init_dark[] = { + "ColorColumn ctermbg=DarkRed guibg=DarkRed", + "CursorColumn ctermbg=DarkGrey guibg=Grey40", + "CursorLine cterm=underline guibg=Grey40", + "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", + "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", + "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", + "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", + "Directory ctermfg=LightCyan guifg=Cyan", + "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", + "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan", + "LineNr ctermfg=Yellow guifg=Yellow", + "MatchParen ctermbg=DarkCyan guibg=DarkCyan", + "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen", + "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta", + "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey", + "PmenuThumb ctermbg=White guibg=White", + "Question ctermfg=LightGreen gui=bold guifg=Green", + "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", + "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", + "SpecialKey ctermfg=LightBlue guifg=Cyan", + "SpellBad ctermbg=Red guisp=Red gui=undercurl", + "SpellCap ctermbg=Blue guisp=Blue gui=undercurl", + "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl", + "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl", + "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey", + "Title ctermfg=LightMagenta gui=bold guifg=Magenta", + "Visual guibg=DarkGrey", + "WarningMsg ctermfg=LightRed guifg=Red", + "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", + "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", + "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", + "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", + "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", + "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", + "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", + "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", + "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", + NULL +}; + +const char *const highlight_init_cmdline[] = { + // XXX When modifying a list modify it in both valid and invalid halves. + // TODO(ZyX-I): merge valid and invalid groups via a macros. + + // NvimInternalError should appear only when highlighter has a bug. + "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red", + + // Highlight groups (links) used by parser: + + "default link NvimAssignment Operator", + "default link NvimPlainAssignment NvimAssignment", + "default link NvimAugmentedAssignment NvimAssignment", + "default link NvimAssignmentWithAddition NvimAugmentedAssignment", + "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment", + "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment", + + "default link NvimOperator Operator", + + "default link NvimUnaryOperator NvimOperator", + "default link NvimUnaryPlus NvimUnaryOperator", + "default link NvimUnaryMinus NvimUnaryOperator", + "default link NvimNot NvimUnaryOperator", + + "default link NvimBinaryOperator NvimOperator", + "default link NvimComparison NvimBinaryOperator", + "default link NvimComparisonModifier NvimComparison", + "default link NvimBinaryPlus NvimBinaryOperator", + "default link NvimBinaryMinus NvimBinaryOperator", + "default link NvimConcat NvimBinaryOperator", + "default link NvimConcatOrSubscript NvimConcat", + "default link NvimOr NvimBinaryOperator", + "default link NvimAnd NvimBinaryOperator", + "default link NvimMultiplication NvimBinaryOperator", + "default link NvimDivision NvimBinaryOperator", + "default link NvimMod NvimBinaryOperator", + + "default link NvimTernary NvimOperator", + "default link NvimTernaryColon NvimTernary", + + "default link NvimParenthesis Delimiter", + "default link NvimLambda NvimParenthesis", + "default link NvimNestingParenthesis NvimParenthesis", + "default link NvimCallingParenthesis NvimParenthesis", + + "default link NvimSubscript NvimParenthesis", + "default link NvimSubscriptBracket NvimSubscript", + "default link NvimSubscriptColon NvimSubscript", + "default link NvimCurly NvimSubscript", + + "default link NvimContainer NvimParenthesis", + "default link NvimDict NvimContainer", + "default link NvimList NvimContainer", + + "default link NvimIdentifier Identifier", + "default link NvimIdentifierScope NvimIdentifier", + "default link NvimIdentifierScopeDelimiter NvimIdentifier", + "default link NvimIdentifierName NvimIdentifier", + "default link NvimIdentifierKey NvimIdentifier", + + "default link NvimColon Delimiter", + "default link NvimComma Delimiter", + "default link NvimArrow Delimiter", + + "default link NvimRegister SpecialChar", + "default link NvimNumber Number", + "default link NvimFloat NvimNumber", + "default link NvimNumberPrefix Type", + + "default link NvimOptionSigil Type", + "default link NvimOptionName NvimIdentifier", + "default link NvimOptionScope NvimIdentifierScope", + "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter", + + "default link NvimEnvironmentSigil NvimOptionSigil", + "default link NvimEnvironmentName NvimIdentifier", + + "default link NvimString String", + "default link NvimStringBody NvimString", + "default link NvimStringQuote NvimString", + "default link NvimStringSpecial SpecialChar", + + "default link NvimSingleQuote NvimStringQuote", + "default link NvimSingleQuotedBody NvimStringBody", + "default link NvimSingleQuotedQuote NvimStringSpecial", + + "default link NvimDoubleQuote NvimStringQuote", + "default link NvimDoubleQuotedBody NvimStringBody", + "default link NvimDoubleQuotedEscape NvimStringSpecial", + + "default link NvimFigureBrace NvimInternalError", + "default link NvimSingleQuotedUnknownEscape NvimInternalError", + + "default link NvimSpacing Normal", + + // NvimInvalid groups: + + "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError", + + "default link NvimInvalid Error", + + "default link NvimInvalidAssignment NvimInvalid", + "default link NvimInvalidPlainAssignment NvimInvalidAssignment", + "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment", + "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment", + + "default link NvimInvalidOperator NvimInvalid", + + "default link NvimInvalidUnaryOperator NvimInvalidOperator", + "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator", + "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator", + "default link NvimInvalidNot NvimInvalidUnaryOperator", + + "default link NvimInvalidBinaryOperator NvimInvalidOperator", + "default link NvimInvalidComparison NvimInvalidBinaryOperator", + "default link NvimInvalidComparisonModifier NvimInvalidComparison", + "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator", + "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator", + "default link NvimInvalidConcat NvimInvalidBinaryOperator", + "default link NvimInvalidConcatOrSubscript NvimInvalidConcat", + "default link NvimInvalidOr NvimInvalidBinaryOperator", + "default link NvimInvalidAnd NvimInvalidBinaryOperator", + "default link NvimInvalidMultiplication NvimInvalidBinaryOperator", + "default link NvimInvalidDivision NvimInvalidBinaryOperator", + "default link NvimInvalidMod NvimInvalidBinaryOperator", + + "default link NvimInvalidTernary NvimInvalidOperator", + "default link NvimInvalidTernaryColon NvimInvalidTernary", + + "default link NvimInvalidDelimiter NvimInvalid", + + "default link NvimInvalidParenthesis NvimInvalidDelimiter", + "default link NvimInvalidLambda NvimInvalidParenthesis", + "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis", + "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis", + + "default link NvimInvalidSubscript NvimInvalidParenthesis", + "default link NvimInvalidSubscriptBracket NvimInvalidSubscript", + "default link NvimInvalidSubscriptColon NvimInvalidSubscript", + "default link NvimInvalidCurly NvimInvalidSubscript", + + "default link NvimInvalidContainer NvimInvalidParenthesis", + "default link NvimInvalidDict NvimInvalidContainer", + "default link NvimInvalidList NvimInvalidContainer", + + "default link NvimInvalidValue NvimInvalid", + + "default link NvimInvalidIdentifier NvimInvalidValue", + "default link NvimInvalidIdentifierScope NvimInvalidIdentifier", + "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier", + "default link NvimInvalidIdentifierName NvimInvalidIdentifier", + "default link NvimInvalidIdentifierKey NvimInvalidIdentifier", + + "default link NvimInvalidColon NvimInvalidDelimiter", + "default link NvimInvalidComma NvimInvalidDelimiter", + "default link NvimInvalidArrow NvimInvalidDelimiter", + + "default link NvimInvalidRegister NvimInvalidValue", + "default link NvimInvalidNumber NvimInvalidValue", + "default link NvimInvalidFloat NvimInvalidNumber", + "default link NvimInvalidNumberPrefix NvimInvalidNumber", + + "default link NvimInvalidOptionSigil NvimInvalidIdentifier", + "default link NvimInvalidOptionName NvimInvalidIdentifier", + "default link NvimInvalidOptionScope NvimInvalidIdentifierScope", + "default link NvimInvalidOptionScopeDelimiter NvimInvalidIdentifierScopeDelimiter", + + "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil", + "default link NvimInvalidEnvironmentName NvimInvalidIdentifier", + + // Invalid string bodies and specials are still highlighted as valid ones to + // minimize the red area. + "default link NvimInvalidString NvimInvalidValue", + "default link NvimInvalidStringBody NvimStringBody", + "default link NvimInvalidStringQuote NvimInvalidString", + "default link NvimInvalidStringSpecial NvimStringSpecial", + + "default link NvimInvalidSingleQuote NvimInvalidStringQuote", + "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody", + "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial", + + "default link NvimInvalidDoubleQuote NvimInvalidStringQuote", + "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody", + "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial", + "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue", + + "default link NvimInvalidFigureBrace NvimInvalidDelimiter", + + "default link NvimInvalidSpacing ErrorMsg", + + // Not actually invalid, but we highlight user that he is doing something + // wrong. + "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue", + NULL, +}; + +/// Returns the number of highlight groups. +int highlight_num_groups(void) +{ + return highlight_ga.ga_len; +} + +/// Returns the name of a highlight group. +char_u *highlight_group_name(int id) +{ + return HL_TABLE()[id].sg_name; +} + +/// Returns the ID of the link to a highlight group. +int highlight_link_id(int id) +{ + return HL_TABLE()[id].sg_link; +} + +/// Create default links for Nvim* highlight groups used for cmdline coloring +void syn_init_cmdline_highlight(bool reset, bool init) +{ + for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) { + do_highlight(highlight_init_cmdline[i], reset, init); + } +} + +/// Load colors from a file if "g:colors_name" is set, otherwise load builtin +/// colors +/// +/// @param both include groups where 'bg' doesn't matter +/// @param reset clear groups first +void init_highlight(bool both, bool reset) +{ + static int had_both = false; + + // Try finding the color scheme file. Used when a color file was loaded + // and 'background' or 't_Co' is changed. + char_u *p = get_var_value("g:colors_name"); + if (p != NULL) { + // Value of g:colors_name could be freed in load_colors() and make + // p invalid, so copy it. + char_u *copy_p = vim_strsave(p); + bool okay = load_colors(copy_p); + xfree(copy_p); + if (okay) { + return; + } + } + + // Didn't use a color file, use the compiled-in colors. + if (both) { + had_both = true; + const char *const *const pp = highlight_init_both; + for (size_t i = 0; pp[i] != NULL; i++) { + do_highlight(pp[i], reset, true); + } + } else if (!had_both) { + // Don't do anything before the call with both == true from main(). + // Not everything has been setup then, and that call will overrule + // everything anyway. + return; + } + + const char *const *const pp = ((*p_bg == 'l') + ? highlight_init_light + : highlight_init_dark); + for (size_t i = 0; pp[i] != NULL; i++) { + do_highlight(pp[i], reset, true); + } + + // Reverse looks ugly, but grey may not work for 8 colors. Thus let it + // depend on the number of colors available. + // With 8 colors brown is equal to yellow, need to use black for Search fg + // to avoid Statement highlighted text disappears. + // Clear the attributes, needed when changing the t_Co value. + if (t_colors > 8) { + do_highlight((*p_bg == 'l' + ? "Visual cterm=NONE ctermbg=LightGrey" + : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); + } else { + do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); + if (*p_bg == 'l') { + do_highlight("Search ctermfg=black", false, true); + } + } + + syn_init_cmdline_highlight(false, false); +} + +/// Load color file "name". +/// Return OK for success, FAIL for failure. +int load_colors(char_u *name) +{ + char_u *buf; + int retval = FAIL; + static bool recursive = false; + + // When being called recursively, this is probably because setting + // 'background' caused the highlighting to be reloaded. This means it is + // working, thus we should return OK. + if (recursive) { + return OK; + } + + recursive = true; + size_t buflen = STRLEN(name) + 12; + buf = xmalloc(buflen); + apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); + snprintf((char *)buf, buflen, "colors/%s.vim", name); + retval = source_runtime((char *)buf, DIP_START + DIP_OPT); + if (retval == FAIL) { + snprintf((char *)buf, buflen, "colors/%s.lua", name); + retval = source_runtime((char *)buf, DIP_START + DIP_OPT); + } + xfree(buf); + apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf); + + recursive = false; + + return retval; +} + +static char *(color_names[28]) = { + "Black", "DarkBlue", "DarkGreen", "DarkCyan", + "DarkRed", "DarkMagenta", "Brown", "DarkYellow", + "Gray", "Grey", "LightGray", "LightGrey", + "DarkGray", "DarkGrey", + "Blue", "LightBlue", "Green", "LightGreen", + "Cyan", "LightCyan", "Red", "LightRed", "Magenta", + "LightMagenta", "Yellow", "LightYellow", "White", "NONE" +}; +// indices: +// 0, 1, 2, 3, +// 4, 5, 6, 7, +// 8, 9, 10, 11, +// 12, 13, +// 14, 15, 16, 17, +// 18, 19, 20, 21, 22, +// 23, 24, 25, 26, 27 +static int color_numbers_16[28] = { 0, 1, 2, 3, + 4, 5, 6, 6, + 7, 7, 7, 7, + 8, 8, + 9, 9, 10, 10, + 11, 11, 12, 12, 13, + 13, 14, 14, 15, -1 }; +// for xterm with 88 colors... +static int color_numbers_88[28] = { 0, 4, 2, 6, + 1, 5, 32, 72, + 84, 84, 7, 7, + 82, 82, + 12, 43, 10, 61, + 14, 63, 9, 74, 13, + 75, 11, 78, 15, -1 }; +// for xterm with 256 colors... +static int color_numbers_256[28] = { 0, 4, 2, 6, + 1, 5, 130, 3, + 248, 248, 7, 7, + 242, 242, + 12, 81, 10, 121, + 14, 159, 9, 224, 13, + 225, 11, 229, 15, -1 }; +// for terminals with less than 16 colors... +static int color_numbers_8[28] = { 0, 4, 2, 6, + 1, 5, 3, 3, + 7, 7, 7, 7, + 0+8, 0+8, + 4+8, 4+8, 2+8, 2+8, + 6+8, 6+8, 1+8, 1+8, 5+8, + 5+8, 3+8, 3+8, 7+8, -1 }; + +// Lookup the "cterm" value to be used for color with index "idx" in +// color_names[]. +// "boldp" will be set to TRUE or FALSE for a foreground color when using 8 +// colors, otherwise it will be unchanged. +int lookup_color(const int idx, const bool foreground, TriState *const boldp) +{ + int color = color_numbers_16[idx]; + + // Use the _16 table to check if it's a valid color name. + if (color < 0) { + return -1; + } + + if (t_colors == 8) { + // t_Co is 8: use the 8 colors table + color = color_numbers_8[idx]; + if (foreground) { + // set/reset bold attribute to get light foreground + // colors (on some terminals, e.g. "linux") + if (color & 8) { + *boldp = kTrue; + } else { + *boldp = kFalse; + } + } + color &= 7; // truncate to 8 colors + } else if (t_colors == 16) { + color = color_numbers_8[idx]; + } else if (t_colors == 88) { + color = color_numbers_88[idx]; + } else if (t_colors >= 256) { + color = color_numbers_256[idx]; + } + return color; +} + +void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) +{ + int idx = id - 1; // Index is ID minus one. + + bool is_default = attrs.rgb_ae_attr & HL_DEFAULT; + + // Return if "default" was used and the group already has settings + if (is_default && hl_has_settings(idx, true)) { + return; + } + + HlGroup *g = &HL_TABLE()[idx]; + + if (link_id > 0) { + g->sg_cleared = false; + g->sg_link = link_id; + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + g->sg_set |= SG_LINK; + if (is_default) { + g->sg_deflink = link_id; + g->sg_deflink_sctx = current_sctx; + g->sg_deflink_sctx.sc_lnum += sourcing_lnum; + } + return; + } + + g->sg_cleared = false; + g->sg_link = 0; + g->sg_gui = attrs.rgb_ae_attr; + + g->sg_rgb_fg = attrs.rgb_fg_color; + g->sg_rgb_bg = attrs.rgb_bg_color; + g->sg_rgb_sp = attrs.rgb_sp_color; + + struct { + char **dest; RgbValue val; Object name; + } cattrs[] = { + { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { NULL, -1, NIL }, + }; + + char hex_name[8]; + char *name; + + for (int j = 0; cattrs[j].dest; j++) { + if (cattrs[j].val < 0) { + XFREE_CLEAR(*cattrs[j].dest); + continue; + } + + if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + name = cattrs[j].name.data.string.data; + } else { + snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); + name = hex_name; + } + + if (!*cattrs[j].dest + || STRCMP(*cattrs[j].dest, name) != 0) { + xfree(*cattrs[j].dest); + *cattrs[j].dest = xstrdup(name); + } + } + + g->sg_cterm = attrs.cterm_ae_attr; + g->sg_cterm_bg = attrs.cterm_bg_color; + g->sg_cterm_fg = attrs.cterm_fg_color; + g->sg_cterm_bold = g->sg_cterm & HL_BOLD; + g->sg_blend = attrs.hl_blend; + + g->sg_script_ctx = current_sctx; + g->sg_script_ctx.sc_lnum += sourcing_lnum; + + // 'Normal' is special + if (STRCMP(g->sg_name_u, "NORMAL") == 0) { + cterm_normal_fg_color = g->sg_cterm_fg; + cterm_normal_bg_color = g->sg_cterm_bg; + normal_fg = g->sg_rgb_fg; + normal_bg = g->sg_rgb_bg; + normal_sp = g->sg_rgb_sp; + ui_default_colors_set(); + } else { + g->sg_attr = hl_get_syn_attr(0, id, attrs); + + // a cursor style uses this syn_id, make sure its attribute is updated. + if (cursor_mode_uses_syn_id(id)) { + ui_mode_info_set(); + } + } +} + +/// Handle ":highlight" command +/// +/// When using ":highlight clear" this is called recursively for each group with +/// forceit and init being both true. +/// +/// @param[in] line Command arguments. +/// @param[in] forceit True when bang is given, allows to link group even if +/// it has its own settings. +/// @param[in] init True when initializing. +void do_highlight(const char *line, const bool forceit, const bool init) + FUNC_ATTR_NONNULL_ALL +{ + const char *name_end; + const char *linep; + const char *key_start; + const char *arg_start; + int off; + int len; + int attr; + int id; + int idx; + HlGroup item_before; + bool did_change = false; + bool dodefault = false; + bool doclear = false; + bool dolink = false; + bool error = false; + int color; + bool is_normal_group = false; // "Normal" group + bool did_highlight_changed = false; + + // If no argument, list current highlighting. + if (ends_excmd((uint8_t)(*line))) { + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + // TODO(brammool): only call when the group has attributes set + highlight_list_one(i); + } + return; + } + + // Isolate the name. + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); + + // Check for "default" argument. + if (strncmp(line, "default", (size_t)(name_end - line)) == 0) { + dodefault = true; + line = linep; + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); + } + + // Check for "clear" or "link" argument. + if (strncmp(line, "clear", (size_t)(name_end - line)) == 0) { + doclear = true; + } else if (strncmp(line, "link", (size_t)(name_end - line)) == 0) { + dolink = true; + } + + // ":highlight {group-name}": list highlighting for one group. + if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { + id = syn_name2id_len((const char_u *)line, (size_t)(name_end - line)); + if (id == 0) { + semsg(_("E411: highlight group not found: %s"), line); + } else { + highlight_list_one(id); + } + return; + } + + // Handle ":highlight link {from} {to}" command. + if (dolink) { + const char *from_start = linep; + const char *from_end; + const char *to_start; + const char *to_end; + int from_id; + int to_id; + HlGroup *hlgroup = NULL; + + from_end = (const char *)skiptowhite((const char_u *)from_start); + to_start = (const char *)skipwhite((const char_u *)from_end); + to_end = (const char *)skiptowhite((const char_u *)to_start); + + if (ends_excmd((uint8_t)(*from_start)) + || ends_excmd((uint8_t)(*to_start))) { + semsg(_("E412: Not enough arguments: \":highlight link %s\""), + from_start); + return; + } + + if (!ends_excmd(*skipwhite((const char_u *)to_end))) { + semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start); + return; + } + + from_id = syn_check_group(from_start, (size_t)(from_end - from_start)); + if (strncmp(to_start, "NONE", 4) == 0) { + to_id = 0; + } else { + to_id = syn_check_group(to_start, (size_t)(to_end - to_start)); + } + + if (from_id > 0) { + hlgroup = &HL_TABLE()[from_id - 1]; + if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { + hlgroup->sg_deflink = to_id; + hlgroup->sg_deflink_sctx = current_sctx; + hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_deflink_sctx); + } + } + + if (from_id > 0 && (!init || hlgroup->sg_set == 0)) { + // Don't allow a link when there already is some highlighting + // for the group, unless '!' is used + if (to_id > 0 && !forceit && !init + && hl_has_settings(from_id - 1, dodefault)) { + if (sourcing_name == NULL && !dodefault) { + emsg(_("E414: group has settings, highlight link ignored")); + } + } else if (hlgroup->sg_link != to_id + || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid + || hlgroup->sg_cleared) { + if (!init) { + hlgroup->sg_set |= SG_LINK; + } + hlgroup->sg_link = to_id; + hlgroup->sg_script_ctx = current_sctx; + hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hlgroup->sg_script_ctx); + hlgroup->sg_cleared = false; + redraw_all_later(SOME_VALID); + + // Only call highlight changed() once after multiple changes + need_highlight_changed = true; + } + } + + return; + } + + if (doclear) { + // ":highlight clear [group]" command. + line = linep; + if (ends_excmd((uint8_t)(*line))) { + do_unlet(S_LEN("colors_name"), true); + restore_cterm_colors(); + + // Clear all default highlight groups and load the defaults. + for (int j = 0; j < highlight_ga.ga_len; j++) { + highlight_clear(j); + } + init_highlight(true, true); + highlight_changed(); + redraw_all_later(NOT_VALID); + return; + } + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); + } + + // Find the group name in the table. If it does not exist yet, add it. + id = syn_check_group(line, (size_t)(name_end - line)); + if (id == 0) { // Failed (out of memory). + return; + } + idx = id - 1; // Index is ID minus one. + + // Return if "default" was used and the group already has settings + if (dodefault && hl_has_settings(idx, true)) { + return; + } + + // Make a copy so we can check if any attribute actually changed + item_before = HL_TABLE()[idx]; + is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); + + // Clear the highlighting for ":hi clear {group}" and ":hi clear". + if (doclear || (forceit && init)) { + highlight_clear(idx); + if (!doclear) { + HL_TABLE()[idx].sg_set = 0; + } + } + + char *key = NULL; + char *arg = NULL; + if (!doclear) { + while (!ends_excmd((uint8_t)(*linep))) { + key_start = linep; + if (*linep == '=') { + semsg(_("E415: unexpected equal sign: %s"), key_start); + error = true; + break; + } + + // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", + // "guibg" or "guisp"). + while (*linep && !ascii_iswhite(*linep) && *linep != '=') { + linep++; + } + xfree(key); + key = (char *)vim_strnsave_up((const char_u *)key_start, + (size_t)(linep - key_start)); + linep = (const char *)skipwhite((const char_u *)linep); + + if (strcmp(key, "NONE") == 0) { + if (!init || HL_TABLE()[idx].sg_set == 0) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; + } + highlight_clear(idx); + } + continue; + } + + // Check for the equal sign. + if (*linep != '=') { + semsg(_("E416: missing equal sign: %s"), key_start); + error = true; + break; + } + linep++; + + // Isolate the argument. + linep = (const char *)skipwhite((const char_u *)linep); + if (*linep == '\'') { // guifg='color name' + arg_start = ++linep; + linep = strchr(linep, '\''); + if (linep == NULL) { + semsg(_(e_invarg2), key_start); + error = true; + break; + } + } else { + arg_start = linep; + linep = (const char *)skiptowhite((const char_u *)linep); + } + if (linep == arg_start) { + semsg(_("E417: missing argument: %s"), key_start); + error = true; + break; + } + xfree(arg); + arg = xstrndup(arg_start, (size_t)(linep - arg_start)); + + if (*linep == '\'') { + linep++; + } + + // Store the argument. + if (strcmp(key, "TERM") == 0 + || strcmp(key, "CTERM") == 0 + || strcmp(key, "GUI") == 0) { + attr = 0; + off = 0; + int i; + while (arg[off] != NUL) { + for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) { + len = (int)STRLEN(hl_name_table[i]); + if (STRNICMP(arg + off, hl_name_table[i], len) == 0) { + attr |= hl_attr_table[i]; + off += len; + break; + } + } + if (i < 0) { + semsg(_("E418: Illegal value: %s"), arg); + error = true; + break; + } + if (arg[off] == ',') { // Another one follows. + off++; + } + } + if (error) { + break; + } + if (*key == 'C') { + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_CTERM; + } + HL_TABLE()[idx].sg_cterm = attr; + HL_TABLE()[idx].sg_cterm_bold = false; + } + } else if (*key == 'G') { + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + HL_TABLE()[idx].sg_gui = attr; + } + } + } else if (STRCMP(key, "FONT") == 0) { + // in non-GUI fonts are simply ignored + } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { + if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_CTERM; + } + + // When setting the foreground color, and previously the "bold" + // flag was set for a light color, reset it now + if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = false; + } + + if (ascii_isdigit(*arg)) { + color = atoi(arg); + } else if (STRICMP(arg, "fg") == 0) { + if (cterm_normal_fg_color) { + color = cterm_normal_fg_color - 1; + } else { + emsg(_("E419: FG color unknown")); + error = true; + break; + } + } else if (STRICMP(arg, "bg") == 0) { + if (cterm_normal_bg_color > 0) { + color = cterm_normal_bg_color - 1; + } else { + emsg(_("E420: BG color unknown")); + error = true; + break; + } + } else { + // Reduce calls to STRICMP a bit, it can be slow. + off = TOUPPER_ASC(*arg); + int i; + for (i = ARRAY_SIZE(color_names); --i >= 0;) { + if (off == color_names[i][0] + && STRICMP(arg + 1, color_names[i] + 1) == 0) { + break; + } + } + if (i < 0) { + semsg(_("E421: Color name or number not recognized: %s"), + key_start); + error = true; + break; + } + + TriState bold = kNone; + color = lookup_color(i, key[5] == 'F', &bold); + + // set/reset bold attribute to get light foreground + // colors (on some terminals, e.g. "linux") + if (bold == kTrue) { + HL_TABLE()[idx].sg_cterm |= HL_BOLD; + HL_TABLE()[idx].sg_cterm_bold = true; + } else if (bold == kFalse) { + HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + } + } + // Add one to the argument, to avoid zero. Zero is used for + // "NONE", then "color" is -1. + if (key[5] == 'F') { + HL_TABLE()[idx].sg_cterm_fg = color + 1; + if (is_normal_group) { + cterm_normal_fg_color = color + 1; + } + } else { + HL_TABLE()[idx].sg_cterm_bg = color + 1; + if (is_normal_group) { + cterm_normal_bg_color = color + 1; + if (!ui_rgb_attached()) { + if (color >= 0) { + int dark = -1; + + if (t_colors < 16) { + dark = (color == 0 || color == 4); + } else if (color < 16) { + // Limit the heuristic to the standard 16 colors + dark = (color < 7 || color == 8); + } + // Set the 'background' option if the value is + // wrong. + if (dark != -1 + && dark != (*p_bg == 'd') + && !option_was_set("bg")) { + set_option_value("bg", 0L, (dark ? "dark" : "light"), 0); + reset_option_was_set("bg"); + } + } + } + } + } + } + } else if (strcmp(key, "GUIFG") == 0) { + char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (strcmp(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_fg = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_fg = HL_TABLE()[idx].sg_rgb_fg; + } + } else if (STRCMP(key, "GUIBG") == 0) { + char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (STRCMP(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_bg = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_bg = HL_TABLE()[idx].sg_rgb_bg; + } + } else if (strcmp(key, "GUISP") == 0) { + char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + + if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init) { + HL_TABLE()[idx].sg_set |= SG_GUI; + } + + if (*namep == NULL || STRCMP(*namep, arg) != 0) { + xfree(*namep); + if (strcmp(arg, "NONE") != 0) { + *namep = xstrdup(arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); + } else { + *namep = NULL; + HL_TABLE()[idx].sg_rgb_sp = -1; + } + did_change = true; + } + } + + if (is_normal_group) { + normal_sp = HL_TABLE()[idx].sg_rgb_sp; + } + } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { + // Ignored for now + } else if (strcmp(key, "BLEND") == 0) { + if (strcmp(arg, "NONE") != 0) { + HL_TABLE()[idx].sg_blend = (int)strtol(arg, NULL, 10); + } else { + HL_TABLE()[idx].sg_blend = -1; + } + } else { + semsg(_("E423: Illegal argument: %s"), key_start); + error = true; + break; + } + HL_TABLE()[idx].sg_cleared = false; + + // When highlighting has been given for a group, don't link it. + if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) { + HL_TABLE()[idx].sg_link = 0; + } + + // Continue with next argument. + linep = (const char *)skipwhite((const char_u *)linep); + } + } + + // If there is an error, and it's a new entry, remove it from the table. + if (error && idx == highlight_ga.ga_len) { + syn_unadd_group(); + } else { + if (!error && is_normal_group) { + // Need to update all groups, because they might be using "bg" and/or + // "fg", which have been changed now. + highlight_attr_set_all(); + + if (!ui_has(kUILinegrid) && starting == 0) { + // Older UIs assume that we clear the screen after normal group is + // changed + ui_refresh(); + } else { + // TUI and newer UIs will repaint the screen themselves. NOT_VALID + // redraw below will still handle usages of guibg=fg etc. + ui_default_colors_set(); + } + did_highlight_changed = true; + redraw_all_later(NOT_VALID); + } else { + set_hl_attr(idx); + } + HL_TABLE()[idx].sg_script_ctx = current_sctx; + HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); + } + xfree(key); + xfree(arg); + + // Only call highlight_changed() once, after a sequence of highlight + // commands, and only if an attribute actually changed + if ((did_change + || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) + && !did_highlight_changed) { + // Do not trigger a redraw when highlighting is changed while + // redrawing. This may happen when evaluating 'statusline' changes the + // StatusLine group. + if (!updating_screen) { + redraw_all_later(NOT_VALID); + } + need_highlight_changed = true; + } +} + +#if defined(EXITFREE) +void free_highlight(void) +{ + for (int i = 0; i < highlight_ga.ga_len; i++) { + highlight_clear(i); + xfree(HL_TABLE()[i].sg_name); + xfree(HL_TABLE()[i].sg_name_u); + } + ga_clear(&highlight_ga); + map_destroy(cstr_t, int)(&highlight_unames); +} + +#endif + +/// Reset the cterm colors to what they were before Vim was started, if +/// possible. Otherwise reset them to zero. +void restore_cterm_colors(void) +{ + normal_fg = -1; + normal_bg = -1; + normal_sp = -1; + cterm_normal_fg_color = 0; + cterm_normal_bg_color = 0; +} + +/// @param check_link if true also check for an existing link. +/// +/// @return TRUE if highlight group "idx" has any settings. +static int hl_has_settings(int idx, bool check_link) +{ + return HL_TABLE()[idx].sg_cleared == 0 + && (HL_TABLE()[idx].sg_attr != 0 + || HL_TABLE()[idx].sg_cterm_fg != 0 + || HL_TABLE()[idx].sg_cterm_bg != 0 + || HL_TABLE()[idx].sg_rgb_fg_name != NULL + || HL_TABLE()[idx].sg_rgb_bg_name != NULL + || HL_TABLE()[idx].sg_rgb_sp_name != NULL + || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); +} + +/// Clear highlighting for one group. +static void highlight_clear(int idx) +{ + HL_TABLE()[idx].sg_cleared = true; + + HL_TABLE()[idx].sg_attr = 0; + HL_TABLE()[idx].sg_cterm = 0; + HL_TABLE()[idx].sg_cterm_bold = false; + HL_TABLE()[idx].sg_cterm_fg = 0; + HL_TABLE()[idx].sg_cterm_bg = 0; + HL_TABLE()[idx].sg_gui = 0; + HL_TABLE()[idx].sg_rgb_fg = -1; + HL_TABLE()[idx].sg_rgb_bg = -1; + HL_TABLE()[idx].sg_rgb_sp = -1; + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); + XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); + HL_TABLE()[idx].sg_blend = -1; + // Restore default link and context if they exist. Otherwise clears. + HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; + // Since we set the default link, set the location to where the default + // link was set. + HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; +} + +/// \addtogroup LIST_XXX +/// @{ +#define LIST_ATTR 1 +#define LIST_STRING 2 +#define LIST_INT 3 +/// @} + +static void highlight_list_one(const int id) +{ + const HlGroup *sgp = &HL_TABLE()[id - 1]; // index is ID minus one + bool didh = false; + + if (message_filtered(sgp->sg_name)) { + return; + } + + didh = highlight_list_arg(id, didh, LIST_ATTR, + sgp->sg_cterm, NULL, "cterm"); + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_cterm_fg, NULL, "ctermfg"); + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_cterm_bg, NULL, "ctermbg"); + + didh = highlight_list_arg(id, didh, LIST_ATTR, + sgp->sg_gui, NULL, "gui"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_fg_name, "guifg"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_bg_name, "guibg"); + didh = highlight_list_arg(id, didh, LIST_STRING, + 0, sgp->sg_rgb_sp_name, "guisp"); + + didh = highlight_list_arg(id, didh, LIST_INT, + sgp->sg_blend+1, NULL, "blend"); + + if (sgp->sg_link && !got_int) { + (void)syn_list_header(didh, 0, id, true); + didh = true; + msg_puts_attr("links to", HL_ATTR(HLF_D)); + msg_putchar(' '); + msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + } + + if (!didh) { + highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); + } + if (p_verbose > 0) { + last_set_msg(sgp->sg_script_ctx); + } +} + +Dictionary get_global_hl_defs(void) +{ + Dictionary rv = ARRAY_DICT_INIT; + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + Dictionary attrs = ARRAY_DICT_INIT; + HlGroup *h = &HL_TABLE()[i - 1]; + if (h->sg_attr > 0) { + attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); + } else if (h->sg_link > 0) { + const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); + } + PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); + } + + return rv; +} + +/// Outputs a highlight when doing ":hi MyHighlight" +/// +/// @param type one of \ref LIST_XXX +/// @param iarg integer argument used if \p type == LIST_INT +/// @param sarg string used if \p type == LIST_STRING +static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, + const char *const name) +{ + char buf[100]; + + if (got_int) { + return false; + } + if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { + char *ts = buf; + if (type == LIST_INT) { + snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); + } else if (type == LIST_STRING) { + ts = sarg; + } else { // type == LIST_ATTR + buf[0] = NUL; + for (int i = 0; hl_attr_table[i] != 0; i++) { + if (iarg & hl_attr_table[i]) { + if (buf[0] != NUL) { + xstrlcat(buf, ",", 100); + } + xstrlcat(buf, hl_name_table[i], 100); + iarg &= ~hl_attr_table[i]; // don't want "inverse" + } + } + } + + (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + (int)STRLEN(name) + + 1), id, false); + didh = true; + if (!got_int) { + if (*name != NUL) { + msg_puts_attr(name, HL_ATTR(HLF_D)); + msg_puts_attr("=", HL_ATTR(HLF_D)); + } + msg_outtrans((char_u *)ts); + } + } + return didh; +} + +/// Check whether highlight group has attribute +/// +/// @param[in] id Highlight group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + int attr; + + if (id <= 0 || id > highlight_ga.ga_len) { + return NULL; + } + + if (modec == 'g') { + attr = HL_TABLE()[id - 1].sg_gui; + } else { + attr = HL_TABLE()[id - 1].sg_cterm; + } + + return (attr & flag) ? "1" : NULL; +} + +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static char name[20]; + int n; + bool fg = false; + bool sp = false; + bool font = false; + + if (id <= 0 || id > highlight_ga.ga_len) { + return NULL; + } + + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { + return NULL; + } + if (modec == 'g') { + if (what[2] == '#' && ui_rgb_attached()) { + if (fg) { + n = HL_TABLE()[id - 1].sg_rgb_fg; + } else if (sp) { + n = HL_TABLE()[id - 1].sg_rgb_sp; + } else { + n = HL_TABLE()[id - 1].sg_rgb_bg; + } + if (n < 0 || n > 0xffffff) { + return NULL; + } + snprintf(name, sizeof(name), "#%06x", n); + return name; + } + if (fg) { + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; + } + if (sp) { + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; + } + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; + } + if (font || sp) { + return NULL; + } + if (modec == 'c') { + if (fg) { + n = HL_TABLE()[id - 1].sg_cterm_fg - 1; + } else { + n = HL_TABLE()[id - 1].sg_cterm_bg - 1; + } + if (n < 0) { + return NULL; + } + snprintf(name, sizeof(name), "%d", n); + return name; + } + // term doesn't have color. + return NULL; +} + +/// Output the syntax list header. +/// +/// @param did_header did header already +/// @param outlen length of string that comes +/// @param id highlight group id +/// @param force_newline always start a new line +/// @return true when started a new line. +bool syn_list_header(const bool did_header, const int outlen, const int id, + bool force_newline) +{ + int endcol = 19; + bool newline = true; + int name_col = 0; + bool adjust = true; + + if (!did_header) { + msg_putchar('\n'); + if (got_int) { + return true; + } + msg_outtrans(HL_TABLE()[id - 1].sg_name); + name_col = msg_col; + endcol = 15; + } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { + msg_putchar(' '); + adjust = false; + } else if (msg_col + outlen + 1 >= Columns || force_newline) { + msg_putchar('\n'); + if (got_int) { + return true; + } + } else { + if (msg_col >= endcol) { // wrap around is like starting a new line + newline = false; + } + } + + if (adjust) { + if (msg_col >= endcol) { + // output at least one space + endcol = msg_col + 1; + } + + msg_advance(endcol); + } + + // Show "xxx" with the attributes. + if (!did_header) { + if (endcol == Columns - 1 && endcol <= name_col) { + msg_putchar(' '); + } + msg_puts_attr("xxx", syn_id2attr(id)); + msg_putchar(' '); + } + + return newline; +} + +/// Set the attribute numbers for a highlight group. +/// Called after one of the attributes has changed. +/// @param idx corrected highlight index +static void set_hl_attr(int idx) +{ + HlAttrs at_en = HLATTRS_INIT; + HlGroup *sgp = HL_TABLE() + idx; + + at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm; + at_en.cterm_fg_color = sgp->sg_cterm_fg; + at_en.cterm_bg_color = sgp->sg_cterm_bg; + at_en.rgb_ae_attr = (int16_t)sgp->sg_gui; + // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is + // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // before setting attr_entry->{f,g}g_color to a other than -1 + at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; + at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; + at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; + at_en.hl_blend = sgp->sg_blend; + + sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); + + // a cursor style uses this syn_id, make sure its attribute is updated. + if (cursor_mode_uses_syn_id(idx+1)) { + ui_mode_info_set(); + } +} + +int syn_name2id(const char *name) + FUNC_ATTR_NONNULL_ALL +{ + return syn_name2id_len((char_u *)name, STRLEN(name)); +} + +/// Lookup a highlight group name and return its ID. +/// +/// @param highlight name e.g. 'Cursor', 'Normal' +/// @return the highlight id, else 0 if \p name does not exist +int syn_name2id_len(const char_u *name, size_t len) + FUNC_ATTR_NONNULL_ALL +{ + char name_u[MAX_SYN_NAME + 1]; + + if (len == 0 || len > MAX_SYN_NAME) { + return 0; + } + + // Avoid using stricmp() too much, it's slow on some systems */ + // Avoid alloc()/free(), these are slow too. + memcpy(name_u, name, len); + name_u[len] = '\0'; + vim_strup((char_u *)name_u); + + // map_get(..., int) returns 0 when no key is present, which is + // the expected value for missing highlight group. + return map_get(cstr_t, int)(&highlight_unames, name_u); +} + +/// Lookup a highlight group name and return its attributes. +/// Return zero if not found. +int syn_name2attr(const char_u *name) + FUNC_ATTR_NONNULL_ALL +{ + int id = syn_name2id((char *)name); + + if (id != 0) { + return syn_id2attr(id); + } + return 0; +} + +/// Return TRUE if highlight group "name" exists. +int highlight_exists(const char *name) +{ + return syn_name2id(name) > 0; +} + +/// Return the name of highlight group "id". +/// When not a valid ID return an empty string. +char_u *syn_id2name(int id) +{ + if (id <= 0 || id > highlight_ga.ga_len) { + return (char_u *)""; + } + return HL_TABLE()[id - 1].sg_name; +} + +/// Find highlight group name in the table and return its ID. +/// If it doesn't exist yet, a new entry is created. +/// +/// @param pp Highlight group name +/// @param len length of \p pp +/// +/// @return 0 for failure else the id of the group +int syn_check_group(const char *name, size_t len) +{ + if (len > MAX_SYN_NAME) { + emsg(_(e_highlight_group_name_too_long)); + return 0; + } + int id = syn_name2id_len((char_u *)name, len); + if (id == 0) { // doesn't exist yet + return syn_add_group(vim_strnsave((char_u *)name, len)); + } + return id; +} + +/// Add new highlight group and return its ID. +/// +/// @param name must be an allocated string, it will be consumed. +/// @return 0 for failure, else the allocated group id +/// @see syn_check_group syn_unadd_group +static int syn_add_group(char_u *name) +{ + char_u *p; + + // Check that the name is ASCII letters, digits and underscore. + for (p = name; *p != NUL; p++) { + if (!vim_isprintc(*p)) { + emsg(_("E669: Unprintable character in group name")); + xfree(name); + return 0; + } else if (!ASCII_ISALNUM(*p) && *p != '_') { + // This is an error, but since there previously was no check only give a warning. + msg_source(HL_ATTR(HLF_W)); + msg(_("W18: Invalid character in group name")); + break; + } + } + + // First call for this growarray: init growing array. + if (highlight_ga.ga_data == NULL) { + highlight_ga.ga_itemsize = sizeof(HlGroup); + ga_set_growsize(&highlight_ga, 10); + } + + if (highlight_ga.ga_len >= MAX_HL_ID) { + emsg(_("E849: Too many highlight and syntax groups")); + xfree(name); + return 0; + } + + char *const name_up = (char *)vim_strsave_up(name); + + // Append another syntax_highlight entry. + HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga); + memset(hlgp, 0, sizeof(*hlgp)); + hlgp->sg_name = name; + hlgp->sg_rgb_bg = -1; + hlgp->sg_rgb_fg = -1; + hlgp->sg_rgb_sp = -1; + hlgp->sg_blend = -1; + hlgp->sg_name_u = name_up; + + int id = highlight_ga.ga_len; // ID is index plus one + + map_put(cstr_t, int)(&highlight_unames, name_up, id); + + return id; +} + +/// When, just after calling syn_add_group(), an error is discovered, this +/// function deletes the new name. +static void syn_unadd_group(void) +{ + highlight_ga.ga_len--; + HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; + map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); + xfree(item->sg_name); + xfree(item->sg_name_u); +} + +/// Translate a group ID to highlight attributes. +/// @see syn_attr2entry +int syn_id2attr(int hl_id) +{ + hl_id = syn_get_final_id(hl_id); + HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + + int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); + if (attr >= 0) { + return attr; + } + return sgp->sg_attr; +} + +/// Translate a group ID to the final group ID (following links). +int syn_get_final_id(int hl_id) +{ + int count; + + if (hl_id > highlight_ga.ga_len || hl_id < 1) { + return 0; // Can be called from eval!! + } + + // Follow links until there is no more. + // Look out for loops! Break after 100 links. + for (count = 100; --count >= 0;) { + HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + + // ACHTUNG: when using "tmp" attribute (no link) the function might be + // called twice. it needs be smart enough to remember attr only to + // syn_id2attr time + int check = ns_get_hl(-1, hl_id, true, sgp->sg_set); + if (check == 0) { + return hl_id; // how dare! it broke the link! + } else if (check > 0) { + hl_id = check; + continue; + } + + + if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { + break; + } + hl_id = sgp->sg_link; + } + + return hl_id; +} + +/// Refresh the color attributes of all highlight groups. +void highlight_attr_set_all(void) +{ + for (int idx = 0; idx < highlight_ga.ga_len; idx++) { + HlGroup *sgp = &HL_TABLE()[idx]; + if (sgp->sg_rgb_bg_name != NULL) { + sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); + } + if (sgp->sg_rgb_fg_name != NULL) { + sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); + } + if (sgp->sg_rgb_sp_name != NULL) { + sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); + } + set_hl_attr(idx); + } +} + +// Apply difference between User[1-9] and HLF_S to HLF_SNC. +static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) + FUNC_ATTR_NONNULL_ALL +{ + HlGroup *const hlt = HL_TABLE(); + + if (id_alt == 0) { + memset(&hlt[hlcnt + i], 0, sizeof(HlGroup)); + hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; + hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; + } else { + memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(HlGroup)); + } + hlt[hlcnt + i].sg_link = 0; + + hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; + if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) { + hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; + } + if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) { + hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; + } + hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; + if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) { + hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg; + } + if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) { + hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; + } + if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { + hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; + } + highlight_ga.ga_len = hlcnt + i + 1; + set_hl_attr(hlcnt + i); // At long last we can apply + table[i] = syn_id2attr(hlcnt + i + 1); +} + +/// Translate highlight groups into attributes in highlight_attr[] and set up +/// the user highlights User1..9. A set of corresponding highlights to use on +/// top of HLF_SNC is computed. Called only when nvim starts and upon first +/// screen redraw after any :highlight command. +void highlight_changed(void) +{ + int id; + char userhl[30]; // use 30 to avoid compiler warning + int id_S = -1; + int id_SNC = 0; + int hlcnt; + + need_highlight_changed = false; + + /// Translate builtin highlight groups into attributes for quick lookup. + for (int hlf = 0; hlf < HLF_COUNT; hlf++) { + id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf])); + if (id == 0) { + abort(); + } + int final_id = syn_get_final_id(id); + if (hlf == HLF_SNC) { + id_SNC = final_id; + } else if (hlf == HLF_S) { + id_S = final_id; + } + + highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, + hlf == HLF_INACTIVE); + + if (highlight_attr[hlf] != highlight_attr_last[hlf]) { + if (hlf == HLF_MSG) { + clear_cmdline = true; + } + ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), + highlight_attr[hlf]); + highlight_attr_last[hlf] = highlight_attr[hlf]; + } + } + + // + // Setup the user highlights + // + // Temporarily utilize 10 more hl entries: + // 9 for User1-User9 combined with StatusLineNC + // 1 for StatusLine default + // Must to be in there simultaneously in case of table overflows in + // get_attr_entry() + ga_grow(&highlight_ga, 10); + hlcnt = highlight_ga.ga_len; + if (id_S == -1) { + // Make sure id_S is always valid to simplify code below. Use the last entry + memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(HlGroup)); + id_S = hlcnt + 10; + } + for (int i = 0; i < 9; i++) { + snprintf(userhl, sizeof(userhl), "User%d", i + 1); + id = syn_name2id(userhl); + if (id == 0) { + highlight_user[i] = 0; + highlight_stlnc[i] = 0; + } else { + highlight_user[i] = syn_id2attr(id); + combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc); + } + } + highlight_ga.ga_len = hlcnt; +} + +/// Handle command line completion for :highlight command. +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) +{ + // Default: expand group names. + xp->xp_context = EXPAND_HIGHLIGHT; + xp->xp_pattern = (char_u *)arg; + include_link = 2; + include_default = 1; + + // (part of) subcommand already typed + if (*arg != NUL) { + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. + include_default = 0; + if (strncmp("default", arg, (unsigned)(p - arg)) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); + } + if (*p != NUL) { // past group name + include_link = 0; + if (arg[1] == 'i' && arg[0] == 'N') { + highlight_list(); + } + if (strncmp("link", arg, (unsigned)(p - arg)) == 0 + || strncmp("clear", arg, (unsigned)(p - arg)) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + } + } + if (*p != NUL) { // Past group name(s). + xp->xp_context = EXPAND_NOTHING; + } + } + } + } +} + +/// List highlighting matches in a nice way. +static void highlight_list(void) +{ + int i; + + for (i = 10; --i >= 0;) { + highlight_list_two(i, HL_ATTR(HLF_D)); + } + for (i = 40; --i >= 0;) { + highlight_list_two(99, 0); + } +} + +static void highlight_list_two(int cnt, int attr) +{ + msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr); + msg_clr_eos(); + ui_flush(); + os_delay(cnt == 99 ? 40L : (uint64_t)cnt * 50L, false); +} + +/// Function given to ExpandGeneric() to obtain the list of group names. +const char *get_highlight_name(expand_T *const xp, int idx) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + return get_highlight_name_ext(xp, idx, true); +} + +/// Obtain a highlight group name. +/// +/// @param skip_cleared if true don't return a cleared entry. +const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (idx < 0) { + return NULL; + } + + // Items are never removed from the table, skip the ones that were cleared. + if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { + return ""; + } + + if (idx == highlight_ga.ga_len && include_none != 0) { + return "none"; + } else if (idx == highlight_ga.ga_len + include_none + && include_default != 0) { + return "default"; + } else if (idx == highlight_ga.ga_len + include_none + include_default + && include_link != 0) { + return "link"; + } else if (idx == highlight_ga.ga_len + include_none + include_default + 1 + && include_link != 0) { + return "clear"; + } else if (idx >= highlight_ga.ga_len) { + return NULL; + } + return (const char *)HL_TABLE()[idx].sg_name; +} + +color_name_table_T color_name_table[] = { + // Colors from rgb.txt + { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) }, + { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) }, + { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) }, + { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) }, + { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) }, + { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) }, + { "Aqua", RGB_(0x00, 0xff, 0xff) }, + { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) }, + { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) }, + { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) }, + { "Azure", RGB_(0xf0, 0xff, 0xff) }, + { "Azure1", RGB_(0xf0, 0xff, 0xff) }, + { "Azure2", RGB_(0xe0, 0xee, 0xee) }, + { "Azure3", RGB_(0xc1, 0xcd, 0xcd) }, + { "Azure4", RGB_(0x83, 0x8b, 0x8b) }, + { "Beige", RGB_(0xf5, 0xf5, 0xdc) }, + { "Bisque", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque1", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque2", RGB_(0xee, 0xd5, 0xb7) }, + { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) }, + { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) }, + { "Black", RGB_(0x00, 0x00, 0x00) }, + { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) }, + { "Blue", RGB_(0x00, 0x00, 0xff) }, + { "Blue1", RGB_(0x0, 0x0, 0xff) }, + { "Blue2", RGB_(0x0, 0x0, 0xee) }, + { "Blue3", RGB_(0x0, 0x0, 0xcd) }, + { "Blue4", RGB_(0x0, 0x0, 0x8b) }, + { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) }, + { "Brown", RGB_(0xa5, 0x2a, 0x2a) }, + { "Brown1", RGB_(0xff, 0x40, 0x40) }, + { "Brown2", RGB_(0xee, 0x3b, 0x3b) }, + { "Brown3", RGB_(0xcd, 0x33, 0x33) }, + { "Brown4", RGB_(0x8b, 0x23, 0x23) }, + { "BurlyWood", RGB_(0xde, 0xb8, 0x87) }, + { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) }, + { "Burlywood2", RGB_(0xee, 0xc5, 0x91) }, + { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) }, + { "Burlywood4", RGB_(0x8b, 0x73, 0x55) }, + { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) }, + { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) }, + { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) }, + { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) }, + { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) }, + { "ChartReuse", RGB_(0x7f, 0xff, 0x00) }, + { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) }, + { "Chartreuse2", RGB_(0x76, 0xee, 0x0) }, + { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) }, + { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) }, + { "Chocolate", RGB_(0xd2, 0x69, 0x1e) }, + { "Chocolate1", RGB_(0xff, 0x7f, 0x24) }, + { "Chocolate2", RGB_(0xee, 0x76, 0x21) }, + { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) }, + { "Chocolate4", RGB_(0x8b, 0x45, 0x13) }, + { "Coral", RGB_(0xff, 0x7f, 0x50) }, + { "Coral1", RGB_(0xff, 0x72, 0x56) }, + { "Coral2", RGB_(0xee, 0x6a, 0x50) }, + { "Coral3", RGB_(0xcd, 0x5b, 0x45) }, + { "Coral4", RGB_(0x8b, 0x3e, 0x2f) }, + { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) }, + { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) }, + { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) }, + { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) }, + { "Crimson", RGB_(0xdc, 0x14, 0x3c) }, + { "Cyan", RGB_(0x00, 0xff, 0xff) }, + { "Cyan1", RGB_(0x0, 0xff, 0xff) }, + { "Cyan2", RGB_(0x0, 0xee, 0xee) }, + { "Cyan3", RGB_(0x0, 0xcd, 0xcd) }, + { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, + { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, + { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, + { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, + { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, + { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, + { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, + { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) }, + { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkGreen", RGB_(0x00, 0x64, 0x00) }, + { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) }, + { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) }, + { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) }, + { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) }, + { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) }, + { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) }, + { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) }, + { "DarkOrange", RGB_(0xff, 0x8c, 0x00) }, + { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) }, + { "DarkOrange2", RGB_(0xee, 0x76, 0x0) }, + { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) }, + { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) }, + { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) }, + { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) }, + { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) }, + { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) }, + { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) }, + { "DarkRed", RGB_(0x8b, 0x00, 0x00) }, + { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) }, + { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) }, + { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) }, + { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) }, + { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) }, + { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) }, + { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) }, + { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) }, + { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) }, + { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) }, + { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) }, + { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) }, + { "DarkViolet", RGB_(0x94, 0x00, 0xd3) }, + { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) }, + { "DeepPink", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink1", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink2", RGB_(0xee, 0x12, 0x89) }, + { "DeepPink3", RGB_(0xcd, 0x10, 0x76) }, + { "DeepPink4", RGB_(0x8b, 0xa, 0x50) }, + { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) }, + { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) }, + { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) }, + { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) }, + { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) }, + { "DimGray", RGB_(0x69, 0x69, 0x69) }, + { "DimGrey", RGB_(0x69, 0x69, 0x69) }, + { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) }, + { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) }, + { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) }, + { "Firebrick", RGB_(0xb2, 0x22, 0x22) }, + { "Firebrick1", RGB_(0xff, 0x30, 0x30) }, + { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) }, + { "Firebrick3", RGB_(0xcd, 0x26, 0x26) }, + { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) }, + { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) }, + { "ForestGreen", RGB_(0x22, 0x8b, 0x22) }, + { "Fuchsia", RGB_(0xff, 0x00, 0xff) }, + { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) }, + { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) }, + { "Gold", RGB_(0xff, 0xd7, 0x00) }, + { "Gold1", RGB_(0xff, 0xd7, 0x0) }, + { "Gold2", RGB_(0xee, 0xc9, 0x0) }, + { "Gold3", RGB_(0xcd, 0xad, 0x0) }, + { "Gold4", RGB_(0x8b, 0x75, 0x0) }, + { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, + { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, + { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, + { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, + { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) }, + { "Gray", RGB_(0x80, 0x80, 0x80) }, + { "Gray0", RGB_(0x0, 0x0, 0x0) }, + { "Gray1", RGB_(0x3, 0x3, 0x3) }, + { "Gray10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Gray100", RGB_(0xff, 0xff, 0xff) }, + { "Gray11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Gray12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Gray13", RGB_(0x21, 0x21, 0x21) }, + { "Gray14", RGB_(0x24, 0x24, 0x24) }, + { "Gray15", RGB_(0x26, 0x26, 0x26) }, + { "Gray16", RGB_(0x29, 0x29, 0x29) }, + { "Gray17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Gray18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Gray19", RGB_(0x30, 0x30, 0x30) }, + { "Gray2", RGB_(0x5, 0x5, 0x5) }, + { "Gray20", RGB_(0x33, 0x33, 0x33) }, + { "Gray21", RGB_(0x36, 0x36, 0x36) }, + { "Gray22", RGB_(0x38, 0x38, 0x38) }, + { "Gray23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Gray24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Gray25", RGB_(0x40, 0x40, 0x40) }, + { "Gray26", RGB_(0x42, 0x42, 0x42) }, + { "Gray27", RGB_(0x45, 0x45, 0x45) }, + { "Gray28", RGB_(0x47, 0x47, 0x47) }, + { "Gray29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Gray3", RGB_(0x8, 0x8, 0x8) }, + { "Gray30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Gray31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Gray32", RGB_(0x52, 0x52, 0x52) }, + { "Gray33", RGB_(0x54, 0x54, 0x54) }, + { "Gray34", RGB_(0x57, 0x57, 0x57) }, + { "Gray35", RGB_(0x59, 0x59, 0x59) }, + { "Gray36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Gray37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Gray38", RGB_(0x61, 0x61, 0x61) }, + { "Gray39", RGB_(0x63, 0x63, 0x63) }, + { "Gray4", RGB_(0xa, 0xa, 0xa) }, + { "Gray40", RGB_(0x66, 0x66, 0x66) }, + { "Gray41", RGB_(0x69, 0x69, 0x69) }, + { "Gray42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Gray43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Gray44", RGB_(0x70, 0x70, 0x70) }, + { "Gray45", RGB_(0x73, 0x73, 0x73) }, + { "Gray46", RGB_(0x75, 0x75, 0x75) }, + { "Gray47", RGB_(0x78, 0x78, 0x78) }, + { "Gray48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Gray49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Gray5", RGB_(0xd, 0xd, 0xd) }, + { "Gray50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Gray51", RGB_(0x82, 0x82, 0x82) }, + { "Gray52", RGB_(0x85, 0x85, 0x85) }, + { "Gray53", RGB_(0x87, 0x87, 0x87) }, + { "Gray54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Gray55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Gray56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Gray57", RGB_(0x91, 0x91, 0x91) }, + { "Gray58", RGB_(0x94, 0x94, 0x94) }, + { "Gray59", RGB_(0x96, 0x96, 0x96) }, + { "Gray6", RGB_(0xf, 0xf, 0xf) }, + { "Gray60", RGB_(0x99, 0x99, 0x99) }, + { "Gray61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Gray62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Gray63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Gray64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Gray65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Gray66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Gray67", RGB_(0xab, 0xab, 0xab) }, + { "Gray68", RGB_(0xad, 0xad, 0xad) }, + { "Gray69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Gray7", RGB_(0x12, 0x12, 0x12) }, + { "Gray70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Gray71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Gray72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Gray73", RGB_(0xba, 0xba, 0xba) }, + { "Gray74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Gray75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Gray76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Gray77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Gray78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Gray79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Gray8", RGB_(0x14, 0x14, 0x14) }, + { "Gray80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Gray81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Gray82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Gray83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Gray84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Gray85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Gray86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Gray87", RGB_(0xde, 0xde, 0xde) }, + { "Gray88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Gray89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Gray9", RGB_(0x17, 0x17, 0x17) }, + { "Gray90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Gray91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Gray92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Gray93", RGB_(0xed, 0xed, 0xed) }, + { "Gray94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Gray95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Gray96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Gray97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Gray98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Gray99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Green", RGB_(0x00, 0x80, 0x00) }, + { "Green1", RGB_(0x0, 0xff, 0x0) }, + { "Green2", RGB_(0x0, 0xee, 0x0) }, + { "Green3", RGB_(0x0, 0xcd, 0x0) }, + { "Green4", RGB_(0x0, 0x8b, 0x0) }, + { "GreenYellow", RGB_(0xad, 0xff, 0x2f) }, + { "Grey", RGB_(0x80, 0x80, 0x80) }, + { "Grey0", RGB_(0x0, 0x0, 0x0) }, + { "Grey1", RGB_(0x3, 0x3, 0x3) }, + { "Grey10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Grey100", RGB_(0xff, 0xff, 0xff) }, + { "Grey11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Grey12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Grey13", RGB_(0x21, 0x21, 0x21) }, + { "Grey14", RGB_(0x24, 0x24, 0x24) }, + { "Grey15", RGB_(0x26, 0x26, 0x26) }, + { "Grey16", RGB_(0x29, 0x29, 0x29) }, + { "Grey17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Grey18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Grey19", RGB_(0x30, 0x30, 0x30) }, + { "Grey2", RGB_(0x5, 0x5, 0x5) }, + { "Grey20", RGB_(0x33, 0x33, 0x33) }, + { "Grey21", RGB_(0x36, 0x36, 0x36) }, + { "Grey22", RGB_(0x38, 0x38, 0x38) }, + { "Grey23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Grey24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Grey25", RGB_(0x40, 0x40, 0x40) }, + { "Grey26", RGB_(0x42, 0x42, 0x42) }, + { "Grey27", RGB_(0x45, 0x45, 0x45) }, + { "Grey28", RGB_(0x47, 0x47, 0x47) }, + { "Grey29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Grey3", RGB_(0x8, 0x8, 0x8) }, + { "Grey30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Grey31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Grey32", RGB_(0x52, 0x52, 0x52) }, + { "Grey33", RGB_(0x54, 0x54, 0x54) }, + { "Grey34", RGB_(0x57, 0x57, 0x57) }, + { "Grey35", RGB_(0x59, 0x59, 0x59) }, + { "Grey36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Grey37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Grey38", RGB_(0x61, 0x61, 0x61) }, + { "Grey39", RGB_(0x63, 0x63, 0x63) }, + { "Grey4", RGB_(0xa, 0xa, 0xa) }, + { "Grey40", RGB_(0x66, 0x66, 0x66) }, + { "Grey41", RGB_(0x69, 0x69, 0x69) }, + { "Grey42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Grey43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Grey44", RGB_(0x70, 0x70, 0x70) }, + { "Grey45", RGB_(0x73, 0x73, 0x73) }, + { "Grey46", RGB_(0x75, 0x75, 0x75) }, + { "Grey47", RGB_(0x78, 0x78, 0x78) }, + { "Grey48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Grey49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Grey5", RGB_(0xd, 0xd, 0xd) }, + { "Grey50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Grey51", RGB_(0x82, 0x82, 0x82) }, + { "Grey52", RGB_(0x85, 0x85, 0x85) }, + { "Grey53", RGB_(0x87, 0x87, 0x87) }, + { "Grey54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Grey55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Grey56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Grey57", RGB_(0x91, 0x91, 0x91) }, + { "Grey58", RGB_(0x94, 0x94, 0x94) }, + { "Grey59", RGB_(0x96, 0x96, 0x96) }, + { "Grey6", RGB_(0xf, 0xf, 0xf) }, + { "Grey60", RGB_(0x99, 0x99, 0x99) }, + { "Grey61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Grey62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Grey63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Grey64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Grey65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Grey66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Grey67", RGB_(0xab, 0xab, 0xab) }, + { "Grey68", RGB_(0xad, 0xad, 0xad) }, + { "Grey69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Grey7", RGB_(0x12, 0x12, 0x12) }, + { "Grey70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Grey71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Grey72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Grey73", RGB_(0xba, 0xba, 0xba) }, + { "Grey74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Grey75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Grey76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Grey77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Grey78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Grey79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Grey8", RGB_(0x14, 0x14, 0x14) }, + { "Grey80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Grey81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Grey82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Grey83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Grey84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Grey85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Grey86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Grey87", RGB_(0xde, 0xde, 0xde) }, + { "Grey88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Grey89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Grey9", RGB_(0x17, 0x17, 0x17) }, + { "Grey90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Grey91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Grey92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Grey93", RGB_(0xed, 0xed, 0xed) }, + { "Grey94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Grey95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Grey96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Grey97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Grey98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Grey99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Honeydew", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) }, + { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) }, + { "Honeydew4", RGB_(0x83, 0x8b, 0x83) }, + { "HotPink", RGB_(0xff, 0x69, 0xb4) }, + { "HotPink1", RGB_(0xff, 0x6e, 0xb4) }, + { "HotPink2", RGB_(0xee, 0x6a, 0xa7) }, + { "HotPink3", RGB_(0xcd, 0x60, 0x90) }, + { "HotPink4", RGB_(0x8b, 0x3a, 0x62) }, + { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) }, + { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) }, + { "IndianRed2", RGB_(0xee, 0x63, 0x63) }, + { "IndianRed3", RGB_(0xcd, 0x55, 0x55) }, + { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) }, + { "Indigo", RGB_(0x4b, 0x00, 0x82) }, + { "Ivory", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory1", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory2", RGB_(0xee, 0xee, 0xe0) }, + { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) }, + { "Ivory4", RGB_(0x8b, 0x8b, 0x83) }, + { "Khaki", RGB_(0xf0, 0xe6, 0x8c) }, + { "Khaki1", RGB_(0xff, 0xf6, 0x8f) }, + { "Khaki2", RGB_(0xee, 0xe6, 0x85) }, + { "Khaki3", RGB_(0xcd, 0xc6, 0x73) }, + { "Khaki4", RGB_(0x8b, 0x86, 0x4e) }, + { "Lavender", RGB_(0xe6, 0xe6, 0xfa) }, + { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) }, + { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) }, + { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) }, + { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) }, + { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) }, + { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) }, + { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) }, + { "LightBlue", RGB_(0xad, 0xd8, 0xe6) }, + { "LightBlue1", RGB_(0xbf, 0xef, 0xff) }, + { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) }, + { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) }, + { "LightBlue4", RGB_(0x68, 0x83, 0x8b) }, + { "LightCoral", RGB_(0xf0, 0x80, 0x80) }, + { "LightCyan", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan1", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan2", RGB_(0xd1, 0xee, 0xee) }, + { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) }, + { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) }, + { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) }, + { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) }, + { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, + { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, + { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, + { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, + { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightGreen", RGB_(0x90, 0xee, 0x90) }, + { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightMagenta", RGB_(0xff, 0xbb, 0xff) }, + { "LightPink", RGB_(0xff, 0xb6, 0xc1) }, + { "LightPink1", RGB_(0xff, 0xae, 0xb9) }, + { "LightPink2", RGB_(0xee, 0xa2, 0xad) }, + { "LightPink3", RGB_(0xcd, 0x8c, 0x95) }, + { "LightPink4", RGB_(0x8b, 0x5f, 0x65) }, + { "LightRed", RGB_(0xff, 0xbb, 0xbb) }, + { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon2", RGB_(0xee, 0x95, 0x72) }, + { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) }, + { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) }, + { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) }, + { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) }, + { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) }, + { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) }, + { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) }, + { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) }, + { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) }, + { "LightSlateGray", RGB_(0x77, 0x88, 0x99) }, + { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) }, + { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) }, + { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) }, + { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) }, + { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) }, + { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) }, + { "LightYellow", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow1", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow2", RGB_(0xee, 0xee, 0xd1) }, + { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) }, + { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) }, + { "Lime", RGB_(0x00, 0xff, 0x00) }, + { "LimeGreen", RGB_(0x32, 0xcd, 0x32) }, + { "Linen", RGB_(0xfa, 0xf0, 0xe6) }, + { "Magenta", RGB_(0xff, 0x00, 0xff) }, + { "Magenta1", RGB_(0xff, 0x0, 0xff) }, + { "Magenta2", RGB_(0xee, 0x0, 0xee) }, + { "Magenta3", RGB_(0xcd, 0x0, 0xcd) }, + { "Magenta4", RGB_(0x8b, 0x0, 0x8b) }, + { "Maroon", RGB_(0x80, 0x00, 0x00) }, + { "Maroon1", RGB_(0xff, 0x34, 0xb3) }, + { "Maroon2", RGB_(0xee, 0x30, 0xa7) }, + { "Maroon3", RGB_(0xcd, 0x29, 0x90) }, + { "Maroon4", RGB_(0x8b, 0x1c, 0x62) }, + { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) }, + { "MediumBlue", RGB_(0x00, 0x00, 0xcd) }, + { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) }, + { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) }, + { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) }, + { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) }, + { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) }, + { "MediumPurple", RGB_(0x93, 0x70, 0xdb) }, + { "MediumPurple1", RGB_(0xab, 0x82, 0xff) }, + { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) }, + { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) }, + { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) }, + { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) }, + { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) }, + { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) }, + { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) }, + { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) }, + { "MidnightBlue", RGB_(0x19, 0x19, 0x70) }, + { "MintCream", RGB_(0xf5, 0xff, 0xfa) }, + { "MistyRose", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) }, + { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) }, + { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) }, + { "Moccasin", RGB_(0xff, 0xe4, 0xb5) }, + { "NavajoWhite", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) }, + { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) }, + { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, + { "Navy", RGB_(0x00, 0x00, 0x80) }, + { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, + { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, + { "Olive", RGB_(0x80, 0x80, 0x00) }, + { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, + { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) }, + { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) }, + { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) }, + { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) }, + { "Orange", RGB_(0xff, 0xa5, 0x00) }, + { "Orange1", RGB_(0xff, 0xa5, 0x0) }, + { "Orange2", RGB_(0xee, 0x9a, 0x0) }, + { "Orange3", RGB_(0xcd, 0x85, 0x0) }, + { "Orange4", RGB_(0x8b, 0x5a, 0x0) }, + { "OrangeRed", RGB_(0xff, 0x45, 0x00) }, + { "OrangeRed1", RGB_(0xff, 0x45, 0x0) }, + { "OrangeRed2", RGB_(0xee, 0x40, 0x0) }, + { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) }, + { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) }, + { "Orchid", RGB_(0xda, 0x70, 0xd6) }, + { "Orchid1", RGB_(0xff, 0x83, 0xfa) }, + { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, + { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, + { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, + { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, + { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, + { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, + { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, + { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) }, + { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) }, + { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) }, + { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) }, + { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) }, + { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) }, + { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) }, + { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) }, + { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) }, + { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) }, + { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) }, + { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) }, + { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) }, + { "PeachPuff", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) }, + { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) }, + { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) }, + { "Peru", RGB_(0xcd, 0x85, 0x3f) }, + { "Pink", RGB_(0xff, 0xc0, 0xcb) }, + { "Pink1", RGB_(0xff, 0xb5, 0xc5) }, + { "Pink2", RGB_(0xee, 0xa9, 0xb8) }, + { "Pink3", RGB_(0xcd, 0x91, 0x9e) }, + { "Pink4", RGB_(0x8b, 0x63, 0x6c) }, + { "Plum", RGB_(0xdd, 0xa0, 0xdd) }, + { "Plum1", RGB_(0xff, 0xbb, 0xff) }, + { "Plum2", RGB_(0xee, 0xae, 0xee) }, + { "Plum3", RGB_(0xcd, 0x96, 0xcd) }, + { "Plum4", RGB_(0x8b, 0x66, 0x8b) }, + { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) }, + { "Purple", RGB_(0x80, 0x00, 0x80) }, + { "Purple1", RGB_(0x9b, 0x30, 0xff) }, + { "Purple2", RGB_(0x91, 0x2c, 0xee) }, + { "Purple3", RGB_(0x7d, 0x26, 0xcd) }, + { "Purple4", RGB_(0x55, 0x1a, 0x8b) }, + { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) }, + { "Red", RGB_(0xff, 0x00, 0x00) }, + { "Red1", RGB_(0xff, 0x0, 0x0) }, + { "Red2", RGB_(0xee, 0x0, 0x0) }, + { "Red3", RGB_(0xcd, 0x0, 0x0) }, + { "Red4", RGB_(0x8b, 0x0, 0x0) }, + { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) }, + { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) }, + { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) }, + { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) }, + { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) }, + { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) }, + { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) }, + { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) }, + { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) }, + { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) }, + { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) }, + { "Salmon", RGB_(0xfa, 0x80, 0x72) }, + { "Salmon1", RGB_(0xff, 0x8c, 0x69) }, + { "Salmon2", RGB_(0xee, 0x82, 0x62) }, + { "Salmon3", RGB_(0xcd, 0x70, 0x54) }, + { "Salmon4", RGB_(0x8b, 0x4c, 0x39) }, + { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) }, + { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) }, + { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) }, + { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) }, + { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaShell", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell1", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell2", RGB_(0xee, 0xe5, 0xde) }, + { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) }, + { "Seashell4", RGB_(0x8b, 0x86, 0x82) }, + { "Sienna", RGB_(0xa0, 0x52, 0x2d) }, + { "Sienna1", RGB_(0xff, 0x82, 0x47) }, + { "Sienna2", RGB_(0xee, 0x79, 0x42) }, + { "Sienna3", RGB_(0xcd, 0x68, 0x39) }, + { "Sienna4", RGB_(0x8b, 0x47, 0x26) }, + { "Silver", RGB_(0xc0, 0xc0, 0xc0) }, + { "SkyBlue", RGB_(0x87, 0xce, 0xeb) }, + { "SkyBlue1", RGB_(0x87, 0xce, 0xff) }, + { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) }, + { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) }, + { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) }, + { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) }, + { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) }, + { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) }, + { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) }, + { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) }, + { "SlateGray", RGB_(0x70, 0x80, 0x90) }, + { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) }, + { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) }, + { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) }, + { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) }, + { "SlateGrey", RGB_(0x70, 0x80, 0x90) }, + { "Snow", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow1", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow2", RGB_(0xee, 0xe9, 0xe9) }, + { "Snow3", RGB_(0xcd, 0xc9, 0xc9) }, + { "Snow4", RGB_(0x8b, 0x89, 0x89) }, + { "SpringGreen", RGB_(0x00, 0xff, 0x7f) }, + { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) }, + { "SpringGreen2", RGB_(0x0, 0xee, 0x76) }, + { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) }, + { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) }, + { "SteelBlue", RGB_(0x46, 0x82, 0xb4) }, + { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) }, + { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) }, + { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) }, + { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) }, + { "Tan", RGB_(0xd2, 0xb4, 0x8c) }, + { "Tan1", RGB_(0xff, 0xa5, 0x4f) }, + { "Tan2", RGB_(0xee, 0x9a, 0x49) }, + { "Tan3", RGB_(0xcd, 0x85, 0x3f) }, + { "Tan4", RGB_(0x8b, 0x5a, 0x2b) }, + { "Teal", RGB_(0x00, 0x80, 0x80) }, + { "Thistle", RGB_(0xd8, 0xbf, 0xd8) }, + { "Thistle1", RGB_(0xff, 0xe1, 0xff) }, + { "Thistle2", RGB_(0xee, 0xd2, 0xee) }, + { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) }, + { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) }, + { "Tomato", RGB_(0xff, 0x63, 0x47) }, + { "Tomato1", RGB_(0xff, 0x63, 0x47) }, + { "Tomato2", RGB_(0xee, 0x5c, 0x42) }, + { "Tomato3", RGB_(0xcd, 0x4f, 0x39) }, + { "Tomato4", RGB_(0x8b, 0x36, 0x26) }, + { "Turquoise", RGB_(0x40, 0xe0, 0xd0) }, + { "Turquoise1", RGB_(0x0, 0xf5, 0xff) }, + { "Turquoise2", RGB_(0x0, 0xe5, 0xee) }, + { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) }, + { "Turquoise4", RGB_(0x0, 0x86, 0x8b) }, + { "Violet", RGB_(0xee, 0x82, 0xee) }, + { "VioletRed", RGB_(0xd0, 0x20, 0x90) }, + { "VioletRed1", RGB_(0xff, 0x3e, 0x96) }, + { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) }, + { "VioletRed3", RGB_(0xcd, 0x32, 0x78) }, + { "VioletRed4", RGB_(0x8b, 0x22, 0x52) }, + { "WebGray", RGB_(0x80, 0x80, 0x80) }, + { "WebGreen", RGB_(0x0, 0x80, 0x0) }, + { "WebGrey", RGB_(0x80, 0x80, 0x80) }, + { "WebMaroon", RGB_(0x80, 0x0, 0x0) }, + { "WebPurple", RGB_(0x80, 0x0, 0x80) }, + { "Wheat", RGB_(0xf5, 0xde, 0xb3) }, + { "Wheat1", RGB_(0xff, 0xe7, 0xba) }, + { "Wheat2", RGB_(0xee, 0xd8, 0xae) }, + { "Wheat3", RGB_(0xcd, 0xba, 0x96) }, + { "Wheat4", RGB_(0x8b, 0x7e, 0x66) }, + { "White", RGB_(0xff, 0xff, 0xff) }, + { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) }, + { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Green", RGB_(0x0, 0xff, 0x0) }, + { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Maroon", RGB_(0xb0, 0x30, 0x60) }, + { "X11Purple", RGB_(0xa0, 0x20, 0xf0) }, + { "Yellow", RGB_(0xff, 0xff, 0x00) }, + { "Yellow1", RGB_(0xff, 0xff, 0x0) }, + { "Yellow2", RGB_(0xee, 0xee, 0x0) }, + { "Yellow3", RGB_(0xcd, 0xcd, 0x0) }, + { "Yellow4", RGB_(0x8b, 0x8b, 0x0) }, + { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) }, + { NULL, 0 }, +}; + +/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), +/// else look into color_name_table to translate a color name to its +/// hex value +/// +/// @param[in] name string value to convert to RGB +/// return the hex value or -1 if could not find a correct value +RgbValue name_to_color(const char *name) +{ + if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) + && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) + && isxdigit(name[6]) && name[7] == NUL) { + // rgb hex string + return (RgbValue)strtol((char *)(name + 1), NULL, 16); + } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { + return normal_bg; + } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { + return normal_fg; + } + + for (int i = 0; color_name_table[i].name != NULL; i++) { + if (!STRICMP(name, color_name_table[i].name)) { + return color_name_table[i].color; + } + } + + return -1; +} + +int name_to_ctermcolor(const char *name) +{ + int i; + int off = TOUPPER_ASC(*name); + for (i = ARRAY_SIZE(color_names); --i >= 0;) { + if (off == color_names[i][0] + && STRICMP(name+1, color_names[i]+1) == 0) { + break; + } + } + if (i < 0) { + return -1; + } + TriState bold = kNone; + return lookup_color(i, false, &bold); +} diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h new file mode 100644 index 0000000000..325113a4ab --- /dev/null +++ b/src/nvim/highlight_group.h @@ -0,0 +1,19 @@ +#ifndef NVIM_HIGHLIGHT_GROUP_H +#define NVIM_HIGHLIGHT_GROUP_H + +#include "nvim/types.h" +#include "nvim/eval.h" + +#define MAX_HL_ID 20000 // maximum value for a highlight ID. + +typedef struct { + char *name; + RgbValue color; +} color_name_table_T; +extern color_name_table_T color_name_table[]; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "highlight_group.h.generated.h" +#endif + +#endif // NVIM_HIGHLIGHT_GROUP_H diff --git a/src/nvim/main.c b/src/nvim/main.c index d67b47e82c..dec1ae93e7 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -25,6 +25,7 @@ #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/iconv.h" #include "nvim/if_cscope.h" #include "nvim/lua/executor.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index d68ca6b62e..6cdc4f1fde 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -13,6 +13,7 @@ #include "nvim/decoration_provider.h" #include "nvim/eval.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/memfile.h" #include "nvim/memory.h" diff --git a/src/nvim/option.c b/src/nvim/option.c index ef1c2d499c..ffd009be89 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -47,6 +47,7 @@ #include "nvim/getchar.h" #include "nvim/hardcopy.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/macros.h" @@ -3853,7 +3854,7 @@ static bool parse_winhl_opt(win_T *wp) size_t nlen = (size_t)(colon-p); char *hi = colon+1; char *commap = xstrchrnul(hi, ','); - int len = (int)(commap-hi); + size_t len = (size_t)(commap-hi); int hl_id = len ? syn_check_group(hi, len) : -1; if (strncmp("Normal", p, nlen) == 0) { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 1a08b22f49..d868fe8284 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -22,6 +22,7 @@ #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/highlight_group.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -39,7 +40,6 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/strings.h" -#include "nvim/syntax.h" #include "nvim/ui.h" #include "nvim/vim.h" #include "nvim/window.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 3c974f62bd..bc9c6bbe00 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -88,6 +88,7 @@ #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 6d0ac30003..50400852b8 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -13,6 +13,7 @@ #include "nvim/edit.h" #include "nvim/ex_docmd.h" #include "nvim/fold.h" +#include "nvim/highlight_group.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/screen.h" @@ -954,7 +955,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*linehl == NUL) { sp->sn_line_hl = 0; } else { - sp->sn_line_hl = syn_check_group((char *)linehl, (int)STRLEN(linehl)); + sp->sn_line_hl = syn_check_group((char *)linehl, STRLEN(linehl)); } } @@ -962,7 +963,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*texthl == NUL) { sp->sn_text_hl = 0; } else { - sp->sn_text_hl = syn_check_group((char *)texthl, (int)STRLEN(texthl)); + sp->sn_text_hl = syn_check_group((char *)texthl, STRLEN(texthl)); } } @@ -970,7 +971,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*culhl == NUL) { sp->sn_cul_hl = 0; } else { - sp->sn_cul_hl = syn_check_group((char *)culhl, (int)STRLEN(culhl)); + sp->sn_cul_hl = syn_check_group((char *)culhl, STRLEN(culhl)); } } @@ -978,7 +979,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text if (*numhl == NUL) { sp->sn_num_hl = 0; } else { - sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + sp->sn_num_hl = syn_check_group(numhl, STRLEN(numhl)); } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 068c007d93..f829b6a270 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -25,6 +25,7 @@ #include "nvim/garray.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/indent_c.h" #include "nvim/keymap.h" #include "nvim/lua/executor.h" @@ -51,56 +52,6 @@ static bool did_syntax_onoff = false; -/// Structure that stores information about a highlight group. -/// The ID of a highlight group is also called group ID. It is the index in -/// the highlight_ga array PLUS ONE. -typedef struct hl_group { - char_u *sg_name; ///< highlight group name - char *sg_name_u; ///< uppercase of sg_name - bool sg_cleared; ///< "hi clear" was used - int sg_attr; ///< Screen attr @see ATTR_ENTRY - int sg_link; ///< link to this highlight group ID - int sg_deflink; ///< default link; restored in highlight_clear() - int sg_set; ///< combination of flags in \ref SG_SET - sctx_T sg_deflink_sctx; ///< script where the default link was set - sctx_T sg_script_ctx; ///< script in which the group was last set - // for terminal UIs - int sg_cterm; ///< "cterm=" highlighting attr - ///< (combination of \ref HlAttrFlags) - int sg_cterm_fg; ///< terminal fg color number + 1 - int sg_cterm_bg; ///< terminal bg color number + 1 - bool sg_cterm_bold; ///< bold attr was set for light color - // for RGB UIs - int sg_gui; ///< "gui=" highlighting attributes - ///< (combination of \ref HlAttrFlags) - RgbValue sg_rgb_fg; ///< RGB foreground color - RgbValue sg_rgb_bg; ///< RGB background color - RgbValue sg_rgb_sp; ///< RGB special color - char *sg_rgb_fg_name; ///< RGB foreground color name - char *sg_rgb_bg_name; ///< RGB background color name - char *sg_rgb_sp_name; ///< RGB special color name - - int sg_blend; ///< blend level (0-100 inclusive), -1 if unset -} HlGroup; - -/// \addtogroup SG_SET -/// @{ -#define SG_CTERM 2 // cterm has been set -#define SG_GUI 4 // gui has been set -#define SG_LINK 8 // link has been set -/// @} - -// builtin |highlight-groups| -static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; -Map(cstr_t, int) highlight_unames = MAP_INIT; - -static inline struct hl_group *HL_TABLE(void) -{ - return ((struct hl_group *)((highlight_ga.ga_data))); -} - -#define MAX_HL_ID 20000 // maximum value for a highlight ID. - // different types of offsets that are possible #define SPO_MS_OFF 0 // match start offset #define SPO_ME_OFF 1 // match end offset @@ -111,22 +62,6 @@ static inline struct hl_group *HL_TABLE(void) #define SPO_LC_OFF 6 // leading context offset #define SPO_COUNT 7 -// Flags to indicate an additional string for highlight name completion. -static int include_none = 0; // when 1 include "nvim/None" -static int include_default = 0; // when 1 include "nvim/default" -static int include_link = 0; // when 2 include "nvim/link" and "clear" - -#define MAX_SYN_NAME 200 - -/// The "term", "cterm" and "gui" arguments can be any combination of the -/// following names, separated by commas (but no spaces!). -static char *(hl_name_table[]) = -{ "bold", "standout", "underline", "underlineline", "undercurl", "underdot", - "underdash", "italic", "reverse", "inverse", "strikethrough", "nocombine", "NONE" }; -static int hl_attr_table[] = -{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERLINELINE, HL_UNDERCURL, HL_UNDERDOT, HL_UNDERDASH, - HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 }; - static char e_illegal_arg[] = N_("E390: Illegal argument: %s"); // The patterns that are being searched for are stored in a syn_pattern. @@ -3609,7 +3544,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing) /* * No argument: List all group IDs and all syntax clusters. */ - for (int id = 1; id <= highlight_ga.ga_len && !got_int; id++) { + for (int id = 1; id <= highlight_num_groups() && !got_int; id++) { syn_list_one(id, syncing, false); } for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) { @@ -3767,8 +3702,8 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) } msg_putchar(' '); if (spp->sp_sync_idx >= 0) { - msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s) - [spp->sp_sync_idx].sp_syn.id - 1].sg_name); + msg_outtrans(highlight_group_name(SYN_ITEMS(curwin->w_s) + [spp->sp_sync_idx].sp_syn.id - 1)); } else { msg_puts("NONE"); } @@ -3777,11 +3712,11 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only) } // list the link, if there is one - if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int) { + if (highlight_link_id(id - 1) && (did_header || link_only) && !got_int) { (void)syn_list_header(did_header, 0, id, true); msg_puts_attr("links to", attr); msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + msg_outtrans(highlight_group_name(highlight_link_id(id - 1) - 1)); } } @@ -3845,7 +3780,7 @@ static void put_id_list(const char *const name, const int16_t *const list, const msg_putchar('@'); msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name); } else { - msg_outtrans(HL_TABLE()[*p - 1].sg_name); + msg_outtrans(highlight_group_name(*p - 1)); } if (p[1]) { msg_putchar(','); @@ -3867,7 +3802,7 @@ static void put_pattern(const char *const s, const int c, const synpat_T *const if (last_matchgroup == 0) { msg_outtrans((char_u *)"NONE"); } else { - msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name); + msg_outtrans(highlight_group_name(last_matchgroup - 1)); } msg_putchar(' '); } @@ -5459,8 +5394,8 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis regmatch.rm_ic = TRUE; id = 0; - for (int i = highlight_ga.ga_len; --i >= 0;) { - if (vim_regexec(®match, HL_TABLE()[i].sg_name, (colnr_T)0)) { + for (int i = highlight_num_groups(); --i >= 0;) { + if (vim_regexec(®match, highlight_group_name(i), (colnr_T)0)) { if (round == 2) { // Got more items than expected; can happen // when adding items that match: @@ -6121,7 +6056,7 @@ static void syntime_report(void) msg_puts(profile_msg(p->average)); msg_puts(" "); msg_advance(50); - msg_outtrans(HL_TABLE()[p->id - 1].sg_name); + msg_outtrans(highlight_group_name(p->id - 1)); msg_puts(" "); msg_advance(69); @@ -6146,2741 +6081,3 @@ static void syntime_report(void) msg_puts("\n"); } } - -/************************************** -* Highlighting stuff * -**************************************/ - -// The default highlight groups. These are compiled-in for fast startup and -// they still work when the runtime files can't be found. -// -// When making changes here, also change runtime/colors/default.vim! - -static const char *highlight_init_both[] = { - "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", - "Cursor guibg=fg guifg=bg", - "lCursor guibg=fg guifg=bg", - "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red", - "ErrorMsg ctermbg=DarkRed ctermfg=White guibg=Red guifg=White", - "IncSearch cterm=reverse gui=reverse", - "ModeMsg cterm=bold gui=bold", - "NonText ctermfg=Blue gui=bold guifg=Blue", - "Normal cterm=NONE gui=NONE", - "PmenuSbar ctermbg=Grey guibg=Grey", - "StatusLine cterm=reverse,bold gui=reverse,bold", - "StatusLineNC cterm=reverse gui=reverse", - "TabLineFill cterm=reverse gui=reverse", - "TabLineSel cterm=bold gui=bold", - "TermCursor cterm=reverse gui=reverse", - "VertSplit cterm=reverse gui=reverse", - "WildMenu ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "default link WinSeparator VertSplit", - "default link EndOfBuffer NonText", - "default link LineNrAbove LineNr", - "default link LineNrBelow LineNr", - "default link QuickFixLine Search", - "default link CursorLineSign SignColumn", - "default link CursorLineFold FoldColumn", - "default link Substitute Search", - "default link Whitespace NonText", - "default link MsgSeparator StatusLine", - "default link NormalFloat Pmenu", - "default link FloatBorder WinSeparator", - "default FloatShadow blend=80 guibg=Black", - "default FloatShadowThrough blend=100 guibg=Black", - "RedrawDebugNormal cterm=reverse gui=reverse", - "RedrawDebugClear ctermbg=Yellow guibg=Yellow", - "RedrawDebugComposed ctermbg=Green guibg=Green", - "RedrawDebugRecompose ctermbg=Red guibg=Red", - "Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red", - "Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Blue guibg=Yellow", - "default link String Constant", - "default link Character Constant", - "default link Number Constant", - "default link Boolean Constant", - "default link Float Number", - "default link Function Identifier", - "default link Conditional Statement", - "default link Repeat Statement", - "default link Label Statement", - "default link Operator Statement", - "default link Keyword Statement", - "default link Exception Statement", - "default link Include PreProc", - "default link Define PreProc", - "default link Macro PreProc", - "default link PreCondit PreProc", - "default link StorageClass Type", - "default link Structure Type", - "default link Typedef Type", - "default link Tag Special", - "default link SpecialChar Special", - "default link Delimiter Special", - "default link SpecialComment Special", - "default link Debug Special", - "default DiagnosticError ctermfg=1 guifg=Red", - "default DiagnosticWarn ctermfg=3 guifg=Orange", - "default DiagnosticInfo ctermfg=4 guifg=LightBlue", - "default DiagnosticHint ctermfg=7 guifg=LightGrey", - "default DiagnosticUnderlineError cterm=underline gui=underline guisp=Red", - "default DiagnosticUnderlineWarn cterm=underline gui=underline guisp=Orange", - "default DiagnosticUnderlineInfo cterm=underline gui=underline guisp=LightBlue", - "default DiagnosticUnderlineHint cterm=underline gui=underline guisp=LightGrey", - "default link DiagnosticVirtualTextError DiagnosticError", - "default link DiagnosticVirtualTextWarn DiagnosticWarn", - "default link DiagnosticVirtualTextInfo DiagnosticInfo", - "default link DiagnosticVirtualTextHint DiagnosticHint", - "default link DiagnosticFloatingError DiagnosticError", - "default link DiagnosticFloatingWarn DiagnosticWarn", - "default link DiagnosticFloatingInfo DiagnosticInfo", - "default link DiagnosticFloatingHint DiagnosticHint", - "default link DiagnosticSignError DiagnosticError", - "default link DiagnosticSignWarn DiagnosticWarn", - "default link DiagnosticSignInfo DiagnosticInfo", - "default link DiagnosticSignHint DiagnosticHint", - NULL -}; - -// Default colors only used with a light background. -static const char *highlight_init_light[] = { - "ColorColumn ctermbg=LightRed guibg=LightRed", - "CursorColumn ctermbg=LightGrey guibg=Grey90", - "CursorLine cterm=underline guibg=Grey90", - "CursorLineNr cterm=underline ctermfg=Brown gui=bold guifg=Brown", - "DiffAdd ctermbg=LightBlue guibg=LightBlue", - "DiffChange ctermbg=LightMagenta guibg=LightMagenta", - "DiffDelete ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan", - "Directory ctermfg=DarkBlue guifg=Blue", - "FoldColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "Folded ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue", - "LineNr ctermfg=Brown guifg=Brown", - "MatchParen ctermbg=Cyan guibg=Cyan", - "MoreMsg ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta", - "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey", - "PmenuThumb ctermbg=Black guibg=Black", - "Question ctermfg=DarkGreen gui=bold guifg=SeaGreen", - "Search ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE", - "SignColumn ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue", - "SpecialKey ctermfg=DarkBlue guifg=Blue", - "SpellBad ctermbg=LightRed guisp=Red gui=undercurl", - "SpellCap ctermbg=LightBlue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=DarkCyan gui=undercurl", - "SpellRare ctermbg=LightMagenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey", - "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", - "Visual guibg=LightGrey", - "WarningMsg ctermfg=DarkRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=Blue guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE", - "Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a5acd guibg=NONE", - "Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=#6a0dad guibg=NONE", - "Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=bold guifg=SeaGreen guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=SlateBlue", - "Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=bg guibg=NONE", - NULL -}; - -// Default colors only used with a dark background. -static const char *highlight_init_dark[] = { - "ColorColumn ctermbg=DarkRed guibg=DarkRed", - "CursorColumn ctermbg=DarkGrey guibg=Grey40", - "CursorLine cterm=underline guibg=Grey40", - "CursorLineNr cterm=underline ctermfg=Yellow gui=bold guifg=Yellow", - "DiffAdd ctermbg=DarkBlue guibg=DarkBlue", - "DiffChange ctermbg=DarkMagenta guibg=DarkMagenta", - "DiffDelete ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan", - "Directory ctermfg=LightCyan guifg=Cyan", - "FoldColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "Folded ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan", - "LineNr ctermfg=Yellow guifg=Yellow", - "MatchParen ctermbg=DarkCyan guibg=DarkCyan", - "MoreMsg ctermfg=LightGreen gui=bold guifg=SeaGreen", - "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta", - "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey", - "PmenuThumb ctermbg=White guibg=White", - "Question ctermfg=LightGreen gui=bold guifg=Green", - "Search ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black", - "SignColumn ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan", - "SpecialKey ctermfg=LightBlue guifg=Cyan", - "SpellBad ctermbg=Red guisp=Red gui=undercurl", - "SpellCap ctermbg=Blue guisp=Blue gui=undercurl", - "SpellLocal ctermbg=Cyan guisp=Cyan gui=undercurl", - "SpellRare ctermbg=Magenta guisp=Magenta gui=undercurl", - "TabLine cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey", - "Title ctermfg=LightMagenta gui=bold guifg=Magenta", - "Visual guibg=DarkGrey", - "WarningMsg ctermfg=LightRed guifg=Red", - "Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#80a0ff guibg=NONE", - "Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=#ffa0a0 guibg=NONE", - "Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=Orange guibg=NONE", - "Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=NONE guifg=#40ffff guibg=NONE", - "Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=bold guifg=#ffff60 guibg=NONE", - "PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=#ff80ff guibg=NONE", - "Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=bold guifg=#60ff60 guibg=NONE", - "Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=#80a0ff", - "Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=bg guibg=NONE", - NULL -}; - -const char *const highlight_init_cmdline[] = { - // XXX When modifying a list modify it in both valid and invalid halves. - // TODO(ZyX-I): merge valid and invalid groups via a macros. - - // NvimInternalError should appear only when highlighter has a bug. - "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red", - - // Highlight groups (links) used by parser: - - "default link NvimAssignment Operator", - "default link NvimPlainAssignment NvimAssignment", - "default link NvimAugmentedAssignment NvimAssignment", - "default link NvimAssignmentWithAddition NvimAugmentedAssignment", - "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment", - "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment", - - "default link NvimOperator Operator", - - "default link NvimUnaryOperator NvimOperator", - "default link NvimUnaryPlus NvimUnaryOperator", - "default link NvimUnaryMinus NvimUnaryOperator", - "default link NvimNot NvimUnaryOperator", - - "default link NvimBinaryOperator NvimOperator", - "default link NvimComparison NvimBinaryOperator", - "default link NvimComparisonModifier NvimComparison", - "default link NvimBinaryPlus NvimBinaryOperator", - "default link NvimBinaryMinus NvimBinaryOperator", - "default link NvimConcat NvimBinaryOperator", - "default link NvimConcatOrSubscript NvimConcat", - "default link NvimOr NvimBinaryOperator", - "default link NvimAnd NvimBinaryOperator", - "default link NvimMultiplication NvimBinaryOperator", - "default link NvimDivision NvimBinaryOperator", - "default link NvimMod NvimBinaryOperator", - - "default link NvimTernary NvimOperator", - "default link NvimTernaryColon NvimTernary", - - "default link NvimParenthesis Delimiter", - "default link NvimLambda NvimParenthesis", - "default link NvimNestingParenthesis NvimParenthesis", - "default link NvimCallingParenthesis NvimParenthesis", - - "default link NvimSubscript NvimParenthesis", - "default link NvimSubscriptBracket NvimSubscript", - "default link NvimSubscriptColon NvimSubscript", - "default link NvimCurly NvimSubscript", - - "default link NvimContainer NvimParenthesis", - "default link NvimDict NvimContainer", - "default link NvimList NvimContainer", - - "default link NvimIdentifier Identifier", - "default link NvimIdentifierScope NvimIdentifier", - "default link NvimIdentifierScopeDelimiter NvimIdentifier", - "default link NvimIdentifierName NvimIdentifier", - "default link NvimIdentifierKey NvimIdentifier", - - "default link NvimColon Delimiter", - "default link NvimComma Delimiter", - "default link NvimArrow Delimiter", - - "default link NvimRegister SpecialChar", - "default link NvimNumber Number", - "default link NvimFloat NvimNumber", - "default link NvimNumberPrefix Type", - - "default link NvimOptionSigil Type", - "default link NvimOptionName NvimIdentifier", - "default link NvimOptionScope NvimIdentifierScope", - "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter", - - "default link NvimEnvironmentSigil NvimOptionSigil", - "default link NvimEnvironmentName NvimIdentifier", - - "default link NvimString String", - "default link NvimStringBody NvimString", - "default link NvimStringQuote NvimString", - "default link NvimStringSpecial SpecialChar", - - "default link NvimSingleQuote NvimStringQuote", - "default link NvimSingleQuotedBody NvimStringBody", - "default link NvimSingleQuotedQuote NvimStringSpecial", - - "default link NvimDoubleQuote NvimStringQuote", - "default link NvimDoubleQuotedBody NvimStringBody", - "default link NvimDoubleQuotedEscape NvimStringSpecial", - - "default link NvimFigureBrace NvimInternalError", - "default link NvimSingleQuotedUnknownEscape NvimInternalError", - - "default link NvimSpacing Normal", - - // NvimInvalid groups: - - "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError", - - "default link NvimInvalid Error", - - "default link NvimInvalidAssignment NvimInvalid", - "default link NvimInvalidPlainAssignment NvimInvalidAssignment", - "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment", - "default link NvimInvalidAssignmentWithAddition NvimInvalidAugmentedAssignment", - "default link NvimInvalidAssignmentWithSubtraction NvimInvalidAugmentedAssignment", - "default link NvimInvalidAssignmentWithConcatenation NvimInvalidAugmentedAssignment", - - "default link NvimInvalidOperator NvimInvalid", - - "default link NvimInvalidUnaryOperator NvimInvalidOperator", - "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator", - "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator", - "default link NvimInvalidNot NvimInvalidUnaryOperator", - - "default link NvimInvalidBinaryOperator NvimInvalidOperator", - "default link NvimInvalidComparison NvimInvalidBinaryOperator", - "default link NvimInvalidComparisonModifier NvimInvalidComparison", - "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator", - "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator", - "default link NvimInvalidConcat NvimInvalidBinaryOperator", - "default link NvimInvalidConcatOrSubscript NvimInvalidConcat", - "default link NvimInvalidOr NvimInvalidBinaryOperator", - "default link NvimInvalidAnd NvimInvalidBinaryOperator", - "default link NvimInvalidMultiplication NvimInvalidBinaryOperator", - "default link NvimInvalidDivision NvimInvalidBinaryOperator", - "default link NvimInvalidMod NvimInvalidBinaryOperator", - - "default link NvimInvalidTernary NvimInvalidOperator", - "default link NvimInvalidTernaryColon NvimInvalidTernary", - - "default link NvimInvalidDelimiter NvimInvalid", - - "default link NvimInvalidParenthesis NvimInvalidDelimiter", - "default link NvimInvalidLambda NvimInvalidParenthesis", - "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis", - "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis", - - "default link NvimInvalidSubscript NvimInvalidParenthesis", - "default link NvimInvalidSubscriptBracket NvimInvalidSubscript", - "default link NvimInvalidSubscriptColon NvimInvalidSubscript", - "default link NvimInvalidCurly NvimInvalidSubscript", - - "default link NvimInvalidContainer NvimInvalidParenthesis", - "default link NvimInvalidDict NvimInvalidContainer", - "default link NvimInvalidList NvimInvalidContainer", - - "default link NvimInvalidValue NvimInvalid", - - "default link NvimInvalidIdentifier NvimInvalidValue", - "default link NvimInvalidIdentifierScope NvimInvalidIdentifier", - "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier", - "default link NvimInvalidIdentifierName NvimInvalidIdentifier", - "default link NvimInvalidIdentifierKey NvimInvalidIdentifier", - - "default link NvimInvalidColon NvimInvalidDelimiter", - "default link NvimInvalidComma NvimInvalidDelimiter", - "default link NvimInvalidArrow NvimInvalidDelimiter", - - "default link NvimInvalidRegister NvimInvalidValue", - "default link NvimInvalidNumber NvimInvalidValue", - "default link NvimInvalidFloat NvimInvalidNumber", - "default link NvimInvalidNumberPrefix NvimInvalidNumber", - - "default link NvimInvalidOptionSigil NvimInvalidIdentifier", - "default link NvimInvalidOptionName NvimInvalidIdentifier", - "default link NvimInvalidOptionScope NvimInvalidIdentifierScope", - "default link NvimInvalidOptionScopeDelimiter " - "NvimInvalidIdentifierScopeDelimiter", - - "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil", - "default link NvimInvalidEnvironmentName NvimInvalidIdentifier", - - // Invalid string bodies and specials are still highlighted as valid ones to - // minimize the red area. - "default link NvimInvalidString NvimInvalidValue", - "default link NvimInvalidStringBody NvimStringBody", - "default link NvimInvalidStringQuote NvimInvalidString", - "default link NvimInvalidStringSpecial NvimStringSpecial", - - "default link NvimInvalidSingleQuote NvimInvalidStringQuote", - "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody", - "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial", - - "default link NvimInvalidDoubleQuote NvimInvalidStringQuote", - "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody", - "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial", - "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue", - - "default link NvimInvalidFigureBrace NvimInvalidDelimiter", - - "default link NvimInvalidSpacing ErrorMsg", - - // Not actually invalid, but we highlight user that he is doing something - // wrong. - "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue", - NULL, -}; - -/// Create default links for Nvim* highlight groups used for cmdline coloring -void syn_init_cmdline_highlight(bool reset, bool init) -{ - for (size_t i = 0; highlight_init_cmdline[i] != NULL; i++) { - do_highlight(highlight_init_cmdline[i], reset, init); - } -} - -/// Load colors from a file if "g:colors_name" is set, otherwise load builtin -/// colors -/// -/// @param both include groups where 'bg' doesn't matter -/// @param reset clear groups first -void init_highlight(bool both, bool reset) -{ - static int had_both = false; - - // Try finding the color scheme file. Used when a color file was loaded - // and 'background' or 't_Co' is changed. - char_u *p = get_var_value("g:colors_name"); - if (p != NULL) { - // Value of g:colors_name could be freed in load_colors() and make - // p invalid, so copy it. - char_u *copy_p = vim_strsave(p); - bool okay = load_colors(copy_p); - xfree(copy_p); - if (okay) { - return; - } - } - - /* - * Didn't use a color file, use the compiled-in colors. - */ - if (both) { - had_both = true; - const char *const *const pp = highlight_init_both; - for (size_t i = 0; pp[i] != NULL; i++) { - do_highlight(pp[i], reset, true); - } - } else if (!had_both) { - // Don't do anything before the call with both == true from main(). - // Not everything has been setup then, and that call will overrule - // everything anyway. - return; - } - - const char *const *const pp = ((*p_bg == 'l') - ? highlight_init_light - : highlight_init_dark); - for (size_t i = 0; pp[i] != NULL; i++) { - do_highlight(pp[i], reset, true); - } - - /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it - * depend on the number of colors available. - * With 8 colors brown is equal to yellow, need to use black for Search fg - * to avoid Statement highlighted text disappears. - * Clear the attributes, needed when changing the t_Co value. */ - if (t_colors > 8) { - do_highlight((*p_bg == 'l' - ? "Visual cterm=NONE ctermbg=LightGrey" - : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); - } else { - do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); - if (*p_bg == 'l') { - do_highlight("Search ctermfg=black", false, true); - } - } - - syn_init_cmdline_highlight(false, false); -} - -/* - * Load color file "name". - * Return OK for success, FAIL for failure. - */ -int load_colors(char_u *name) -{ - char_u *buf; - int retval = FAIL; - static bool recursive = false; - - // When being called recursively, this is probably because setting - // 'background' caused the highlighting to be reloaded. This means it is - // working, thus we should return OK. - if (recursive) { - return OK; - } - - recursive = true; - size_t buflen = STRLEN(name) + 12; - buf = xmalloc(buflen); - apply_autocmds(EVENT_COLORSCHEMEPRE, name, curbuf->b_fname, false, curbuf); - snprintf((char *)buf, buflen, "colors/%s.vim", name); - retval = source_runtime((char *)buf, DIP_START + DIP_OPT); - if (retval == FAIL) { - snprintf((char *)buf, buflen, "colors/%s.lua", name); - retval = source_runtime((char *)buf, DIP_START + DIP_OPT); - } - xfree(buf); - apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, false, curbuf); - - recursive = false; - - return retval; -} - -static char *(color_names[28]) = { - "Black", "DarkBlue", "DarkGreen", "DarkCyan", - "DarkRed", "DarkMagenta", "Brown", "DarkYellow", - "Gray", "Grey", "LightGray", "LightGrey", - "DarkGray", "DarkGrey", - "Blue", "LightBlue", "Green", "LightGreen", - "Cyan", "LightCyan", "Red", "LightRed", "Magenta", - "LightMagenta", "Yellow", "LightYellow", "White", "NONE" -}; -// indices: -// 0, 1, 2, 3, -// 4, 5, 6, 7, -// 8, 9, 10, 11, -// 12, 13, -// 14, 15, 16, 17, -// 18, 19, 20, 21, 22, -// 23, 24, 25, 26, 27 -static int color_numbers_16[28] = { 0, 1, 2, 3, - 4, 5, 6, 6, - 7, 7, 7, 7, - 8, 8, - 9, 9, 10, 10, - 11, 11, 12, 12, 13, - 13, 14, 14, 15, -1 }; -// for xterm with 88 colors... -static int color_numbers_88[28] = { 0, 4, 2, 6, - 1, 5, 32, 72, - 84, 84, 7, 7, - 82, 82, - 12, 43, 10, 61, - 14, 63, 9, 74, 13, - 75, 11, 78, 15, -1 }; -// for xterm with 256 colors... -static int color_numbers_256[28] = { 0, 4, 2, 6, - 1, 5, 130, 3, - 248, 248, 7, 7, - 242, 242, - 12, 81, 10, 121, - 14, 159, 9, 224, 13, - 225, 11, 229, 15, -1 }; -// for terminals with less than 16 colors... -static int color_numbers_8[28] = { 0, 4, 2, 6, - 1, 5, 3, 3, - 7, 7, 7, 7, - 0+8, 0+8, - 4+8, 4+8, 2+8, 2+8, - 6+8, 6+8, 1+8, 1+8, 5+8, - 5+8, 3+8, 3+8, 7+8, -1 }; - -// Lookup the "cterm" value to be used for color with index "idx" in -// color_names[]. -// "boldp" will be set to TRUE or FALSE for a foreground color when using 8 -// colors, otherwise it will be unchanged. -int lookup_color(const int idx, const bool foreground, TriState *const boldp) -{ - int color = color_numbers_16[idx]; - - // Use the _16 table to check if it's a valid color name. - if (color < 0) { - return -1; - } - - if (t_colors == 8) { - // t_Co is 8: use the 8 colors table - color = color_numbers_8[idx]; - if (foreground) { - // set/reset bold attribute to get light foreground - // colors (on some terminals, e.g. "linux") - if (color & 8) { - *boldp = kTrue; - } else { - *boldp = kFalse; - } - } - color &= 7; // truncate to 8 colors - } else if (t_colors == 16) { - color = color_numbers_8[idx]; - } else if (t_colors == 88) { - color = color_numbers_88[idx]; - } else if (t_colors >= 256) { - color = color_numbers_256[idx]; - } - return color; -} - -void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) -{ - int idx = id - 1; // Index is ID minus one. - - bool is_default = attrs.rgb_ae_attr & HL_DEFAULT; - - // Return if "default" was used and the group already has settings - if (is_default && hl_has_settings(idx, true)) { - return; - } - - HlGroup *g = &HL_TABLE()[idx]; - - if (link_id > 0) { - g->sg_cleared = false; - g->sg_link = link_id; - g->sg_script_ctx = current_sctx; - g->sg_script_ctx.sc_lnum += sourcing_lnum; - g->sg_set |= SG_LINK; - if (is_default) { - g->sg_deflink = link_id; - g->sg_deflink_sctx = current_sctx; - g->sg_deflink_sctx.sc_lnum += sourcing_lnum; - } - return; - } - - g->sg_cleared = false; - g->sg_link = 0; - g->sg_gui = attrs.rgb_ae_attr; - - g->sg_rgb_fg = attrs.rgb_fg_color; - g->sg_rgb_bg = attrs.rgb_bg_color; - g->sg_rgb_sp = attrs.rgb_sp_color; - - struct { - char **dest; RgbValue val; Object name; - } cattrs[] = { - { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, - { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, - { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, - { NULL, -1, NIL }, - }; - - char hex_name[8]; - char *name; - - for (int j = 0; cattrs[j].dest; j++) { - if (cattrs[j].val < 0) { - XFREE_CLEAR(*cattrs[j].dest); - continue; - } - - if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { - name = cattrs[j].name.data.string.data; - } else { - snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); - name = hex_name; - } - - if (!*cattrs[j].dest - || STRCMP(*cattrs[j].dest, name) != 0) { - xfree(*cattrs[j].dest); - *cattrs[j].dest = xstrdup(name); - } - } - - g->sg_cterm = attrs.cterm_ae_attr; - g->sg_cterm_bg = attrs.cterm_bg_color; - g->sg_cterm_fg = attrs.cterm_fg_color; - g->sg_cterm_bold = g->sg_cterm & HL_BOLD; - g->sg_blend = attrs.hl_blend; - - g->sg_script_ctx = current_sctx; - g->sg_script_ctx.sc_lnum += sourcing_lnum; - - // 'Normal' is special - if (STRCMP(g->sg_name_u, "NORMAL") == 0) { - cterm_normal_fg_color = g->sg_cterm_fg; - cterm_normal_bg_color = g->sg_cterm_bg; - normal_fg = g->sg_rgb_fg; - normal_bg = g->sg_rgb_bg; - normal_sp = g->sg_rgb_sp; - ui_default_colors_set(); - } else { - g->sg_attr = hl_get_syn_attr(0, id, attrs); - - // a cursor style uses this syn_id, make sure its attribute is updated. - if (cursor_mode_uses_syn_id(id)) { - ui_mode_info_set(); - } - } -} - - -/// Handle ":highlight" command -/// -/// When using ":highlight clear" this is called recursively for each group with -/// forceit and init being both true. -/// -/// @param[in] line Command arguments. -/// @param[in] forceit True when bang is given, allows to link group even if -/// it has its own settings. -/// @param[in] init True when initializing. -void do_highlight(const char *line, const bool forceit, const bool init) - FUNC_ATTR_NONNULL_ALL -{ - const char *name_end; - const char *linep; - const char *key_start; - const char *arg_start; - long i; - int off; - int len; - int attr; - int id; - int idx; - struct hl_group item_before; - bool did_change = false; - bool dodefault = false; - bool doclear = false; - bool dolink = false; - bool error = false; - int color; - bool is_normal_group = false; // "Normal" group - bool did_highlight_changed = false; - - // If no argument, list current highlighting. - if (ends_excmd((uint8_t)(*line))) { - for (i = 1; i <= highlight_ga.ga_len && !got_int; i++) { - // TODO(brammool): only call when the group has attributes set - highlight_list_one(i); - } - return; - } - - // Isolate the name. - name_end = (const char *)skiptowhite((const char_u *)line); - linep = (const char *)skipwhite((const char_u *)name_end); - - // Check for "default" argument. - if (strncmp(line, "default", name_end - line) == 0) { - dodefault = true; - line = linep; - name_end = (const char *)skiptowhite((const char_u *)line); - linep = (const char *)skipwhite((const char_u *)name_end); - } - - // Check for "clear" or "link" argument. - if (strncmp(line, "clear", name_end - line) == 0) { - doclear = true; - } else if (strncmp(line, "link", name_end - line) == 0) { - dolink = true; - } - - // ":highlight {group-name}": list highlighting for one group. - if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { - id = syn_name2id_len((const char_u *)line, (int)(name_end - line)); - if (id == 0) { - semsg(_("E411: highlight group not found: %s"), line); - } else { - highlight_list_one(id); - } - return; - } - - // Handle ":highlight link {from} {to}" command. - if (dolink) { - const char *from_start = linep; - const char *from_end; - const char *to_start; - const char *to_end; - int from_id; - int to_id; - struct hl_group *hlgroup = NULL; - - from_end = (const char *)skiptowhite((const char_u *)from_start); - to_start = (const char *)skipwhite((const char_u *)from_end); - to_end = (const char *)skiptowhite((const char_u *)to_start); - - if (ends_excmd((uint8_t)(*from_start)) - || ends_excmd((uint8_t)(*to_start))) { - semsg(_("E412: Not enough arguments: \":highlight link %s\""), - from_start); - return; - } - - if (!ends_excmd(*skipwhite((const char_u *)to_end))) { - semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start); - return; - } - - from_id = syn_check_group(from_start, (int)(from_end - from_start)); - if (strncmp(to_start, "NONE", 4) == 0) { - to_id = 0; - } else { - to_id = syn_check_group(to_start, (int)(to_end - to_start)); - } - - if (from_id > 0) { - hlgroup = &HL_TABLE()[from_id - 1]; - if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { - hlgroup->sg_deflink = to_id; - hlgroup->sg_deflink_sctx = current_sctx; - hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&hlgroup->sg_deflink_sctx); - } - } - - if (from_id > 0 && (!init || hlgroup->sg_set == 0)) { - // Don't allow a link when there already is some highlighting - // for the group, unless '!' is used - if (to_id > 0 && !forceit && !init - && hl_has_settings(from_id - 1, dodefault)) { - if (sourcing_name == NULL && !dodefault) { - emsg(_("E414: group has settings, highlight link ignored")); - } - } else if (hlgroup->sg_link != to_id - || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid - || hlgroup->sg_cleared) { - if (!init) { - hlgroup->sg_set |= SG_LINK; - } - hlgroup->sg_link = to_id; - hlgroup->sg_script_ctx = current_sctx; - hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&hlgroup->sg_script_ctx); - hlgroup->sg_cleared = false; - redraw_all_later(SOME_VALID); - - // Only call highlight changed() once after multiple changes - need_highlight_changed = true; - } - } - - return; - } - - if (doclear) { - // ":highlight clear [group]" command. - line = linep; - if (ends_excmd((uint8_t)(*line))) { - do_unlet(S_LEN("colors_name"), true); - restore_cterm_colors(); - - // Clear all default highlight groups and load the defaults. - for (int j = 0; j < highlight_ga.ga_len; j++) { - highlight_clear(j); - } - init_highlight(true, true); - highlight_changed(); - redraw_all_later(NOT_VALID); - return; - } - name_end = (const char *)skiptowhite((const char_u *)line); - linep = (const char *)skipwhite((const char_u *)name_end); - } - - // Find the group name in the table. If it does not exist yet, add it. - id = syn_check_group(line, (int)(name_end - line)); - if (id == 0) { // Failed (out of memory). - return; - } - idx = id - 1; // Index is ID minus one. - - // Return if "default" was used and the group already has settings - if (dodefault && hl_has_settings(idx, true)) { - return; - } - - // Make a copy so we can check if any attribute actually changed - item_before = HL_TABLE()[idx]; - is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); - - // Clear the highlighting for ":hi clear {group}" and ":hi clear". - if (doclear || (forceit && init)) { - highlight_clear(idx); - if (!doclear) { - HL_TABLE()[idx].sg_set = 0; - } - } - - char *key = NULL; - char *arg = NULL; - if (!doclear) { - while (!ends_excmd((uint8_t)(*linep))) { - key_start = linep; - if (*linep == '=') { - semsg(_("E415: unexpected equal sign: %s"), key_start); - error = true; - break; - } - - // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg", - // "guibg" or "guisp"). - while (*linep && !ascii_iswhite(*linep) && *linep != '=') { - linep++; - } - xfree(key); - key = (char *)vim_strnsave_up((const char_u *)key_start, - linep - key_start); - linep = (const char *)skipwhite((const char_u *)linep); - - if (strcmp(key, "NONE") == 0) { - if (!init || HL_TABLE()[idx].sg_set == 0) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; - } - highlight_clear(idx); - } - continue; - } - - // Check for the equal sign. - if (*linep != '=') { - semsg(_("E416: missing equal sign: %s"), key_start); - error = true; - break; - } - linep++; - - // Isolate the argument. - linep = (const char *)skipwhite((const char_u *)linep); - if (*linep == '\'') { // guifg='color name' - arg_start = ++linep; - linep = strchr(linep, '\''); - if (linep == NULL) { - semsg(_(e_invarg2), key_start); - error = true; - break; - } - } else { - arg_start = linep; - linep = (const char *)skiptowhite((const char_u *)linep); - } - if (linep == arg_start) { - semsg(_("E417: missing argument: %s"), key_start); - error = true; - break; - } - xfree(arg); - arg = xstrndup(arg_start, (size_t)(linep - arg_start)); - - if (*linep == '\'') { - linep++; - } - - // Store the argument. - if (strcmp(key, "TERM") == 0 - || strcmp(key, "CTERM") == 0 - || strcmp(key, "GUI") == 0) { - attr = 0; - off = 0; - while (arg[off] != NUL) { - for (i = ARRAY_SIZE(hl_attr_table); --i >= 0;) { - len = (int)STRLEN(hl_name_table[i]); - if (STRNICMP(arg + off, hl_name_table[i], len) == 0) { - attr |= hl_attr_table[i]; - off += len; - break; - } - } - if (i < 0) { - semsg(_("E418: Illegal value: %s"), arg); - error = true; - break; - } - if (arg[off] == ',') { // Another one follows. - off++; - } - } - if (error) { - break; - } - if (*key == 'C') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; - } - HL_TABLE()[idx].sg_cterm = attr; - HL_TABLE()[idx].sg_cterm_bold = false; - } - } else if (*key == 'G') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - HL_TABLE()[idx].sg_gui = attr; - } - } - } else if (STRCMP(key, "FONT") == 0) { - // in non-GUI fonts are simply ignored - } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; - } - - /* When setting the foreground color, and previously the "bold" - * flag was set for a light color, reset it now */ - if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = false; - } - - if (ascii_isdigit(*arg)) { - color = atoi(arg); - } else if (STRICMP(arg, "fg") == 0) { - if (cterm_normal_fg_color) { - color = cterm_normal_fg_color - 1; - } else { - emsg(_("E419: FG color unknown")); - error = true; - break; - } - } else if (STRICMP(arg, "bg") == 0) { - if (cterm_normal_bg_color > 0) { - color = cterm_normal_bg_color - 1; - } else { - emsg(_("E420: BG color unknown")); - error = true; - break; - } - } else { - // Reduce calls to STRICMP a bit, it can be slow. - off = TOUPPER_ASC(*arg); - for (i = ARRAY_SIZE(color_names); --i >= 0;) { - if (off == color_names[i][0] - && STRICMP(arg + 1, color_names[i] + 1) == 0) { - break; - } - } - if (i < 0) { - semsg(_("E421: Color name or number not recognized: %s"), - key_start); - error = true; - break; - } - - TriState bold = kNone; - color = lookup_color(i, key[5] == 'F', &bold); - - // set/reset bold attribute to get light foreground - // colors (on some terminals, e.g. "linux") - if (bold == kTrue) { - HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = true; - } else if (bold == kFalse) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - } - } - // Add one to the argument, to avoid zero. Zero is used for - // "NONE", then "color" is -1. - if (key[5] == 'F') { - HL_TABLE()[idx].sg_cterm_fg = color + 1; - if (is_normal_group) { - cterm_normal_fg_color = color + 1; - } - } else { - HL_TABLE()[idx].sg_cterm_bg = color + 1; - if (is_normal_group) { - cterm_normal_bg_color = color + 1; - if (!ui_rgb_attached()) { - if (color >= 0) { - int dark = -1; - - if (t_colors < 16) { - dark = (color == 0 || color == 4); - } else if (color < 16) { - // Limit the heuristic to the standard 16 colors - dark = (color < 7 || color == 8); - } - // Set the 'background' option if the value is - // wrong. - if (dark != -1 - && dark != (*p_bg == 'd') - && !option_was_set("bg")) { - set_option_value("bg", 0L, (dark ? "dark" : "light"), 0); - reset_option_was_set("bg"); - } - } - } - } - } - } - } else if (strcmp(key, "GUIFG") == 0) { - char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_fg = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_fg = HL_TABLE()[idx].sg_rgb_fg; - } - } else if (STRCMP(key, "GUIBG") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (STRCMP(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_bg = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_bg = HL_TABLE()[idx].sg_rgb_bg; - } - } else if (strcmp(key, "GUISP") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; - - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; - } - - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_sp = -1; - } - did_change = true; - } - } - - if (is_normal_group) { - normal_sp = HL_TABLE()[idx].sg_rgb_sp; - } - } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { - // Ignored for now - } else if (strcmp(key, "BLEND") == 0) { - if (strcmp(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_blend = strtol(arg, NULL, 10); - } else { - HL_TABLE()[idx].sg_blend = -1; - } - } else { - semsg(_("E423: Illegal argument: %s"), key_start); - error = true; - break; - } - HL_TABLE()[idx].sg_cleared = false; - - // When highlighting has been given for a group, don't link it. - if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) { - HL_TABLE()[idx].sg_link = 0; - } - - // Continue with next argument. - linep = (const char *)skipwhite((const char_u *)linep); - } - } - - // If there is an error, and it's a new entry, remove it from the table. - if (error && idx == highlight_ga.ga_len) { - syn_unadd_group(); - } else { - if (!error && is_normal_group) { - // Need to update all groups, because they might be using "bg" and/or - // "fg", which have been changed now. - highlight_attr_set_all(); - - if (!ui_has(kUILinegrid) && starting == 0) { - // Older UIs assume that we clear the screen after normal group is - // changed - ui_refresh(); - } else { - // TUI and newer UIs will repaint the screen themselves. NOT_VALID - // redraw below will still handle usages of guibg=fg etc. - ui_default_colors_set(); - } - did_highlight_changed = true; - redraw_all_later(NOT_VALID); - } else { - set_hl_attr(idx); - } - HL_TABLE()[idx].sg_script_ctx = current_sctx; - HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); - } - xfree(key); - xfree(arg); - - // Only call highlight_changed() once, after a sequence of highlight - // commands, and only if an attribute actually changed - if ((did_change - || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) - && !did_highlight_changed) { - // Do not trigger a redraw when highlighting is changed while - // redrawing. This may happen when evaluating 'statusline' changes the - // StatusLine group. - if (!updating_screen) { - redraw_all_later(NOT_VALID); - } - need_highlight_changed = true; - } -} - -#if defined(EXITFREE) -void free_highlight(void) -{ - for (int i = 0; i < highlight_ga.ga_len; ++i) { - highlight_clear(i); - xfree(HL_TABLE()[i].sg_name); - xfree(HL_TABLE()[i].sg_name_u); - } - ga_clear(&highlight_ga); - map_destroy(cstr_t, int)(&highlight_unames); -} - -#endif - -/* - * Reset the cterm colors to what they were before Vim was started, if - * possible. Otherwise reset them to zero. - */ -void restore_cterm_colors(void) -{ - normal_fg = -1; - normal_bg = -1; - normal_sp = -1; - cterm_normal_fg_color = 0; - cterm_normal_bg_color = 0; -} - -/// @param check_link if true also check for an existing link. -/// -/// @return TRUE if highlight group "idx" has any settings. -static int hl_has_settings(int idx, bool check_link) -{ - return HL_TABLE()[idx].sg_cleared == 0 - && (HL_TABLE()[idx].sg_attr != 0 - || HL_TABLE()[idx].sg_cterm_fg != 0 - || HL_TABLE()[idx].sg_cterm_bg != 0 - || HL_TABLE()[idx].sg_rgb_fg_name != NULL - || HL_TABLE()[idx].sg_rgb_bg_name != NULL - || HL_TABLE()[idx].sg_rgb_sp_name != NULL - || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); -} - -/* - * Clear highlighting for one group. - */ -static void highlight_clear(int idx) -{ - HL_TABLE()[idx].sg_cleared = true; - - HL_TABLE()[idx].sg_attr = 0; - HL_TABLE()[idx].sg_cterm = 0; - HL_TABLE()[idx].sg_cterm_bold = false; - HL_TABLE()[idx].sg_cterm_fg = 0; - HL_TABLE()[idx].sg_cterm_bg = 0; - HL_TABLE()[idx].sg_gui = 0; - HL_TABLE()[idx].sg_rgb_fg = -1; - HL_TABLE()[idx].sg_rgb_bg = -1; - HL_TABLE()[idx].sg_rgb_sp = -1; - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); - HL_TABLE()[idx].sg_blend = -1; - // Restore default link and context if they exist. Otherwise clears. - HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; - // Since we set the default link, set the location to where the default - // link was set. - HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; -} - - -/// \addtogroup LIST_XXX -/// @{ -#define LIST_ATTR 1 -#define LIST_STRING 2 -#define LIST_INT 3 -/// @} - -static void highlight_list_one(const int id) -{ - struct hl_group *const sgp = &HL_TABLE()[id - 1]; // index is ID minus one - bool didh = false; - - if (message_filtered(sgp->sg_name)) { - return; - } - - didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_cterm, NULL, "cterm"); - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_fg, NULL, "ctermfg"); - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_cterm_bg, NULL, "ctermbg"); - - didh = highlight_list_arg(id, didh, LIST_ATTR, - sgp->sg_gui, NULL, "gui"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_fg_name, "guifg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_bg_name, "guibg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_sp_name, "guisp"); - - didh = highlight_list_arg(id, didh, LIST_INT, - sgp->sg_blend+1, NULL, "blend"); - - if (sgp->sg_link && !got_int) { - (void)syn_list_header(didh, 0, id, true); - didh = true; - msg_puts_attr("links to", HL_ATTR(HLF_D)); - msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); - } - - if (!didh) { - highlight_list_arg(id, didh, LIST_STRING, 0, "cleared", ""); - } - if (p_verbose > 0) { - last_set_msg(sgp->sg_script_ctx); - } -} - -Dictionary get_global_hl_defs(void) -{ - Dictionary rv = ARRAY_DICT_INIT; - for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { - Dictionary attrs = ARRAY_DICT_INIT; - struct hl_group *h = &HL_TABLE()[i - 1]; - if (h->sg_attr > 0) { - attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); - } else if (h->sg_link > 0) { - const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; - PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); - } - PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); - } - - return rv; -} - -/// Outputs a highlight when doing ":hi MyHighlight" -/// -/// @param type one of \ref LIST_XXX -/// @param iarg integer argument used if \p type == LIST_INT -/// @param sarg string used if \p type == LIST_STRING -static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, - const char *const name) -{ - char buf[100]; - - if (got_int) { - return false; - } - if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char *ts = buf; - if (type == LIST_INT) { - snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); - } else if (type == LIST_STRING) { - ts = sarg; - } else { // type == LIST_ATTR - buf[0] = NUL; - for (int i = 0; hl_attr_table[i] != 0; i++) { - if (iarg & hl_attr_table[i]) { - if (buf[0] != NUL) { - xstrlcat(buf, ",", 100); - } - xstrlcat(buf, hl_name_table[i], 100); - iarg &= ~hl_attr_table[i]; // don't want "inverse" - } - } - } - - (void)syn_list_header(didh, (int)(vim_strsize((char_u *)ts) + STRLEN(name) - + 1), id, false); - didh = true; - if (!got_int) { - if (*name != NUL) { - msg_puts_attr(name, HL_ATTR(HLF_D)); - msg_puts_attr("=", HL_ATTR(HLF_D)); - } - msg_outtrans((char_u *)ts); - } - } - return didh; -} - -/// Check whether highlight group has attribute -/// -/// @param[in] id Highlight group to check. -/// @param[in] flag Attribute to check. -/// @param[in] modec 'g' for GUI, 'c' for term. -/// -/// @return "1" if highlight group has attribute, NULL otherwise. -const char *highlight_has_attr(const int id, const int flag, const int modec) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE -{ - int attr; - - if (id <= 0 || id > highlight_ga.ga_len) { - return NULL; - } - - if (modec == 'g') { - attr = HL_TABLE()[id - 1].sg_gui; - } else { - attr = HL_TABLE()[id - 1].sg_cterm; - } - - return (attr & flag) ? "1" : NULL; -} - -/// Return color name of the given highlight group -/// -/// @param[in] id Highlight group to work with. -/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", -/// "bg#" or "sp#". -/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. -/// -/// @return color name, possibly in a static buffer. Buffer will be overwritten -/// on next highlight_color() call. May return NULL. -const char *highlight_color(const int id, const char *const what, const int modec) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL -{ - static char name[20]; - int n; - bool fg = false; - bool sp = false; - bool font = false; - - if (id <= 0 || id > highlight_ga.ga_len) { - return NULL; - } - - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { - fg = true; - } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { - font = true; - } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { - sp = true; - } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { - return NULL; - } - if (modec == 'g') { - if (what[2] == '#' && ui_rgb_attached()) { - if (fg) { - n = HL_TABLE()[id - 1].sg_rgb_fg; - } else if (sp) { - n = HL_TABLE()[id - 1].sg_rgb_sp; - } else { - n = HL_TABLE()[id - 1].sg_rgb_bg; - } - if (n < 0 || n > 0xffffff) { - return NULL; - } - snprintf(name, sizeof(name), "#%06x", n); - return name; - } - if (fg) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; - } - if (sp) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; - } - return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; - } - if (font || sp) { - return NULL; - } - if (modec == 'c') { - if (fg) { - n = HL_TABLE()[id - 1].sg_cterm_fg - 1; - } else { - n = HL_TABLE()[id - 1].sg_cterm_bg - 1; - } - if (n < 0) { - return NULL; - } - snprintf(name, sizeof(name), "%d", n); - return name; - } - // term doesn't have color. - return NULL; -} - -/// Output the syntax list header. -/// -/// @param did_header did header already -/// @param outlen length of string that comes -/// @param id highlight group id -/// @param force_newline always start a new line -/// @return true when started a new line. -static bool syn_list_header(const bool did_header, const int outlen, const int id, - bool force_newline) -{ - int endcol = 19; - bool newline = true; - int name_col = 0; - bool adjust = true; - - if (!did_header) { - msg_putchar('\n'); - if (got_int) { - return true; - } - msg_outtrans(HL_TABLE()[id - 1].sg_name); - name_col = msg_col; - endcol = 15; - } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { - msg_putchar(' '); - adjust = false; - } else if (msg_col + outlen + 1 >= Columns || force_newline) { - msg_putchar('\n'); - if (got_int) { - return true; - } - } else { - if (msg_col >= endcol) { // wrap around is like starting a new line - newline = false; - } - } - - if (adjust) { - if (msg_col >= endcol) { - // output at least one space - endcol = msg_col + 1; - } - - msg_advance(endcol); - } - - // Show "xxx" with the attributes. - if (!did_header) { - if (endcol == Columns - 1 && endcol <= name_col) { - msg_putchar(' '); - } - msg_puts_attr("xxx", syn_id2attr(id)); - msg_putchar(' '); - } - - return newline; -} - -/// Set the attribute numbers for a highlight group. -/// Called after one of the attributes has changed. -/// @param idx corrected highlight index -static void set_hl_attr(int idx) -{ - HlAttrs at_en = HLATTRS_INIT; - struct hl_group *sgp = HL_TABLE() + idx; - - at_en.cterm_ae_attr = sgp->sg_cterm; - at_en.cterm_fg_color = sgp->sg_cterm_fg; - at_en.cterm_bg_color = sgp->sg_cterm_bg; - at_en.rgb_ae_attr = sgp->sg_gui; - // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is - // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name - // before setting attr_entry->{f,g}g_color to a other than -1 - at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; - at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; - at_en.hl_blend = sgp->sg_blend; - - sgp->sg_attr = hl_get_syn_attr(0, idx+1, at_en); - - // a cursor style uses this syn_id, make sure its attribute is updated. - if (cursor_mode_uses_syn_id(idx+1)) { - ui_mode_info_set(); - } -} - -int syn_name2id(const char *name) - FUNC_ATTR_NONNULL_ALL -{ - return syn_name2id_len((char_u *)name, STRLEN(name)); -} - -/// Lookup a highlight group name and return its ID. -/// -/// @param highlight name e.g. 'Cursor', 'Normal' -/// @return the highlight id, else 0 if \p name does not exist -int syn_name2id_len(const char_u *name, size_t len) - FUNC_ATTR_NONNULL_ALL -{ - char name_u[MAX_SYN_NAME + 1]; - - if (len == 0 || len > MAX_SYN_NAME) { - return 0; - } - - // Avoid using stricmp() too much, it's slow on some systems */ - // Avoid alloc()/free(), these are slow too. - memcpy(name_u, name, len); - name_u[len] = '\0'; - vim_strup((char_u *)name_u); - - // map_get(..., int) returns 0 when no key is present, which is - // the expected value for missing highlight group. - return map_get(cstr_t, int)(&highlight_unames, name_u); -} - -/// Lookup a highlight group name and return its attributes. -/// Return zero if not found. -int syn_name2attr(const char_u *name) - FUNC_ATTR_NONNULL_ALL -{ - int id = syn_name2id((char *)name); - - if (id != 0) { - return syn_id2attr(id); - } - return 0; -} - -/* - * Return TRUE if highlight group "name" exists. - */ -int highlight_exists(const char *name) -{ - return syn_name2id(name) > 0; -} - -/* - * Return the name of highlight group "id". - * When not a valid ID return an empty string. - */ -char_u *syn_id2name(int id) -{ - if (id <= 0 || id > highlight_ga.ga_len) { - return (char_u *)""; - } - return HL_TABLE()[id - 1].sg_name; -} - - -/// Find highlight group name in the table and return its ID. -/// If it doesn't exist yet, a new entry is created. -/// -/// @param pp Highlight group name -/// @param len length of \p pp -/// -/// @return 0 for failure else the id of the group -int syn_check_group(const char *name, int len) -{ - if (len > MAX_SYN_NAME) { - emsg(_(e_highlight_group_name_too_long)); - return 0; - } - int id = syn_name2id_len((char_u *)name, len); - if (id == 0) { // doesn't exist yet - return syn_add_group(vim_strnsave((char_u *)name, len)); - } - return id; -} - -/// Add new highlight group and return its ID. -/// -/// @param name must be an allocated string, it will be consumed. -/// @return 0 for failure, else the allocated group id -/// @see syn_check_group syn_unadd_group -static int syn_add_group(char_u *name) -{ - char_u *p; - - // Check that the name is ASCII letters, digits and underscore. - for (p = name; *p != NUL; ++p) { - if (!vim_isprintc(*p)) { - emsg(_("E669: Unprintable character in group name")); - xfree(name); - return 0; - } else if (!ASCII_ISALNUM(*p) && *p != '_') { - /* This is an error, but since there previously was no check only - * give a warning. */ - msg_source(HL_ATTR(HLF_W)); - msg(_("W18: Invalid character in group name")); - break; - } - } - - /* - * First call for this growarray: init growing array. - */ - if (highlight_ga.ga_data == NULL) { - highlight_ga.ga_itemsize = sizeof(struct hl_group); - ga_set_growsize(&highlight_ga, 10); - } - - if (highlight_ga.ga_len >= MAX_HL_ID) { - emsg(_("E849: Too many highlight and syntax groups")); - xfree(name); - return 0; - } - - char *const name_up = (char *)vim_strsave_up(name); - - // Append another syntax_highlight entry. - struct hl_group *hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); - memset(hlgp, 0, sizeof(*hlgp)); - hlgp->sg_name = name; - hlgp->sg_rgb_bg = -1; - hlgp->sg_rgb_fg = -1; - hlgp->sg_rgb_sp = -1; - hlgp->sg_blend = -1; - hlgp->sg_name_u = name_up; - - int id = highlight_ga.ga_len; // ID is index plus one - - map_put(cstr_t, int)(&highlight_unames, name_up, id); - - return id; -} - -/// When, just after calling syn_add_group(), an error is discovered, this -/// function deletes the new name. -static void syn_unadd_group(void) -{ - highlight_ga.ga_len--; - HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; - map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); - xfree(item->sg_name); - xfree(item->sg_name_u); -} - - -/// Translate a group ID to highlight attributes. -/// @see syn_attr2entry -int syn_id2attr(int hl_id) -{ - hl_id = syn_get_final_id(hl_id); - struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one - - int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); - if (attr >= 0) { - return attr; - } - return sgp->sg_attr; -} - - -/* - * Translate a group ID to the final group ID (following links). - */ -int syn_get_final_id(int hl_id) -{ - int count; - - if (hl_id > highlight_ga.ga_len || hl_id < 1) { - return 0; // Can be called from eval!! - } - /* - * Follow links until there is no more. - * Look out for loops! Break after 100 links. - */ - for (count = 100; --count >= 0;) { - struct hl_group *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one - - // ACHTUNG: when using "tmp" attribute (no link) the function might be - // called twice. it needs be smart enough to remember attr only to - // syn_id2attr time - int check = ns_get_hl(-1, hl_id, true, sgp->sg_set); - if (check == 0) { - return hl_id; // how dare! it broke the link! - } else if (check > 0) { - hl_id = check; - continue; - } - - - if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) { - break; - } - hl_id = sgp->sg_link; - } - - return hl_id; -} - -/// Refresh the color attributes of all highlight groups. -void highlight_attr_set_all(void) -{ - for (int idx = 0; idx < highlight_ga.ga_len; idx++) { - struct hl_group *sgp = &HL_TABLE()[idx]; - if (sgp->sg_rgb_bg_name != NULL) { - sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); - } - if (sgp->sg_rgb_fg_name != NULL) { - sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); - } - if (sgp->sg_rgb_sp_name != NULL) { - sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); - } - set_hl_attr(idx); - } -} - -// Apply difference between User[1-9] and HLF_S to HLF_SNC. -static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) - FUNC_ATTR_NONNULL_ALL -{ - struct hl_group *const hlt = HL_TABLE(); - - if (id_alt == 0) { - memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group)); - hlt[hlcnt + i].sg_cterm = highlight_attr[hlf]; - hlt[hlcnt + i].sg_gui = highlight_attr[hlf]; - } else { - memmove(&hlt[hlcnt + i], &hlt[id_alt - 1], sizeof(struct hl_group)); - } - hlt[hlcnt + i].sg_link = 0; - - hlt[hlcnt + i].sg_cterm ^= hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm; - if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg) { - hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg; - } - if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg) { - hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg; - } - hlt[hlcnt + i].sg_gui ^= hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui; - if (hlt[id - 1].sg_rgb_fg != hlt[id_S - 1].sg_rgb_fg) { - hlt[hlcnt + i].sg_rgb_fg = hlt[id - 1].sg_rgb_fg; - } - if (hlt[id - 1].sg_rgb_bg != hlt[id_S - 1].sg_rgb_bg) { - hlt[hlcnt + i].sg_rgb_bg = hlt[id - 1].sg_rgb_bg; - } - if (hlt[id - 1].sg_rgb_sp != hlt[id_S - 1].sg_rgb_sp) { - hlt[hlcnt + i].sg_rgb_sp = hlt[id - 1].sg_rgb_sp; - } - highlight_ga.ga_len = hlcnt + i + 1; - set_hl_attr(hlcnt + i); // At long last we can apply - table[i] = syn_id2attr(hlcnt + i + 1); -} - -/// Translate highlight groups into attributes in highlight_attr[] and set up -/// the user highlights User1..9. A set of corresponding highlights to use on -/// top of HLF_SNC is computed. Called only when nvim starts and upon first -/// screen redraw after any :highlight command. -void highlight_changed(void) -{ - int id; - char userhl[30]; // use 30 to avoid compiler warning - int id_S = -1; - int id_SNC = 0; - int hlcnt; - - need_highlight_changed = false; - - /// Translate builtin highlight groups into attributes for quick lookup. - for (int hlf = 0; hlf < HLF_COUNT; hlf++) { - id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf])); - if (id == 0) { - abort(); - } - int final_id = syn_get_final_id(id); - if (hlf == HLF_SNC) { - id_SNC = final_id; - } else if (hlf == HLF_S) { - id_S = final_id; - } - - highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, - hlf == HLF_INACTIVE); - - if (highlight_attr[hlf] != highlight_attr_last[hlf]) { - if (hlf == HLF_MSG) { - clear_cmdline = true; - } - ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]), - highlight_attr[hlf]); - highlight_attr_last[hlf] = highlight_attr[hlf]; - } - } - - // - // Setup the user highlights - // - // Temporarily utilize 10 more hl entries: - // 9 for User1-User9 combined with StatusLineNC - // 1 for StatusLine default - // Must to be in there simultaneously in case of table overflows in - // get_attr_entry() - ga_grow(&highlight_ga, 10); - hlcnt = highlight_ga.ga_len; - if (id_S == -1) { - // Make sure id_S is always valid to simplify code below. Use the last entry - memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group)); - id_S = hlcnt + 10; - } - for (int i = 0; i < 9; i++) { - snprintf(userhl, sizeof(userhl), "User%d", i + 1); - id = syn_name2id(userhl); - if (id == 0) { - highlight_user[i] = 0; - highlight_stlnc[i] = 0; - } else { - highlight_user[i] = syn_id2attr(id); - combine_stl_hlt(id, id_S, id_SNC, hlcnt, i, HLF_SNC, highlight_stlnc); - } - } - highlight_ga.ga_len = hlcnt; -} - - -/* - * Handle command line completion for :highlight command. - */ -void set_context_in_highlight_cmd(expand_T *xp, const char *arg) -{ - // Default: expand group names. - xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = (char_u *)arg; - include_link = 2; - include_default = 1; - - // (part of) subcommand already typed - if (*arg != NUL) { - const char *p = (const char *)skiptowhite((const char_u *)arg); - if (*p != NUL) { // Past "default" or group name. - include_default = 0; - if (strncmp("default", arg, p - arg) == 0) { - arg = (const char *)skipwhite((const char_u *)p); - xp->xp_pattern = (char_u *)arg; - p = (const char *)skiptowhite((const char_u *)arg); - } - if (*p != NUL) { // past group name - include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') { - highlight_list(); - } - if (strncmp("link", arg, p - arg) == 0 - || strncmp("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite((const char_u *)p); - p = (const char *)skiptowhite(xp->xp_pattern); - if (*p != NUL) { // Past first group name. - xp->xp_pattern = skipwhite((const char_u *)p); - p = (const char *)skiptowhite(xp->xp_pattern); - } - } - if (*p != NUL) { // Past group name(s). - xp->xp_context = EXPAND_NOTHING; - } - } - } - } -} - -/* - * List highlighting matches in a nice way. - */ -static void highlight_list(void) -{ - int i; - - for (i = 10; --i >= 0;) { - highlight_list_two(i, HL_ATTR(HLF_D)); - } - for (i = 40; --i >= 0;) { - highlight_list_two(99, 0); - } -} - -static void highlight_list_two(int cnt, int attr) -{ - msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr); - msg_clr_eos(); - ui_flush(); - os_delay(cnt == 99 ? 40L : (long)cnt * 50L, false); -} - - -/// Function given to ExpandGeneric() to obtain the list of group names. -const char *get_highlight_name(expand_T *const xp, int idx) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - return get_highlight_name_ext(xp, idx, true); -} - - -/// Obtain a highlight group name. -/// -/// @param skip_cleared if true don't return a cleared entry. -const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (idx < 0) { - return NULL; - } - - // Items are never removed from the table, skip the ones that were cleared. - if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { - return ""; - } - - if (idx == highlight_ga.ga_len && include_none != 0) { - return "none"; - } else if (idx == highlight_ga.ga_len + include_none - && include_default != 0) { - return "default"; - } else if (idx == highlight_ga.ga_len + include_none + include_default - && include_link != 0) { - return "link"; - } else if (idx == highlight_ga.ga_len + include_none + include_default + 1 - && include_link != 0) { - return "clear"; - } else if (idx >= highlight_ga.ga_len) { - return NULL; - } - return (const char *)HL_TABLE()[idx].sg_name; -} - -color_name_table_T color_name_table[] = { - // Colors from rgb.txt - { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) }, - { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) }, - { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) }, - { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) }, - { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) }, - { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) }, - { "Aqua", RGB_(0x00, 0xff, 0xff) }, - { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) }, - { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) }, - { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) }, - { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) }, - { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) }, - { "Azure", RGB_(0xf0, 0xff, 0xff) }, - { "Azure1", RGB_(0xf0, 0xff, 0xff) }, - { "Azure2", RGB_(0xe0, 0xee, 0xee) }, - { "Azure3", RGB_(0xc1, 0xcd, 0xcd) }, - { "Azure4", RGB_(0x83, 0x8b, 0x8b) }, - { "Beige", RGB_(0xf5, 0xf5, 0xdc) }, - { "Bisque", RGB_(0xff, 0xe4, 0xc4) }, - { "Bisque1", RGB_(0xff, 0xe4, 0xc4) }, - { "Bisque2", RGB_(0xee, 0xd5, 0xb7) }, - { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) }, - { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) }, - { "Black", RGB_(0x00, 0x00, 0x00) }, - { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) }, - { "Blue", RGB_(0x00, 0x00, 0xff) }, - { "Blue1", RGB_(0x0, 0x0, 0xff) }, - { "Blue2", RGB_(0x0, 0x0, 0xee) }, - { "Blue3", RGB_(0x0, 0x0, 0xcd) }, - { "Blue4", RGB_(0x0, 0x0, 0x8b) }, - { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) }, - { "Brown", RGB_(0xa5, 0x2a, 0x2a) }, - { "Brown1", RGB_(0xff, 0x40, 0x40) }, - { "Brown2", RGB_(0xee, 0x3b, 0x3b) }, - { "Brown3", RGB_(0xcd, 0x33, 0x33) }, - { "Brown4", RGB_(0x8b, 0x23, 0x23) }, - { "BurlyWood", RGB_(0xde, 0xb8, 0x87) }, - { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) }, - { "Burlywood2", RGB_(0xee, 0xc5, 0x91) }, - { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) }, - { "Burlywood4", RGB_(0x8b, 0x73, 0x55) }, - { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) }, - { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) }, - { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) }, - { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) }, - { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) }, - { "ChartReuse", RGB_(0x7f, 0xff, 0x00) }, - { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) }, - { "Chartreuse2", RGB_(0x76, 0xee, 0x0) }, - { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) }, - { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) }, - { "Chocolate", RGB_(0xd2, 0x69, 0x1e) }, - { "Chocolate1", RGB_(0xff, 0x7f, 0x24) }, - { "Chocolate2", RGB_(0xee, 0x76, 0x21) }, - { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) }, - { "Chocolate4", RGB_(0x8b, 0x45, 0x13) }, - { "Coral", RGB_(0xff, 0x7f, 0x50) }, - { "Coral1", RGB_(0xff, 0x72, 0x56) }, - { "Coral2", RGB_(0xee, 0x6a, 0x50) }, - { "Coral3", RGB_(0xcd, 0x5b, 0x45) }, - { "Coral4", RGB_(0x8b, 0x3e, 0x2f) }, - { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) }, - { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) }, - { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) }, - { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) }, - { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) }, - { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) }, - { "Crimson", RGB_(0xdc, 0x14, 0x3c) }, - { "Cyan", RGB_(0x00, 0xff, 0xff) }, - { "Cyan1", RGB_(0x0, 0xff, 0xff) }, - { "Cyan2", RGB_(0x0, 0xee, 0xee) }, - { "Cyan3", RGB_(0x0, 0xcd, 0xcd) }, - { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, - { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, - { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, - { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, - { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, - { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, - { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, - { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) }, - { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) }, - { "DarkGreen", RGB_(0x00, 0x64, 0x00) }, - { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) }, - { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) }, - { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) }, - { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) }, - { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) }, - { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) }, - { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) }, - { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) }, - { "DarkOrange", RGB_(0xff, 0x8c, 0x00) }, - { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) }, - { "DarkOrange2", RGB_(0xee, 0x76, 0x0) }, - { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) }, - { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) }, - { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) }, - { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) }, - { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) }, - { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) }, - { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) }, - { "DarkRed", RGB_(0x8b, 0x00, 0x00) }, - { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) }, - { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) }, - { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) }, - { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) }, - { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) }, - { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) }, - { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) }, - { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) }, - { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) }, - { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) }, - { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) }, - { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) }, - { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) }, - { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) }, - { "DarkViolet", RGB_(0x94, 0x00, 0xd3) }, - { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) }, - { "DeepPink", RGB_(0xff, 0x14, 0x93) }, - { "DeepPink1", RGB_(0xff, 0x14, 0x93) }, - { "DeepPink2", RGB_(0xee, 0x12, 0x89) }, - { "DeepPink3", RGB_(0xcd, 0x10, 0x76) }, - { "DeepPink4", RGB_(0x8b, 0xa, 0x50) }, - { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) }, - { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) }, - { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) }, - { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) }, - { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) }, - { "DimGray", RGB_(0x69, 0x69, 0x69) }, - { "DimGrey", RGB_(0x69, 0x69, 0x69) }, - { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) }, - { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) }, - { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) }, - { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) }, - { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) }, - { "Firebrick", RGB_(0xb2, 0x22, 0x22) }, - { "Firebrick1", RGB_(0xff, 0x30, 0x30) }, - { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) }, - { "Firebrick3", RGB_(0xcd, 0x26, 0x26) }, - { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) }, - { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) }, - { "ForestGreen", RGB_(0x22, 0x8b, 0x22) }, - { "Fuchsia", RGB_(0xff, 0x00, 0xff) }, - { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) }, - { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) }, - { "Gold", RGB_(0xff, 0xd7, 0x00) }, - { "Gold1", RGB_(0xff, 0xd7, 0x0) }, - { "Gold2", RGB_(0xee, 0xc9, 0x0) }, - { "Gold3", RGB_(0xcd, 0xad, 0x0) }, - { "Gold4", RGB_(0x8b, 0x75, 0x0) }, - { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, - { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, - { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, - { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, - { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) }, - { "Gray", RGB_(0x80, 0x80, 0x80) }, - { "Gray0", RGB_(0x0, 0x0, 0x0) }, - { "Gray1", RGB_(0x3, 0x3, 0x3) }, - { "Gray10", RGB_(0x1a, 0x1a, 0x1a) }, - { "Gray100", RGB_(0xff, 0xff, 0xff) }, - { "Gray11", RGB_(0x1c, 0x1c, 0x1c) }, - { "Gray12", RGB_(0x1f, 0x1f, 0x1f) }, - { "Gray13", RGB_(0x21, 0x21, 0x21) }, - { "Gray14", RGB_(0x24, 0x24, 0x24) }, - { "Gray15", RGB_(0x26, 0x26, 0x26) }, - { "Gray16", RGB_(0x29, 0x29, 0x29) }, - { "Gray17", RGB_(0x2b, 0x2b, 0x2b) }, - { "Gray18", RGB_(0x2e, 0x2e, 0x2e) }, - { "Gray19", RGB_(0x30, 0x30, 0x30) }, - { "Gray2", RGB_(0x5, 0x5, 0x5) }, - { "Gray20", RGB_(0x33, 0x33, 0x33) }, - { "Gray21", RGB_(0x36, 0x36, 0x36) }, - { "Gray22", RGB_(0x38, 0x38, 0x38) }, - { "Gray23", RGB_(0x3b, 0x3b, 0x3b) }, - { "Gray24", RGB_(0x3d, 0x3d, 0x3d) }, - { "Gray25", RGB_(0x40, 0x40, 0x40) }, - { "Gray26", RGB_(0x42, 0x42, 0x42) }, - { "Gray27", RGB_(0x45, 0x45, 0x45) }, - { "Gray28", RGB_(0x47, 0x47, 0x47) }, - { "Gray29", RGB_(0x4a, 0x4a, 0x4a) }, - { "Gray3", RGB_(0x8, 0x8, 0x8) }, - { "Gray30", RGB_(0x4d, 0x4d, 0x4d) }, - { "Gray31", RGB_(0x4f, 0x4f, 0x4f) }, - { "Gray32", RGB_(0x52, 0x52, 0x52) }, - { "Gray33", RGB_(0x54, 0x54, 0x54) }, - { "Gray34", RGB_(0x57, 0x57, 0x57) }, - { "Gray35", RGB_(0x59, 0x59, 0x59) }, - { "Gray36", RGB_(0x5c, 0x5c, 0x5c) }, - { "Gray37", RGB_(0x5e, 0x5e, 0x5e) }, - { "Gray38", RGB_(0x61, 0x61, 0x61) }, - { "Gray39", RGB_(0x63, 0x63, 0x63) }, - { "Gray4", RGB_(0xa, 0xa, 0xa) }, - { "Gray40", RGB_(0x66, 0x66, 0x66) }, - { "Gray41", RGB_(0x69, 0x69, 0x69) }, - { "Gray42", RGB_(0x6b, 0x6b, 0x6b) }, - { "Gray43", RGB_(0x6e, 0x6e, 0x6e) }, - { "Gray44", RGB_(0x70, 0x70, 0x70) }, - { "Gray45", RGB_(0x73, 0x73, 0x73) }, - { "Gray46", RGB_(0x75, 0x75, 0x75) }, - { "Gray47", RGB_(0x78, 0x78, 0x78) }, - { "Gray48", RGB_(0x7a, 0x7a, 0x7a) }, - { "Gray49", RGB_(0x7d, 0x7d, 0x7d) }, - { "Gray5", RGB_(0xd, 0xd, 0xd) }, - { "Gray50", RGB_(0x7f, 0x7f, 0x7f) }, - { "Gray51", RGB_(0x82, 0x82, 0x82) }, - { "Gray52", RGB_(0x85, 0x85, 0x85) }, - { "Gray53", RGB_(0x87, 0x87, 0x87) }, - { "Gray54", RGB_(0x8a, 0x8a, 0x8a) }, - { "Gray55", RGB_(0x8c, 0x8c, 0x8c) }, - { "Gray56", RGB_(0x8f, 0x8f, 0x8f) }, - { "Gray57", RGB_(0x91, 0x91, 0x91) }, - { "Gray58", RGB_(0x94, 0x94, 0x94) }, - { "Gray59", RGB_(0x96, 0x96, 0x96) }, - { "Gray6", RGB_(0xf, 0xf, 0xf) }, - { "Gray60", RGB_(0x99, 0x99, 0x99) }, - { "Gray61", RGB_(0x9c, 0x9c, 0x9c) }, - { "Gray62", RGB_(0x9e, 0x9e, 0x9e) }, - { "Gray63", RGB_(0xa1, 0xa1, 0xa1) }, - { "Gray64", RGB_(0xa3, 0xa3, 0xa3) }, - { "Gray65", RGB_(0xa6, 0xa6, 0xa6) }, - { "Gray66", RGB_(0xa8, 0xa8, 0xa8) }, - { "Gray67", RGB_(0xab, 0xab, 0xab) }, - { "Gray68", RGB_(0xad, 0xad, 0xad) }, - { "Gray69", RGB_(0xb0, 0xb0, 0xb0) }, - { "Gray7", RGB_(0x12, 0x12, 0x12) }, - { "Gray70", RGB_(0xb3, 0xb3, 0xb3) }, - { "Gray71", RGB_(0xb5, 0xb5, 0xb5) }, - { "Gray72", RGB_(0xb8, 0xb8, 0xb8) }, - { "Gray73", RGB_(0xba, 0xba, 0xba) }, - { "Gray74", RGB_(0xbd, 0xbd, 0xbd) }, - { "Gray75", RGB_(0xbf, 0xbf, 0xbf) }, - { "Gray76", RGB_(0xc2, 0xc2, 0xc2) }, - { "Gray77", RGB_(0xc4, 0xc4, 0xc4) }, - { "Gray78", RGB_(0xc7, 0xc7, 0xc7) }, - { "Gray79", RGB_(0xc9, 0xc9, 0xc9) }, - { "Gray8", RGB_(0x14, 0x14, 0x14) }, - { "Gray80", RGB_(0xcc, 0xcc, 0xcc) }, - { "Gray81", RGB_(0xcf, 0xcf, 0xcf) }, - { "Gray82", RGB_(0xd1, 0xd1, 0xd1) }, - { "Gray83", RGB_(0xd4, 0xd4, 0xd4) }, - { "Gray84", RGB_(0xd6, 0xd6, 0xd6) }, - { "Gray85", RGB_(0xd9, 0xd9, 0xd9) }, - { "Gray86", RGB_(0xdb, 0xdb, 0xdb) }, - { "Gray87", RGB_(0xde, 0xde, 0xde) }, - { "Gray88", RGB_(0xe0, 0xe0, 0xe0) }, - { "Gray89", RGB_(0xe3, 0xe3, 0xe3) }, - { "Gray9", RGB_(0x17, 0x17, 0x17) }, - { "Gray90", RGB_(0xe5, 0xe5, 0xe5) }, - { "Gray91", RGB_(0xe8, 0xe8, 0xe8) }, - { "Gray92", RGB_(0xeb, 0xeb, 0xeb) }, - { "Gray93", RGB_(0xed, 0xed, 0xed) }, - { "Gray94", RGB_(0xf0, 0xf0, 0xf0) }, - { "Gray95", RGB_(0xf2, 0xf2, 0xf2) }, - { "Gray96", RGB_(0xf5, 0xf5, 0xf5) }, - { "Gray97", RGB_(0xf7, 0xf7, 0xf7) }, - { "Gray98", RGB_(0xfa, 0xfa, 0xfa) }, - { "Gray99", RGB_(0xfc, 0xfc, 0xfc) }, - { "Green", RGB_(0x00, 0x80, 0x00) }, - { "Green1", RGB_(0x0, 0xff, 0x0) }, - { "Green2", RGB_(0x0, 0xee, 0x0) }, - { "Green3", RGB_(0x0, 0xcd, 0x0) }, - { "Green4", RGB_(0x0, 0x8b, 0x0) }, - { "GreenYellow", RGB_(0xad, 0xff, 0x2f) }, - { "Grey", RGB_(0x80, 0x80, 0x80) }, - { "Grey0", RGB_(0x0, 0x0, 0x0) }, - { "Grey1", RGB_(0x3, 0x3, 0x3) }, - { "Grey10", RGB_(0x1a, 0x1a, 0x1a) }, - { "Grey100", RGB_(0xff, 0xff, 0xff) }, - { "Grey11", RGB_(0x1c, 0x1c, 0x1c) }, - { "Grey12", RGB_(0x1f, 0x1f, 0x1f) }, - { "Grey13", RGB_(0x21, 0x21, 0x21) }, - { "Grey14", RGB_(0x24, 0x24, 0x24) }, - { "Grey15", RGB_(0x26, 0x26, 0x26) }, - { "Grey16", RGB_(0x29, 0x29, 0x29) }, - { "Grey17", RGB_(0x2b, 0x2b, 0x2b) }, - { "Grey18", RGB_(0x2e, 0x2e, 0x2e) }, - { "Grey19", RGB_(0x30, 0x30, 0x30) }, - { "Grey2", RGB_(0x5, 0x5, 0x5) }, - { "Grey20", RGB_(0x33, 0x33, 0x33) }, - { "Grey21", RGB_(0x36, 0x36, 0x36) }, - { "Grey22", RGB_(0x38, 0x38, 0x38) }, - { "Grey23", RGB_(0x3b, 0x3b, 0x3b) }, - { "Grey24", RGB_(0x3d, 0x3d, 0x3d) }, - { "Grey25", RGB_(0x40, 0x40, 0x40) }, - { "Grey26", RGB_(0x42, 0x42, 0x42) }, - { "Grey27", RGB_(0x45, 0x45, 0x45) }, - { "Grey28", RGB_(0x47, 0x47, 0x47) }, - { "Grey29", RGB_(0x4a, 0x4a, 0x4a) }, - { "Grey3", RGB_(0x8, 0x8, 0x8) }, - { "Grey30", RGB_(0x4d, 0x4d, 0x4d) }, - { "Grey31", RGB_(0x4f, 0x4f, 0x4f) }, - { "Grey32", RGB_(0x52, 0x52, 0x52) }, - { "Grey33", RGB_(0x54, 0x54, 0x54) }, - { "Grey34", RGB_(0x57, 0x57, 0x57) }, - { "Grey35", RGB_(0x59, 0x59, 0x59) }, - { "Grey36", RGB_(0x5c, 0x5c, 0x5c) }, - { "Grey37", RGB_(0x5e, 0x5e, 0x5e) }, - { "Grey38", RGB_(0x61, 0x61, 0x61) }, - { "Grey39", RGB_(0x63, 0x63, 0x63) }, - { "Grey4", RGB_(0xa, 0xa, 0xa) }, - { "Grey40", RGB_(0x66, 0x66, 0x66) }, - { "Grey41", RGB_(0x69, 0x69, 0x69) }, - { "Grey42", RGB_(0x6b, 0x6b, 0x6b) }, - { "Grey43", RGB_(0x6e, 0x6e, 0x6e) }, - { "Grey44", RGB_(0x70, 0x70, 0x70) }, - { "Grey45", RGB_(0x73, 0x73, 0x73) }, - { "Grey46", RGB_(0x75, 0x75, 0x75) }, - { "Grey47", RGB_(0x78, 0x78, 0x78) }, - { "Grey48", RGB_(0x7a, 0x7a, 0x7a) }, - { "Grey49", RGB_(0x7d, 0x7d, 0x7d) }, - { "Grey5", RGB_(0xd, 0xd, 0xd) }, - { "Grey50", RGB_(0x7f, 0x7f, 0x7f) }, - { "Grey51", RGB_(0x82, 0x82, 0x82) }, - { "Grey52", RGB_(0x85, 0x85, 0x85) }, - { "Grey53", RGB_(0x87, 0x87, 0x87) }, - { "Grey54", RGB_(0x8a, 0x8a, 0x8a) }, - { "Grey55", RGB_(0x8c, 0x8c, 0x8c) }, - { "Grey56", RGB_(0x8f, 0x8f, 0x8f) }, - { "Grey57", RGB_(0x91, 0x91, 0x91) }, - { "Grey58", RGB_(0x94, 0x94, 0x94) }, - { "Grey59", RGB_(0x96, 0x96, 0x96) }, - { "Grey6", RGB_(0xf, 0xf, 0xf) }, - { "Grey60", RGB_(0x99, 0x99, 0x99) }, - { "Grey61", RGB_(0x9c, 0x9c, 0x9c) }, - { "Grey62", RGB_(0x9e, 0x9e, 0x9e) }, - { "Grey63", RGB_(0xa1, 0xa1, 0xa1) }, - { "Grey64", RGB_(0xa3, 0xa3, 0xa3) }, - { "Grey65", RGB_(0xa6, 0xa6, 0xa6) }, - { "Grey66", RGB_(0xa8, 0xa8, 0xa8) }, - { "Grey67", RGB_(0xab, 0xab, 0xab) }, - { "Grey68", RGB_(0xad, 0xad, 0xad) }, - { "Grey69", RGB_(0xb0, 0xb0, 0xb0) }, - { "Grey7", RGB_(0x12, 0x12, 0x12) }, - { "Grey70", RGB_(0xb3, 0xb3, 0xb3) }, - { "Grey71", RGB_(0xb5, 0xb5, 0xb5) }, - { "Grey72", RGB_(0xb8, 0xb8, 0xb8) }, - { "Grey73", RGB_(0xba, 0xba, 0xba) }, - { "Grey74", RGB_(0xbd, 0xbd, 0xbd) }, - { "Grey75", RGB_(0xbf, 0xbf, 0xbf) }, - { "Grey76", RGB_(0xc2, 0xc2, 0xc2) }, - { "Grey77", RGB_(0xc4, 0xc4, 0xc4) }, - { "Grey78", RGB_(0xc7, 0xc7, 0xc7) }, - { "Grey79", RGB_(0xc9, 0xc9, 0xc9) }, - { "Grey8", RGB_(0x14, 0x14, 0x14) }, - { "Grey80", RGB_(0xcc, 0xcc, 0xcc) }, - { "Grey81", RGB_(0xcf, 0xcf, 0xcf) }, - { "Grey82", RGB_(0xd1, 0xd1, 0xd1) }, - { "Grey83", RGB_(0xd4, 0xd4, 0xd4) }, - { "Grey84", RGB_(0xd6, 0xd6, 0xd6) }, - { "Grey85", RGB_(0xd9, 0xd9, 0xd9) }, - { "Grey86", RGB_(0xdb, 0xdb, 0xdb) }, - { "Grey87", RGB_(0xde, 0xde, 0xde) }, - { "Grey88", RGB_(0xe0, 0xe0, 0xe0) }, - { "Grey89", RGB_(0xe3, 0xe3, 0xe3) }, - { "Grey9", RGB_(0x17, 0x17, 0x17) }, - { "Grey90", RGB_(0xe5, 0xe5, 0xe5) }, - { "Grey91", RGB_(0xe8, 0xe8, 0xe8) }, - { "Grey92", RGB_(0xeb, 0xeb, 0xeb) }, - { "Grey93", RGB_(0xed, 0xed, 0xed) }, - { "Grey94", RGB_(0xf0, 0xf0, 0xf0) }, - { "Grey95", RGB_(0xf2, 0xf2, 0xf2) }, - { "Grey96", RGB_(0xf5, 0xf5, 0xf5) }, - { "Grey97", RGB_(0xf7, 0xf7, 0xf7) }, - { "Grey98", RGB_(0xfa, 0xfa, 0xfa) }, - { "Grey99", RGB_(0xfc, 0xfc, 0xfc) }, - { "Honeydew", RGB_(0xf0, 0xff, 0xf0) }, - { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) }, - { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) }, - { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) }, - { "Honeydew4", RGB_(0x83, 0x8b, 0x83) }, - { "HotPink", RGB_(0xff, 0x69, 0xb4) }, - { "HotPink1", RGB_(0xff, 0x6e, 0xb4) }, - { "HotPink2", RGB_(0xee, 0x6a, 0xa7) }, - { "HotPink3", RGB_(0xcd, 0x60, 0x90) }, - { "HotPink4", RGB_(0x8b, 0x3a, 0x62) }, - { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) }, - { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) }, - { "IndianRed2", RGB_(0xee, 0x63, 0x63) }, - { "IndianRed3", RGB_(0xcd, 0x55, 0x55) }, - { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) }, - { "Indigo", RGB_(0x4b, 0x00, 0x82) }, - { "Ivory", RGB_(0xff, 0xff, 0xf0) }, - { "Ivory1", RGB_(0xff, 0xff, 0xf0) }, - { "Ivory2", RGB_(0xee, 0xee, 0xe0) }, - { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) }, - { "Ivory4", RGB_(0x8b, 0x8b, 0x83) }, - { "Khaki", RGB_(0xf0, 0xe6, 0x8c) }, - { "Khaki1", RGB_(0xff, 0xf6, 0x8f) }, - { "Khaki2", RGB_(0xee, 0xe6, 0x85) }, - { "Khaki3", RGB_(0xcd, 0xc6, 0x73) }, - { "Khaki4", RGB_(0x8b, 0x86, 0x4e) }, - { "Lavender", RGB_(0xe6, 0xe6, 0xfa) }, - { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) }, - { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) }, - { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) }, - { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) }, - { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) }, - { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) }, - { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) }, - { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) }, - { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) }, - { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) }, - { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) }, - { "LightBlue", RGB_(0xad, 0xd8, 0xe6) }, - { "LightBlue1", RGB_(0xbf, 0xef, 0xff) }, - { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) }, - { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) }, - { "LightBlue4", RGB_(0x68, 0x83, 0x8b) }, - { "LightCoral", RGB_(0xf0, 0x80, 0x80) }, - { "LightCyan", RGB_(0xe0, 0xff, 0xff) }, - { "LightCyan1", RGB_(0xe0, 0xff, 0xff) }, - { "LightCyan2", RGB_(0xd1, 0xee, 0xee) }, - { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) }, - { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) }, - { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) }, - { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) }, - { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, - { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, - { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, - { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, - { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, - { "LightGreen", RGB_(0x90, 0xee, 0x90) }, - { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, - { "LightMagenta", RGB_(0xff, 0xbb, 0xff) }, - { "LightPink", RGB_(0xff, 0xb6, 0xc1) }, - { "LightPink1", RGB_(0xff, 0xae, 0xb9) }, - { "LightPink2", RGB_(0xee, 0xa2, 0xad) }, - { "LightPink3", RGB_(0xcd, 0x8c, 0x95) }, - { "LightPink4", RGB_(0x8b, 0x5f, 0x65) }, - { "LightRed", RGB_(0xff, 0xbb, 0xbb) }, - { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) }, - { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) }, - { "LightSalmon2", RGB_(0xee, 0x95, 0x72) }, - { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) }, - { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) }, - { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) }, - { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) }, - { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) }, - { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) }, - { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) }, - { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) }, - { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) }, - { "LightSlateGray", RGB_(0x77, 0x88, 0x99) }, - { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) }, - { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) }, - { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) }, - { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) }, - { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) }, - { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) }, - { "LightYellow", RGB_(0xff, 0xff, 0xe0) }, - { "LightYellow1", RGB_(0xff, 0xff, 0xe0) }, - { "LightYellow2", RGB_(0xee, 0xee, 0xd1) }, - { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) }, - { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) }, - { "Lime", RGB_(0x00, 0xff, 0x00) }, - { "LimeGreen", RGB_(0x32, 0xcd, 0x32) }, - { "Linen", RGB_(0xfa, 0xf0, 0xe6) }, - { "Magenta", RGB_(0xff, 0x00, 0xff) }, - { "Magenta1", RGB_(0xff, 0x0, 0xff) }, - { "Magenta2", RGB_(0xee, 0x0, 0xee) }, - { "Magenta3", RGB_(0xcd, 0x0, 0xcd) }, - { "Magenta4", RGB_(0x8b, 0x0, 0x8b) }, - { "Maroon", RGB_(0x80, 0x00, 0x00) }, - { "Maroon1", RGB_(0xff, 0x34, 0xb3) }, - { "Maroon2", RGB_(0xee, 0x30, 0xa7) }, - { "Maroon3", RGB_(0xcd, 0x29, 0x90) }, - { "Maroon4", RGB_(0x8b, 0x1c, 0x62) }, - { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) }, - { "MediumBlue", RGB_(0x00, 0x00, 0xcd) }, - { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) }, - { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) }, - { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) }, - { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) }, - { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) }, - { "MediumPurple", RGB_(0x93, 0x70, 0xdb) }, - { "MediumPurple1", RGB_(0xab, 0x82, 0xff) }, - { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) }, - { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) }, - { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) }, - { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) }, - { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) }, - { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) }, - { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) }, - { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) }, - { "MidnightBlue", RGB_(0x19, 0x19, 0x70) }, - { "MintCream", RGB_(0xf5, 0xff, 0xfa) }, - { "MistyRose", RGB_(0xff, 0xe4, 0xe1) }, - { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) }, - { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) }, - { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) }, - { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) }, - { "Moccasin", RGB_(0xff, 0xe4, 0xb5) }, - { "NavajoWhite", RGB_(0xff, 0xde, 0xad) }, - { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) }, - { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) }, - { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) }, - { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, - { "Navy", RGB_(0x00, 0x00, 0x80) }, - { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, - { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, - { "Olive", RGB_(0x80, 0x80, 0x00) }, - { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, - { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) }, - { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) }, - { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) }, - { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) }, - { "Orange", RGB_(0xff, 0xa5, 0x00) }, - { "Orange1", RGB_(0xff, 0xa5, 0x0) }, - { "Orange2", RGB_(0xee, 0x9a, 0x0) }, - { "Orange3", RGB_(0xcd, 0x85, 0x0) }, - { "Orange4", RGB_(0x8b, 0x5a, 0x0) }, - { "OrangeRed", RGB_(0xff, 0x45, 0x00) }, - { "OrangeRed1", RGB_(0xff, 0x45, 0x0) }, - { "OrangeRed2", RGB_(0xee, 0x40, 0x0) }, - { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) }, - { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) }, - { "Orchid", RGB_(0xda, 0x70, 0xd6) }, - { "Orchid1", RGB_(0xff, 0x83, 0xfa) }, - { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, - { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, - { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, - { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, - { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, - { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, - { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, - { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) }, - { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) }, - { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) }, - { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) }, - { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) }, - { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) }, - { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) }, - { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) }, - { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) }, - { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) }, - { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) }, - { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) }, - { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) }, - { "PeachPuff", RGB_(0xff, 0xda, 0xb9) }, - { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) }, - { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) }, - { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) }, - { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) }, - { "Peru", RGB_(0xcd, 0x85, 0x3f) }, - { "Pink", RGB_(0xff, 0xc0, 0xcb) }, - { "Pink1", RGB_(0xff, 0xb5, 0xc5) }, - { "Pink2", RGB_(0xee, 0xa9, 0xb8) }, - { "Pink3", RGB_(0xcd, 0x91, 0x9e) }, - { "Pink4", RGB_(0x8b, 0x63, 0x6c) }, - { "Plum", RGB_(0xdd, 0xa0, 0xdd) }, - { "Plum1", RGB_(0xff, 0xbb, 0xff) }, - { "Plum2", RGB_(0xee, 0xae, 0xee) }, - { "Plum3", RGB_(0xcd, 0x96, 0xcd) }, - { "Plum4", RGB_(0x8b, 0x66, 0x8b) }, - { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) }, - { "Purple", RGB_(0x80, 0x00, 0x80) }, - { "Purple1", RGB_(0x9b, 0x30, 0xff) }, - { "Purple2", RGB_(0x91, 0x2c, 0xee) }, - { "Purple3", RGB_(0x7d, 0x26, 0xcd) }, - { "Purple4", RGB_(0x55, 0x1a, 0x8b) }, - { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) }, - { "Red", RGB_(0xff, 0x00, 0x00) }, - { "Red1", RGB_(0xff, 0x0, 0x0) }, - { "Red2", RGB_(0xee, 0x0, 0x0) }, - { "Red3", RGB_(0xcd, 0x0, 0x0) }, - { "Red4", RGB_(0x8b, 0x0, 0x0) }, - { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) }, - { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) }, - { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) }, - { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) }, - { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) }, - { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) }, - { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) }, - { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) }, - { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) }, - { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) }, - { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) }, - { "Salmon", RGB_(0xfa, 0x80, 0x72) }, - { "Salmon1", RGB_(0xff, 0x8c, 0x69) }, - { "Salmon2", RGB_(0xee, 0x82, 0x62) }, - { "Salmon3", RGB_(0xcd, 0x70, 0x54) }, - { "Salmon4", RGB_(0x8b, 0x4c, 0x39) }, - { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) }, - { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) }, - { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) }, - { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) }, - { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) }, - { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) }, - { "SeaShell", RGB_(0xff, 0xf5, 0xee) }, - { "Seashell1", RGB_(0xff, 0xf5, 0xee) }, - { "Seashell2", RGB_(0xee, 0xe5, 0xde) }, - { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) }, - { "Seashell4", RGB_(0x8b, 0x86, 0x82) }, - { "Sienna", RGB_(0xa0, 0x52, 0x2d) }, - { "Sienna1", RGB_(0xff, 0x82, 0x47) }, - { "Sienna2", RGB_(0xee, 0x79, 0x42) }, - { "Sienna3", RGB_(0xcd, 0x68, 0x39) }, - { "Sienna4", RGB_(0x8b, 0x47, 0x26) }, - { "Silver", RGB_(0xc0, 0xc0, 0xc0) }, - { "SkyBlue", RGB_(0x87, 0xce, 0xeb) }, - { "SkyBlue1", RGB_(0x87, 0xce, 0xff) }, - { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) }, - { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) }, - { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) }, - { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) }, - { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) }, - { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) }, - { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) }, - { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) }, - { "SlateGray", RGB_(0x70, 0x80, 0x90) }, - { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) }, - { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) }, - { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) }, - { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) }, - { "SlateGrey", RGB_(0x70, 0x80, 0x90) }, - { "Snow", RGB_(0xff, 0xfa, 0xfa) }, - { "Snow1", RGB_(0xff, 0xfa, 0xfa) }, - { "Snow2", RGB_(0xee, 0xe9, 0xe9) }, - { "Snow3", RGB_(0xcd, 0xc9, 0xc9) }, - { "Snow4", RGB_(0x8b, 0x89, 0x89) }, - { "SpringGreen", RGB_(0x00, 0xff, 0x7f) }, - { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) }, - { "SpringGreen2", RGB_(0x0, 0xee, 0x76) }, - { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) }, - { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) }, - { "SteelBlue", RGB_(0x46, 0x82, 0xb4) }, - { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) }, - { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) }, - { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) }, - { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) }, - { "Tan", RGB_(0xd2, 0xb4, 0x8c) }, - { "Tan1", RGB_(0xff, 0xa5, 0x4f) }, - { "Tan2", RGB_(0xee, 0x9a, 0x49) }, - { "Tan3", RGB_(0xcd, 0x85, 0x3f) }, - { "Tan4", RGB_(0x8b, 0x5a, 0x2b) }, - { "Teal", RGB_(0x00, 0x80, 0x80) }, - { "Thistle", RGB_(0xd8, 0xbf, 0xd8) }, - { "Thistle1", RGB_(0xff, 0xe1, 0xff) }, - { "Thistle2", RGB_(0xee, 0xd2, 0xee) }, - { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) }, - { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) }, - { "Tomato", RGB_(0xff, 0x63, 0x47) }, - { "Tomato1", RGB_(0xff, 0x63, 0x47) }, - { "Tomato2", RGB_(0xee, 0x5c, 0x42) }, - { "Tomato3", RGB_(0xcd, 0x4f, 0x39) }, - { "Tomato4", RGB_(0x8b, 0x36, 0x26) }, - { "Turquoise", RGB_(0x40, 0xe0, 0xd0) }, - { "Turquoise1", RGB_(0x0, 0xf5, 0xff) }, - { "Turquoise2", RGB_(0x0, 0xe5, 0xee) }, - { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) }, - { "Turquoise4", RGB_(0x0, 0x86, 0x8b) }, - { "Violet", RGB_(0xee, 0x82, 0xee) }, - { "VioletRed", RGB_(0xd0, 0x20, 0x90) }, - { "VioletRed1", RGB_(0xff, 0x3e, 0x96) }, - { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) }, - { "VioletRed3", RGB_(0xcd, 0x32, 0x78) }, - { "VioletRed4", RGB_(0x8b, 0x22, 0x52) }, - { "WebGray", RGB_(0x80, 0x80, 0x80) }, - { "WebGreen", RGB_(0x0, 0x80, 0x0) }, - { "WebGrey", RGB_(0x80, 0x80, 0x80) }, - { "WebMaroon", RGB_(0x80, 0x0, 0x0) }, - { "WebPurple", RGB_(0x80, 0x0, 0x80) }, - { "Wheat", RGB_(0xf5, 0xde, 0xb3) }, - { "Wheat1", RGB_(0xff, 0xe7, 0xba) }, - { "Wheat2", RGB_(0xee, 0xd8, 0xae) }, - { "Wheat3", RGB_(0xcd, 0xba, 0x96) }, - { "Wheat4", RGB_(0x8b, 0x7e, 0x66) }, - { "White", RGB_(0xff, 0xff, 0xff) }, - { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) }, - { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) }, - { "X11Green", RGB_(0x0, 0xff, 0x0) }, - { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) }, - { "X11Maroon", RGB_(0xb0, 0x30, 0x60) }, - { "X11Purple", RGB_(0xa0, 0x20, 0xf0) }, - { "Yellow", RGB_(0xff, 0xff, 0x00) }, - { "Yellow1", RGB_(0xff, 0xff, 0x0) }, - { "Yellow2", RGB_(0xee, 0xee, 0x0) }, - { "Yellow3", RGB_(0xcd, 0xcd, 0x0) }, - { "Yellow4", RGB_(0x8b, 0x8b, 0x0) }, - { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) }, - { NULL, 0 }, -}; - - -/// Translate to RgbValue if \p name is an hex value (e.g. #XXXXXX), -/// else look into color_name_table to translate a color name to its -/// hex value -/// -/// @param[in] name string value to convert to RGB -/// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const char *name) -{ - if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) - && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) - && isxdigit(name[6]) && name[7] == NUL) { - // rgb hex string - return strtol((char *)(name + 1), NULL, 16); - } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { - return normal_bg; - } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { - return normal_fg; - } - - for (int i = 0; color_name_table[i].name != NULL; i++) { - if (!STRICMP(name, color_name_table[i].name)) { - return color_name_table[i].color; - } - } - - return -1; -} - -int name_to_ctermcolor(const char *name) -{ - int i; - int off = TOUPPER_ASC(*name); - for (i = ARRAY_SIZE(color_names); --i >= 0;) { - if (off == color_names[i][0] - && STRICMP(name+1, color_names[i]+1) == 0) { - break; - } - } - if (i < 0) { - return -1; - } - TriState bold = kNone; - return lookup_color(i, false, &bold); -} - -/************************************** -* End of Highlighting stuff * -**************************************/ diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index 15fc084a0a..0d890314c5 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -29,12 +29,6 @@ #define SYN_GROUP_STATIC(s) syn_check_group(S_LEN(s)) -typedef struct { - char *name; - RgbValue color; -} color_name_table_T; -extern color_name_table_T color_name_table[]; - /// Array of highlight definitions, used for unit testing extern const char *const highlight_init_cmdline[]; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 5189705a36..5c8789ec37 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -55,6 +55,7 @@ #include "nvim/fileio.h" #include "nvim/getchar.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/keymap.h" #include "nvim/log.h" #include "nvim/macros.h" @@ -70,7 +71,6 @@ #include "nvim/os/input.h" #include "nvim/screen.h" #include "nvim/state.h" -#include "nvim/syntax.h" #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/vim.h" diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index d7becb4fd4..e356960cc8 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -14,6 +14,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/highlight.h" +#include "nvim/highlight_group.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" #include "nvim/lua/executor.h" @@ -23,7 +24,6 @@ #include "nvim/os/os.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" -#include "nvim/syntax.h" #include "nvim/ugrid.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index e9c7d0d8e2..8b9f1e024d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -25,6 +25,7 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hashtab.h" +#include "nvim/highlight_group.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/memline.h" -- cgit From 9e66d27d37d2260358783c043fdb636bf881e85f Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 13:15:18 +0000 Subject: fix(syntax.c): correct hunk from Vim patch 8.0.0647 (#17761) --- src/nvim/screen.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index bc9c6bbe00..60de4e479e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2623,9 +2623,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } next_search_hl(wp, shl, lnum, (colnr_T)v, shl == &search_hl ? NULL : cur); - if (wp->w_s->b_syn_slow) { - has_syntax = false; - } // Need to get the line again, a multi-line regexp may have made it // invalid. @@ -3383,6 +3380,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc did_emsg = save_did_emsg; } + if (wp->w_s->b_syn_slow) { + has_syntax = false; + } + // Need to get the line again, a multi-line regexp may // have made it invalid. line = ml_get_buf(wp->w_buffer, lnum, false); -- cgit From e0fe91f6a3ec88d6f8bee4f7af1af6bf9b397c1f Mon Sep 17 00:00:00 2001 From: matveyt <35012635+matveyt@users.noreply.github.com> Date: Fri, 18 Mar 2022 21:48:20 +0300 Subject: fix(translation): po file for Russian (#17767) --- src/nvim/po/ru.po | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po index 3a96ece2fb..5d3e51b7e2 100644 --- a/src/nvim/po/ru.po +++ b/src/nvim/po/ru.po @@ -959,7 +959,6 @@ msgstr "E129: ТребуетÑÑ Ð¸Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸" #, c-format msgid "E128: Function name must start with a capital or \"s:\": %s" msgstr "E128: Ð˜Ð¼Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¸ должно начинатьÑÑ Ñ Ð·Ð°Ð³Ð»Ð°Ð²Ð½Ð¾Ð¹ буквы или \"s:\": %s" -"двоеточие: %s" #: ../eval.c:17833 #, c-format @@ -1578,7 +1577,7 @@ msgstr "E494: ИÑпользуйте w или w>>" #: ../ex_docmd.c:3454 msgid "E319: The command is not available in this version" -msgstr "E319: Извините, Ñта команда недоÑтупна в данной верÑии" +msgstr "E319: Эта команда недоÑтупна в данной верÑии" #: ../ex_docmd.c:3752 msgid "E172: Only one file name allowed" @@ -2055,7 +2054,7 @@ msgstr "E201: Ðвтокоманды *ReadPre не должны изменÑть #: ../fileio.c:672 msgid "Nvim: Reading from stdin...\n" -msgstr "Vim: Чтение из Ñтандартного потока ввода stdin...\n" +msgstr "Nvim: Чтение из Ñтандартного потока ввода stdin...\n" #. Re-opening the original file failed! #: ../fileio.c:909 @@ -2427,7 +2426,7 @@ msgstr "--Удалено--" #: ../fileio.c:5732 #, c-format msgid "auto-removing autocommand: %s " -msgstr "авто-удаление автокоманды: %s <буффер=%d>" +msgstr "авто-удаление автокоманды: %s <буфер=%d>" #. the group doesn't exist #: ../fileio.c:5772 @@ -2666,11 +2665,11 @@ msgstr "E17: \"%s\" ÑвлÑетÑÑ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð¾Ð¼" #: ../globals.h:1020 #, fuzzy msgid "E900: Invalid job id" -msgstr "E49: ÐедопуÑтимый размер прокрутки" +msgstr "E900: ÐедопуÑтимый идентификатор заданиÑ" #: ../globals.h:1021 msgid "E901: Job table is full" -msgstr "" +msgstr "E901: Таблица заданий переполнена" #: ../globals.h:1024 #, c-format @@ -2707,9 +2706,7 @@ msgstr "E477: ! не допуÑкаетÑÑ" #: ../globals.h:1035 msgid "E25: Nvim does not have a built-in GUI" -msgstr "" -"E25: ВозможноÑть иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð³Ñ€Ð°Ñ„Ð¸Ñ‡ÐµÑкого интерфейÑа выключена при " -"компилÑции" +msgstr "E25: У Nvim нет вÑтроенного графичеÑкого интерфейÑа" #: ../globals.h:1036 #, c-format @@ -3351,10 +3348,10 @@ msgstr "E282: Ðевозможно выполнить чтение из \"%s\"" #: ../main.c:2149 msgid "" "\n" -"More info with: \"vim -h\"\n" +"More info with: \"" msgstr "" "\n" -"Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ: \"vim -h\"\n" +"Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ: \"" #: ../main.c:2178 msgid "[file ..] edit specified file(s)" @@ -4413,7 +4410,7 @@ msgstr "E663: Ð’ конце ÑпиÑка изменений" #: ../normal.c:7053 msgid "Type :quit to exit Nvim" -msgstr "Введите :quit Ð´Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° из Vim" +msgstr "Введите :quit Ð´Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° из Nvim" #: ../ops.c:248 #, c-format @@ -4521,6 +4518,7 @@ msgid "" "lines" msgstr "" "E883: шаблон поиÑка и региÑтр Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ могут Ñодержать двух или более " +"Ñтрок" #: ../ops.c:5089 #, c-format @@ -6137,7 +6135,7 @@ msgstr "Vim: Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð²Ð²Ð¾Ð´Ð°, выход...\n" #: ../undo.c:379 #, fuzzy msgid "E881: Line count changed unexpectedly" -msgstr "E834: Ðеожиданно изменилÑÑ Ñчётчик Ñтрок" +msgstr "E881: Ðеожиданно изменилÑÑ Ñчётчик Ñтрок" #: ../undo.c:627 #, c-format -- cgit From f2e5f509d995b291e4b9e05a0c059e83490a18e1 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 18 Mar 2022 19:58:00 +0100 Subject: docs: reword description for nvim_buf_line_count() (#17766) This adds a few more keywords to make the function easier to find. --- src/nvim/api/buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 9f0cadd5ce..18f7177489 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -52,7 +52,7 @@ /// whether a buffer is loaded. -/// Gets the buffer line count +/// Returns the number of lines in the given buffer. /// /// @param buffer Buffer handle, or 0 for current buffer /// @param[out] err Error details, if any -- cgit From 81648fd27719e0b0d7f5f7ac19142c39b857600e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 15:50:25 +0800 Subject: vim-patch:8.2.2716: the equivalent class regexp is missing some characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: The equivalent class regexp is missing some characters. Solution: Update the list of equivalent characters. (Dominique Pellé, closes vim/vim#8029) https://github.com/vim/vim/commit/0b94e297afd072c51bf2eed12c7ffe3978d93399 Match upstream's indent in s:equivalence_class(). --- src/nvim/regexp_bt.c | 604 ++++++++++++++++++++------------ src/nvim/regexp_nfa.c | 641 +++++++++++++++++++++------------- src/nvim/testdir/test_regexp_utf8.vim | 26 +- 3 files changed, 797 insertions(+), 474 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 1153b8ed33..7340957903 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -526,8 +526,6 @@ static void regmbc(int c) } } -#define REGMBC(x) regmbc(x); -#define CASEMBC(x) case x: /* * Produce the bytes for equivalence class "c". @@ -538,297 +536,465 @@ static void reg_equi_class(int c) { { switch (c) { - // Do not use '\300' style, it results in a negative number. - case 'A': case 0xc0: case 0xc1: case 0xc2: - case 0xc3: case 0xc4: case 0xc5: - CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) CASEMBC(0x1cd) - CASEMBC(0x1de) CASEMBC(0x1e0) CASEMBC(0x1ea2) - regmbc('A'); regmbc(0xc0); regmbc(0xc1); - regmbc(0xc2); regmbc(0xc3); regmbc(0xc4); - regmbc(0xc5); - REGMBC(0x100) REGMBC(0x102) REGMBC(0x104) - REGMBC(0x1cd) REGMBC(0x1de) REGMBC(0x1e0) - REGMBC(0x1ea2) + // Do not use '\300' style, it results in a negative number. + case 'A': case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: + case 0xc5: case 0x100: case 0x102: case 0x104: case 0x1cd: + case 0x1de: case 0x1e0: case 0x1fa: case 0x202: case 0x226: + case 0x23a: case 0x1e00: case 0x1ea0: case 0x1ea2: case 0x1ea4: + case 0x1ea6: case 0x1ea8: case 0x1eaa: case 0x1eac: case 0x1eae: + case 0x1eb0: case 0x1eb2: case 0x1eb4: case 0x1eb6: + regmbc('A'); regmbc(0xc0); regmbc(0xc1); regmbc(0xc2); + regmbc(0xc3); regmbc(0xc4); regmbc(0xc5); + regmbc(0x100); regmbc(0x102); regmbc(0x104); + regmbc(0x1cd); regmbc(0x1de); regmbc(0x1e0); + regmbc(0x1fa); regmbc(0x202); regmbc(0x226); + regmbc(0x23a); regmbc(0x1e00); regmbc(0x1ea0); + regmbc(0x1ea2); regmbc(0x1ea4); regmbc(0x1ea6); + regmbc(0x1ea8); regmbc(0x1eaa); regmbc(0x1eac); + regmbc(0x1eae); regmbc(0x1eb0); regmbc(0x1eb2); + regmbc(0x1eb4); regmbc(0x1eb6); return; - case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06) - regmbc('B'); REGMBC(0x1e02) REGMBC(0x1e06) + case 'B': case 0x181: case 0x243: case 0x1e02: + case 0x1e04: case 0x1e06: + regmbc('B'); + regmbc(0x181); regmbc(0x243); regmbc(0x1e02); + regmbc(0x1e04); regmbc(0x1e06); return; case 'C': case 0xc7: - CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) CASEMBC(0x10c) + case 0x106: case 0x108: case 0x10a: case 0x10c: case 0x187: + case 0x23b: case 0x1e08: case 0xa792: regmbc('C'); regmbc(0xc7); - REGMBC(0x106) REGMBC(0x108) REGMBC(0x10a) - REGMBC(0x10c) + regmbc(0x106); regmbc(0x108); regmbc(0x10a); + regmbc(0x10c); regmbc(0x187); regmbc(0x23b); + regmbc(0x1e08); regmbc(0xa792); return; - case 'D': CASEMBC(0x10e) CASEMBC(0x110) CASEMBC(0x1e0a) - CASEMBC(0x1e0e) CASEMBC(0x1e10) - regmbc('D'); REGMBC(0x10e) REGMBC(0x110) - REGMBC(0x1e0a) REGMBC(0x1e0e) REGMBC(0x1e10) + case 'D': case 0x10e: case 0x110: case 0x18a: + case 0x1e0a: case 0x1e0c: case 0x1e0e: case 0x1e10: + case 0x1e12: + regmbc('D'); regmbc(0x10e); regmbc(0x110); + regmbc(0x18a); regmbc(0x1e0a); regmbc(0x1e0c); + regmbc(0x1e0e); regmbc(0x1e10); regmbc(0x1e12); return; case 'E': case 0xc8: case 0xc9: case 0xca: case 0xcb: - CASEMBC(0x112) CASEMBC(0x114) CASEMBC(0x116) CASEMBC(0x118) - CASEMBC(0x11a) CASEMBC(0x1eba) CASEMBC(0x1ebc) + case 0x112: case 0x114: case 0x116: case 0x118: case 0x11a: + case 0x204: case 0x206: case 0x228: case 0x246: case 0x1e14: + case 0x1e16: case 0x1e18: case 0x1e1a: case 0x1e1c: + case 0x1eb8: case 0x1eba: case 0x1ebc: case 0x1ebe: + case 0x1ec0: case 0x1ec2: case 0x1ec4: case 0x1ec6: regmbc('E'); regmbc(0xc8); regmbc(0xc9); - regmbc(0xca); regmbc(0xcb); - REGMBC(0x112) REGMBC(0x114) REGMBC(0x116) - REGMBC(0x118) REGMBC(0x11a) REGMBC(0x1eba) - REGMBC(0x1ebc) + regmbc(0xca); regmbc(0xcb); regmbc(0x112); + regmbc(0x114); regmbc(0x116); regmbc(0x118); + regmbc(0x11a); regmbc(0x204); regmbc(0x206); + regmbc(0x228); regmbc(0x246); regmbc(0x1e14); + regmbc(0x1e16); regmbc(0x1e18); regmbc(0x1e1a); + regmbc(0x1e1c); regmbc(0x1eb8); regmbc(0x1eba); + regmbc(0x1ebc); regmbc(0x1ebe); regmbc(0x1ec0); + regmbc(0x1ec2); regmbc(0x1ec4); regmbc(0x1ec6); return; - case 'F': CASEMBC(0x1e1e) - regmbc('F'); REGMBC(0x1e1e) + case 'F': case 0x191: case 0x1e1e: case 0xa798: + regmbc('F'); regmbc(0x191); regmbc(0x1e1e); + regmbc(0xa798); return; - case 'G': CASEMBC(0x11c) CASEMBC(0x11e) CASEMBC(0x120) - CASEMBC(0x122) CASEMBC(0x1e4) CASEMBC(0x1e6) CASEMBC(0x1f4) - CASEMBC(0x1e20) - regmbc('G'); REGMBC(0x11c) REGMBC(0x11e) - REGMBC(0x120) REGMBC(0x122) REGMBC(0x1e4) - REGMBC(0x1e6) REGMBC(0x1f4) REGMBC(0x1e20) + case 'G': case 0x11c: case 0x11e: case 0x120: + case 0x122: case 0x193: case 0x1e4: case 0x1e6: + case 0x1f4: case 0x1e20: case 0xa7a0: + regmbc('G'); regmbc(0x11c); regmbc(0x11e); + regmbc(0x120); regmbc(0x122); regmbc(0x193); + regmbc(0x1e4); regmbc(0x1e6); regmbc(0x1f4); + regmbc(0x1e20); regmbc(0xa7a0); return; - case 'H': CASEMBC(0x124) CASEMBC(0x126) CASEMBC(0x1e22) - CASEMBC(0x1e26) CASEMBC(0x1e28) - regmbc('H'); REGMBC(0x124) REGMBC(0x126) - REGMBC(0x1e22) REGMBC(0x1e26) REGMBC(0x1e28) + case 'H': case 0x124: case 0x126: case 0x21e: + case 0x1e22: case 0x1e24: case 0x1e26: + case 0x1e28: case 0x1e2a: case 0x2c67: + regmbc('H'); regmbc(0x124); regmbc(0x126); + regmbc(0x21e); regmbc(0x1e22); regmbc(0x1e24); + regmbc(0x1e26); regmbc(0x1e28); regmbc(0x1e2a); + regmbc(0x2c67); return; case 'I': case 0xcc: case 0xcd: case 0xce: case 0xcf: - CASEMBC(0x128) CASEMBC(0x12a) CASEMBC(0x12c) CASEMBC(0x12e) - CASEMBC(0x130) CASEMBC(0x1cf) CASEMBC(0x1ec8) + case 0x128: case 0x12a: case 0x12c: case 0x12e: + case 0x130: case 0x197: case 0x1cf: case 0x208: + case 0x20a: case 0x1e2c: case 0x1e2e: case 0x1ec8: + case 0x1eca: regmbc('I'); regmbc(0xcc); regmbc(0xcd); - regmbc(0xce); regmbc(0xcf); - REGMBC(0x128) REGMBC(0x12a) REGMBC(0x12c) - REGMBC(0x12e) REGMBC(0x130) REGMBC(0x1cf) - REGMBC(0x1ec8) + regmbc(0xce); regmbc(0xcf); regmbc(0x128); + regmbc(0x12a); regmbc(0x12c); regmbc(0x12e); + regmbc(0x130); regmbc(0x197); regmbc(0x1cf); + regmbc(0x208); regmbc(0x20a); regmbc(0x1e2c); + regmbc(0x1e2e); regmbc(0x1ec8); regmbc(0x1eca); return; - case 'J': CASEMBC(0x134) - regmbc('J'); REGMBC(0x134) + case 'J': case 0x134: case 0x248: + regmbc('J'); regmbc(0x134); regmbc(0x248); return; - case 'K': CASEMBC(0x136) CASEMBC(0x1e8) CASEMBC(0x1e30) - CASEMBC(0x1e34) - regmbc('K'); REGMBC(0x136) REGMBC(0x1e8) - REGMBC(0x1e30) REGMBC(0x1e34) + case 'K': case 0x136: case 0x198: case 0x1e8: case 0x1e30: + case 0x1e32: case 0x1e34: case 0x2c69: case 0xa740: + regmbc('K'); regmbc(0x136); regmbc(0x198); + regmbc(0x1e8); regmbc(0x1e30); regmbc(0x1e32); + regmbc(0x1e34); regmbc(0x2c69); regmbc(0xa740); return; - case 'L': CASEMBC(0x139) CASEMBC(0x13b) CASEMBC(0x13d) - CASEMBC(0x13f) CASEMBC(0x141) CASEMBC(0x1e3a) - regmbc('L'); REGMBC(0x139) REGMBC(0x13b) - REGMBC(0x13d) REGMBC(0x13f) REGMBC(0x141) - REGMBC(0x1e3a) + case 'L': case 0x139: case 0x13b: case 0x13d: case 0x13f: + case 0x141: case 0x23d: case 0x1e36: case 0x1e38: + case 0x1e3a: case 0x1e3c: case 0x2c60: + regmbc('L'); regmbc(0x139); regmbc(0x13b); + regmbc(0x13d); regmbc(0x13f); regmbc(0x141); + regmbc(0x23d); regmbc(0x1e36); regmbc(0x1e38); + regmbc(0x1e3a); regmbc(0x1e3c); regmbc(0x2c60); return; - case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40) - regmbc('M'); REGMBC(0x1e3e) REGMBC(0x1e40) + case 'M': case 0x1e3e: case 0x1e40: case 0x1e42: + regmbc('M'); regmbc(0x1e3e); regmbc(0x1e40); + regmbc(0x1e42); return; case 'N': case 0xd1: - CASEMBC(0x143) CASEMBC(0x145) CASEMBC(0x147) CASEMBC(0x1e44) - CASEMBC(0x1e48) + case 0x143: case 0x145: case 0x147: case 0x1f8: + case 0x1e44: case 0x1e46: case 0x1e48: case 0x1e4a: + case 0xa7a4: regmbc('N'); regmbc(0xd1); - REGMBC(0x143) REGMBC(0x145) REGMBC(0x147) - REGMBC(0x1e44) REGMBC(0x1e48) + regmbc(0x143); regmbc(0x145); regmbc(0x147); + regmbc(0x1f8); regmbc(0x1e44); regmbc(0x1e46); + regmbc(0x1e48); regmbc(0x1e4a); regmbc(0xa7a4); return; - case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5: - case 0xd6: case 0xd8: - CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) CASEMBC(0x1a0) - CASEMBC(0x1d1) CASEMBC(0x1ea) CASEMBC(0x1ec) CASEMBC(0x1ece) - regmbc('O'); regmbc(0xd2); regmbc(0xd3); - regmbc(0xd4); regmbc(0xd5); regmbc(0xd6); - regmbc(0xd8); - REGMBC(0x14c) REGMBC(0x14e) REGMBC(0x150) - REGMBC(0x1a0) REGMBC(0x1d1) REGMBC(0x1ea) - REGMBC(0x1ec) REGMBC(0x1ece) + case 'O': case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: + case 0xd8: case 0x14c: case 0x14e: case 0x150: case 0x19f: + case 0x1a0: case 0x1d1: case 0x1ea: case 0x1ec: case 0x1fe: + case 0x20c: case 0x20e: case 0x22a: case 0x22c: case 0x22e: + case 0x230: case 0x1e4c: case 0x1e4e: case 0x1e50: case 0x1e52: + case 0x1ecc: case 0x1ece: case 0x1ed0: case 0x1ed2: case 0x1ed4: + case 0x1ed6: case 0x1ed8: case 0x1eda: case 0x1edc: case 0x1ede: + case 0x1ee0: case 0x1ee2: + regmbc('O'); regmbc(0xd2); regmbc(0xd3); regmbc(0xd4); + regmbc(0xd5); regmbc(0xd6); regmbc(0xd8); + regmbc(0x14c); regmbc(0x14e); regmbc(0x150); + regmbc(0x19f); regmbc(0x1a0); regmbc(0x1d1); + regmbc(0x1ea); regmbc(0x1ec); regmbc(0x1fe); + regmbc(0x20c); regmbc(0x20e); regmbc(0x22a); + regmbc(0x22c); regmbc(0x22e); regmbc(0x230); + regmbc(0x1e4c); regmbc(0x1e4e); regmbc(0x1e50); + regmbc(0x1e52); regmbc(0x1ecc); regmbc(0x1ece); + regmbc(0x1ed0); regmbc(0x1ed2); regmbc(0x1ed4); + regmbc(0x1ed6); regmbc(0x1ed8); regmbc(0x1eda); + regmbc(0x1edc); regmbc(0x1ede); regmbc(0x1ee0); + regmbc(0x1ee2); return; - case 'P': case 0x1e54: case 0x1e56: - regmbc('P'); REGMBC(0x1e54) REGMBC(0x1e56) + case 'P': case 0x1a4: case 0x1e54: case 0x1e56: case 0x2c63: + regmbc('P'); regmbc(0x1a4); regmbc(0x1e54); + regmbc(0x1e56); regmbc(0x2c63); return; - case 'R': CASEMBC(0x154) CASEMBC(0x156) CASEMBC(0x158) - CASEMBC(0x1e58) CASEMBC(0x1e5e) - regmbc('R'); REGMBC(0x154) REGMBC(0x156) REGMBC(0x158) - REGMBC(0x1e58) REGMBC(0x1e5e) + case 'Q': case 0x24a: + regmbc('Q'); regmbc(0x24a); return; - case 'S': CASEMBC(0x15a) CASEMBC(0x15c) CASEMBC(0x15e) - CASEMBC(0x160) CASEMBC(0x1e60) - regmbc('S'); REGMBC(0x15a) REGMBC(0x15c) - REGMBC(0x15e) REGMBC(0x160) REGMBC(0x1e60) + case 'R': case 0x154: case 0x156: case 0x158: case 0x210: + case 0x212: case 0x24c: case 0x1e58: case 0x1e5a: + case 0x1e5c: case 0x1e5e: case 0x2c64: case 0xa7a6: + regmbc('R'); regmbc(0x154); regmbc(0x156); + regmbc(0x210); regmbc(0x212); regmbc(0x158); + regmbc(0x24c); regmbc(0x1e58); regmbc(0x1e5a); + regmbc(0x1e5c); regmbc(0x1e5e); regmbc(0x2c64); + regmbc(0xa7a6); return; - case 'T': CASEMBC(0x162) CASEMBC(0x164) CASEMBC(0x166) - CASEMBC(0x1e6a) CASEMBC(0x1e6e) - regmbc('T'); REGMBC(0x162) REGMBC(0x164) - REGMBC(0x166) REGMBC(0x1e6a) REGMBC(0x1e6e) + case 'S': case 0x15a: case 0x15c: case 0x15e: case 0x160: + case 0x218: case 0x1e60: case 0x1e62: case 0x1e64: + case 0x1e66: case 0x1e68: case 0x2c7e: case 0xa7a8: + regmbc('S'); regmbc(0x15a); regmbc(0x15c); + regmbc(0x15e); regmbc(0x160); regmbc(0x218); + regmbc(0x1e60); regmbc(0x1e62); regmbc(0x1e64); + regmbc(0x1e66); regmbc(0x1e68); regmbc(0x2c7e); + regmbc(0xa7a8); + return; + case 'T': case 0x162: case 0x164: case 0x166: case 0x1ac: + case 0x1ae: case 0x21a: case 0x23e: case 0x1e6a: case 0x1e6c: + case 0x1e6e: case 0x1e70: + regmbc('T'); regmbc(0x162); regmbc(0x164); + regmbc(0x166); regmbc(0x1ac); regmbc(0x23e); + regmbc(0x1ae); regmbc(0x21a); regmbc(0x1e6a); + regmbc(0x1e6c); regmbc(0x1e6e); regmbc(0x1e70); return; case 'U': case 0xd9: case 0xda: case 0xdb: case 0xdc: - CASEMBC(0x168) CASEMBC(0x16a) CASEMBC(0x16c) CASEMBC(0x16e) - CASEMBC(0x170) CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3) - CASEMBC(0x1ee6) + case 0x168: case 0x16a: case 0x16c: case 0x16e: + case 0x170: case 0x172: case 0x1af: case 0x1d3: + case 0x1d5: case 0x1d7: case 0x1d9: case 0x1db: + case 0x214: case 0x216: case 0x244: case 0x1e72: + case 0x1e74: case 0x1e76: case 0x1e78: case 0x1e7a: + case 0x1ee4: case 0x1ee6: case 0x1ee8: case 0x1eea: + case 0x1eec: case 0x1eee: case 0x1ef0: regmbc('U'); regmbc(0xd9); regmbc(0xda); - regmbc(0xdb); regmbc(0xdc); - REGMBC(0x168) REGMBC(0x16a) REGMBC(0x16c) - REGMBC(0x16e) REGMBC(0x170) REGMBC(0x172) - REGMBC(0x1af) REGMBC(0x1d3) REGMBC(0x1ee6) + regmbc(0xdb); regmbc(0xdc); regmbc(0x168); + regmbc(0x16a); regmbc(0x16c); regmbc(0x16e); + regmbc(0x170); regmbc(0x172); regmbc(0x1af); + regmbc(0x1d3); regmbc(0x1d5); regmbc(0x1d7); + regmbc(0x1d9); regmbc(0x1db); regmbc(0x214); + regmbc(0x216); regmbc(0x244); regmbc(0x1e72); + regmbc(0x1e74); regmbc(0x1e76); regmbc(0x1e78); + regmbc(0x1e7a); regmbc(0x1ee4); regmbc(0x1ee6); + regmbc(0x1ee8); regmbc(0x1eea); regmbc(0x1eec); + regmbc(0x1eee); regmbc(0x1ef0); return; - case 'V': CASEMBC(0x1e7c) - regmbc('V'); REGMBC(0x1e7c) + case 'V': case 0x1b2: case 0x1e7c: case 0x1e7e: + regmbc('V'); regmbc(0x1b2); regmbc(0x1e7c); + regmbc(0x1e7e); return; - case 'W': CASEMBC(0x174) CASEMBC(0x1e80) CASEMBC(0x1e82) - CASEMBC(0x1e84) CASEMBC(0x1e86) - regmbc('W'); REGMBC(0x174) REGMBC(0x1e80) - REGMBC(0x1e82) REGMBC(0x1e84) REGMBC(0x1e86) + case 'W': case 0x174: case 0x1e80: case 0x1e82: + case 0x1e84: case 0x1e86: case 0x1e88: + regmbc('W'); regmbc(0x174); regmbc(0x1e80); + regmbc(0x1e82); regmbc(0x1e84); regmbc(0x1e86); + regmbc(0x1e88); return; - case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c) - regmbc('X'); REGMBC(0x1e8a) REGMBC(0x1e8c) + case 'X': case 0x1e8a: case 0x1e8c: + regmbc('X'); regmbc(0x1e8a); regmbc(0x1e8c); return; case 'Y': case 0xdd: - CASEMBC(0x176) CASEMBC(0x178) CASEMBC(0x1e8e) CASEMBC(0x1ef2) - CASEMBC(0x1ef6) CASEMBC(0x1ef8) - regmbc('Y'); regmbc(0xdd); - REGMBC(0x176) REGMBC(0x178) REGMBC(0x1e8e) - REGMBC(0x1ef2) REGMBC(0x1ef6) REGMBC(0x1ef8) + case 0x176: case 0x178: case 0x1b3: case 0x232: case 0x24e: + case 0x1e8e: case 0x1ef2: case 0x1ef6: case 0x1ef4: case 0x1ef8: + regmbc('Y'); regmbc(0xdd); regmbc(0x176); + regmbc(0x178); regmbc(0x1b3); regmbc(0x232); + regmbc(0x24e); regmbc(0x1e8e); regmbc(0x1ef2); + regmbc(0x1ef4); regmbc(0x1ef6); regmbc(0x1ef8); return; - case 'Z': CASEMBC(0x179) CASEMBC(0x17b) CASEMBC(0x17d) - CASEMBC(0x1b5) CASEMBC(0x1e90) CASEMBC(0x1e94) - regmbc('Z'); REGMBC(0x179) REGMBC(0x17b) - REGMBC(0x17d) REGMBC(0x1b5) REGMBC(0x1e90) - REGMBC(0x1e94) + case 'Z': case 0x179: case 0x17b: case 0x17d: case 0x1b5: + case 0x1e90: case 0x1e92: case 0x1e94: case 0x2c6b: + regmbc('Z'); regmbc(0x179); regmbc(0x17b); + regmbc(0x17d); regmbc(0x1b5); regmbc(0x1e90); + regmbc(0x1e92); regmbc(0x1e94); regmbc(0x2c6b); return; case 'a': case 0xe0: case 0xe1: case 0xe2: - case 0xe3: case 0xe4: case 0xe5: - CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) CASEMBC(0x1ce) - CASEMBC(0x1df) CASEMBC(0x1e1) CASEMBC(0x1ea3) + case 0xe3: case 0xe4: case 0xe5: case 0x101: case 0x103: + case 0x105: case 0x1ce: case 0x1df: case 0x1e1: case 0x1fb: + case 0x201: case 0x203: case 0x227: case 0x1d8f: case 0x1e01: + case 0x1e9a: case 0x1ea1: case 0x1ea3: case 0x1ea5: + case 0x1ea7: case 0x1ea9: case 0x1eab: case 0x1ead: + case 0x1eaf: case 0x1eb1: case 0x1eb3: case 0x1eb5: + case 0x1eb7: case 0x2c65: regmbc('a'); regmbc(0xe0); regmbc(0xe1); regmbc(0xe2); regmbc(0xe3); regmbc(0xe4); - regmbc(0xe5); - REGMBC(0x101) REGMBC(0x103) REGMBC(0x105) - REGMBC(0x1ce) REGMBC(0x1df) REGMBC(0x1e1) - REGMBC(0x1ea3) + regmbc(0xe5); regmbc(0x101); regmbc(0x103); + regmbc(0x105); regmbc(0x1ce); regmbc(0x1df); + regmbc(0x1e1); regmbc(0x1fb); regmbc(0x201); + regmbc(0x203); regmbc(0x227); regmbc(0x1d8f); + regmbc(0x1e01); regmbc(0x1e9a); regmbc(0x1ea1); + regmbc(0x1ea3); regmbc(0x1ea5); regmbc(0x1ea7); + regmbc(0x1ea9); regmbc(0x1eab); regmbc(0x1ead); + regmbc(0x1eaf); regmbc(0x1eb1); regmbc(0x1eb3); + regmbc(0x1eb5); regmbc(0x1eb7); regmbc(0x2c65); return; - case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07) - regmbc('b'); REGMBC(0x1e03) REGMBC(0x1e07) + case 'b': case 0x180: case 0x253: case 0x1d6c: case 0x1d80: + case 0x1e03: case 0x1e05: case 0x1e07: + regmbc('b'); + regmbc(0x180); regmbc(0x253); regmbc(0x1d6c); + regmbc(0x1d80); regmbc(0x1e03); regmbc(0x1e05); + regmbc(0x1e07); return; case 'c': case 0xe7: - CASEMBC(0x107) CASEMBC(0x109) CASEMBC(0x10b) CASEMBC(0x10d) - regmbc('c'); regmbc(0xe7); - REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b) - REGMBC(0x10d) + case 0x107: case 0x109: case 0x10b: case 0x10d: case 0x188: + case 0x23c: case 0x1e09: case 0xa793: case 0xa794: + regmbc('c'); regmbc(0xe7); regmbc(0x107); + regmbc(0x109); regmbc(0x10b); regmbc(0x10d); + regmbc(0x188); regmbc(0x23c); regmbc(0x1e09); + regmbc(0xa793); regmbc(0xa794); return; - case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b) - CASEMBC(0x1e0f) CASEMBC(0x1e11) - regmbc('d'); REGMBC(0x10f) REGMBC(0x111) - REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11) + case 'd': case 0x10f: case 0x111: case 0x257: case 0x1d6d: + case 0x1d81: case 0x1d91: case 0x1e0b: case 0x1e0d: + case 0x1e0f: case 0x1e11: case 0x1e13: + regmbc('d'); regmbc(0x10f); regmbc(0x111); + regmbc(0x257); regmbc(0x1d6d); regmbc(0x1d81); + regmbc(0x1d91); regmbc(0x1e0b); regmbc(0x1e0d); + regmbc(0x1e0f); regmbc(0x1e11); regmbc(0x1e13); return; case 'e': case 0xe8: case 0xe9: case 0xea: case 0xeb: - CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119) - CASEMBC(0x11b) CASEMBC(0x1ebb) CASEMBC(0x1ebd) + case 0x113: case 0x115: case 0x117: case 0x119: + case 0x11b: case 0x205: case 0x207: case 0x229: + case 0x247: case 0x1d92: case 0x1e15: case 0x1e17: + case 0x1e19: case 0x1e1b: case 0x1eb9: case 0x1ebb: + case 0x1e1d: case 0x1ebd: case 0x1ebf: case 0x1ec1: + case 0x1ec3: case 0x1ec5: case 0x1ec7: regmbc('e'); regmbc(0xe8); regmbc(0xe9); - regmbc(0xea); regmbc(0xeb); - REGMBC(0x113) REGMBC(0x115) REGMBC(0x117) - REGMBC(0x119) REGMBC(0x11b) REGMBC(0x1ebb) - REGMBC(0x1ebd) + regmbc(0xea); regmbc(0xeb); regmbc(0x113); + regmbc(0x115); regmbc(0x117); regmbc(0x119); + regmbc(0x11b); regmbc(0x205); regmbc(0x207); + regmbc(0x229); regmbc(0x247); regmbc(0x1d92); + regmbc(0x1e15); regmbc(0x1e17); regmbc(0x1e19); + regmbc(0x1e1b); regmbc(0x1e1d); regmbc(0x1eb9); + regmbc(0x1ebb); regmbc(0x1ebd); regmbc(0x1ebf); + regmbc(0x1ec1); regmbc(0x1ec3); regmbc(0x1ec5); + regmbc(0x1ec7); return; - case 'f': CASEMBC(0x1e1f) - regmbc('f'); REGMBC(0x1e1f) + case 'f': case 0x192: case 0x1d6e: case 0x1d82: + case 0x1e1f: case 0xa799: + regmbc('f'); regmbc(0x192); regmbc(0x1d6e); + regmbc(0x1d82); regmbc(0x1e1f); regmbc(0xa799); return; - case 'g': CASEMBC(0x11d) CASEMBC(0x11f) CASEMBC(0x121) - CASEMBC(0x123) CASEMBC(0x1e5) CASEMBC(0x1e7) CASEMBC(0x1f5) - CASEMBC(0x1e21) - regmbc('g'); REGMBC(0x11d) REGMBC(0x11f) - REGMBC(0x121) REGMBC(0x123) REGMBC(0x1e5) - REGMBC(0x1e7) REGMBC(0x1f5) REGMBC(0x1e21) + case 'g': case 0x11d: case 0x11f: case 0x121: case 0x123: + case 0x1e5: case 0x1e7: case 0x260: case 0x1f5: case 0x1d83: + case 0x1e21: case 0xa7a1: + regmbc('g'); regmbc(0x11d); regmbc(0x11f); + regmbc(0x121); regmbc(0x123); regmbc(0x1e5); + regmbc(0x1e7); regmbc(0x1f5); regmbc(0x260); + regmbc(0x1d83); regmbc(0x1e21); regmbc(0xa7a1); return; - case 'h': CASEMBC(0x125) CASEMBC(0x127) CASEMBC(0x1e23) - CASEMBC(0x1e27) CASEMBC(0x1e29) CASEMBC(0x1e96) - regmbc('h'); REGMBC(0x125) REGMBC(0x127) - REGMBC(0x1e23) REGMBC(0x1e27) REGMBC(0x1e29) - REGMBC(0x1e96) + case 'h': case 0x125: case 0x127: case 0x21f: case 0x1e23: + case 0x1e25: case 0x1e27: case 0x1e29: case 0x1e2b: + case 0x1e96: case 0x2c68: case 0xa795: + regmbc('h'); regmbc(0x125); regmbc(0x127); + regmbc(0x21f); regmbc(0x1e23); regmbc(0x1e25); + regmbc(0x1e27); regmbc(0x1e29); regmbc(0x1e2b); + regmbc(0x1e96); regmbc(0x2c68); regmbc(0xa795); return; case 'i': case 0xec: case 0xed: case 0xee: case 0xef: - CASEMBC(0x129) CASEMBC(0x12b) CASEMBC(0x12d) CASEMBC(0x12f) - CASEMBC(0x1d0) CASEMBC(0x1ec9) + case 0x129: case 0x12b: case 0x12d: case 0x12f: + case 0x1d0: case 0x209: case 0x20b: case 0x268: + case 0x1d96: case 0x1e2d: case 0x1e2f: case 0x1ec9: + case 0x1ecb: regmbc('i'); regmbc(0xec); regmbc(0xed); - regmbc(0xee); regmbc(0xef); - REGMBC(0x129) REGMBC(0x12b) REGMBC(0x12d) - REGMBC(0x12f) REGMBC(0x1d0) REGMBC(0x1ec9) + regmbc(0xee); regmbc(0xef); regmbc(0x129); + regmbc(0x12b); regmbc(0x12d); regmbc(0x12f); + regmbc(0x1d0); regmbc(0x209); regmbc(0x20b); + regmbc(0x268); regmbc(0x1d96); regmbc(0x1e2d); + regmbc(0x1e2f); regmbc(0x1ec9); regmbc(0x1ecb); return; - case 'j': CASEMBC(0x135) CASEMBC(0x1f0) - regmbc('j'); REGMBC(0x135) REGMBC(0x1f0) + case 'j': case 0x135: case 0x1f0: case 0x249: + regmbc('j'); regmbc(0x135); regmbc(0x1f0); + regmbc(0x249); return; - case 'k': CASEMBC(0x137) CASEMBC(0x1e9) CASEMBC(0x1e31) - CASEMBC(0x1e35) - regmbc('k'); REGMBC(0x137) REGMBC(0x1e9) - REGMBC(0x1e31) REGMBC(0x1e35) + case 'k': case 0x137: case 0x199: case 0x1e9: + case 0x1d84: case 0x1e31: case 0x1e33: case 0x1e35: + case 0x2c6a: case 0xa741: + regmbc('k'); regmbc(0x137); regmbc(0x199); + regmbc(0x1e9); regmbc(0x1d84); regmbc(0x1e31); + regmbc(0x1e33); regmbc(0x1e35); regmbc(0x2c6a); + regmbc(0xa741); return; - case 'l': CASEMBC(0x13a) CASEMBC(0x13c) CASEMBC(0x13e) - CASEMBC(0x140) CASEMBC(0x142) CASEMBC(0x1e3b) - regmbc('l'); REGMBC(0x13a) REGMBC(0x13c) - REGMBC(0x13e) REGMBC(0x140) REGMBC(0x142) - REGMBC(0x1e3b) + case 'l': case 0x13a: case 0x13c: case 0x13e: + case 0x140: case 0x142: case 0x19a: case 0x1e37: + case 0x1e39: case 0x1e3b: case 0x1e3d: case 0x2c61: + regmbc('l'); regmbc(0x13a); regmbc(0x13c); + regmbc(0x13e); regmbc(0x140); regmbc(0x142); + regmbc(0x19a); regmbc(0x1e37); regmbc(0x1e39); + regmbc(0x1e3b); regmbc(0x1e3d); regmbc(0x2c61); return; - case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41) - regmbc('m'); REGMBC(0x1e3f) REGMBC(0x1e41) + case 'm': case 0x1d6f: case 0x1e3f: case 0x1e41: case 0x1e43: + regmbc('m'); regmbc(0x1d6f); regmbc(0x1e3f); + regmbc(0x1e41); regmbc(0x1e43); return; - case 'n': case 0xf1: - CASEMBC(0x144) CASEMBC(0x146) CASEMBC(0x148) CASEMBC(0x149) - CASEMBC(0x1e45) CASEMBC(0x1e49) - regmbc('n'); regmbc(0xf1); - REGMBC(0x144) REGMBC(0x146) REGMBC(0x148) - REGMBC(0x149) REGMBC(0x1e45) REGMBC(0x1e49) + case 'n': case 0xf1: case 0x144: case 0x146: case 0x148: + case 0x149: case 0x1f9: case 0x1d70: case 0x1d87: + case 0x1e45: case 0x1e47: case 0x1e49: case 0x1e4b: + case 0xa7a5: + regmbc('n'); regmbc(0xf1); regmbc(0x144); + regmbc(0x146); regmbc(0x148); regmbc(0x149); + regmbc(0x1f9); regmbc(0x1d70); regmbc(0x1d87); + regmbc(0x1e45); regmbc(0x1e47); regmbc(0x1e49); + regmbc(0x1e4b); regmbc(0xa7a5); return; case 'o': case 0xf2: case 0xf3: case 0xf4: case 0xf5: - case 0xf6: case 0xf8: - CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) CASEMBC(0x1a1) - CASEMBC(0x1d2) CASEMBC(0x1eb) CASEMBC(0x1ed) CASEMBC(0x1ecf) + case 0xf6: case 0xf8: case 0x14d: case 0x14f: case 0x151: + case 0x1a1: case 0x1d2: case 0x1eb: case 0x1ed: case 0x1ff: + case 0x20d: case 0x20f: case 0x22b: case 0x22d: case 0x22f: + case 0x231: case 0x275: case 0x1e4d: case 0x1e4f: + case 0x1e51: case 0x1e53: case 0x1ecd: case 0x1ecf: + case 0x1ed1: case 0x1ed3: case 0x1ed5: case 0x1ed7: + case 0x1ed9: case 0x1edb: case 0x1edd: case 0x1edf: + case 0x1ee1: case 0x1ee3: regmbc('o'); regmbc(0xf2); regmbc(0xf3); regmbc(0xf4); regmbc(0xf5); regmbc(0xf6); - regmbc(0xf8); - REGMBC(0x14d) REGMBC(0x14f) REGMBC(0x151) - REGMBC(0x1a1) REGMBC(0x1d2) REGMBC(0x1eb) - REGMBC(0x1ed) REGMBC(0x1ecf) + regmbc(0xf8); regmbc(0x14d); regmbc(0x14f); + regmbc(0x151); regmbc(0x1a1); regmbc(0x1d2); + regmbc(0x1eb); regmbc(0x1ed); regmbc(0x1ff); + regmbc(0x20d); regmbc(0x20f); regmbc(0x22b); + regmbc(0x22d); regmbc(0x22f); regmbc(0x231); + regmbc(0x275); regmbc(0x1e4d); regmbc(0x1e4f); + regmbc(0x1e51); regmbc(0x1e53); regmbc(0x1ecd); + regmbc(0x1ecf); regmbc(0x1ed1); regmbc(0x1ed3); + regmbc(0x1ed5); regmbc(0x1ed7); regmbc(0x1ed9); + regmbc(0x1edb); regmbc(0x1edd); regmbc(0x1edf); + regmbc(0x1ee1); regmbc(0x1ee3); + return; + case 'p': case 0x1a5: case 0x1d71: case 0x1d88: case 0x1d7d: + case 0x1e55: case 0x1e57: + regmbc('p'); regmbc(0x1a5); regmbc(0x1d71); + regmbc(0x1d7d); regmbc(0x1d88); regmbc(0x1e55); + regmbc(0x1e57); return; - case 'p': CASEMBC(0x1e55) CASEMBC(0x1e57) - regmbc('p'); REGMBC(0x1e55) REGMBC(0x1e57) + case 'q': case 0x24b: case 0x2a0: + regmbc('q'); regmbc(0x24b); regmbc(0x2a0); return; - case 'r': CASEMBC(0x155) CASEMBC(0x157) CASEMBC(0x159) - CASEMBC(0x1e59) CASEMBC(0x1e5f) - regmbc('r'); REGMBC(0x155) REGMBC(0x157) REGMBC(0x159) - REGMBC(0x1e59) REGMBC(0x1e5f) + case 'r': case 0x155: case 0x157: case 0x159: case 0x211: + case 0x213: case 0x24d: case 0x27d: case 0x1d72: case 0x1d73: + case 0x1d89: case 0x1e59: case 0x1e5b: case 0x1e5d: case 0x1e5f: + case 0xa7a7: + regmbc('r'); regmbc(0x155); regmbc(0x157); + regmbc(0x159); regmbc(0x211); regmbc(0x213); + regmbc(0x24d); regmbc(0x1d72); regmbc(0x1d73); + regmbc(0x1d89); regmbc(0x1e59); regmbc(0x27d); + regmbc(0x1e5b); regmbc(0x1e5d); regmbc(0x1e5f); + regmbc(0xa7a7); return; - case 's': CASEMBC(0x15b) CASEMBC(0x15d) CASEMBC(0x15f) - CASEMBC(0x161) CASEMBC(0x1e61) - regmbc('s'); REGMBC(0x15b) REGMBC(0x15d) - REGMBC(0x15f) REGMBC(0x161) REGMBC(0x1e61) + case 's': case 0x15b: case 0x15d: case 0x15f: case 0x161: + case 0x1e61: case 0x219: case 0x23f: case 0x1d74: case 0x1d8a: + case 0x1e63: case 0x1e65: case 0x1e67: case 0x1e69: case 0xa7a9: + regmbc('s'); regmbc(0x15b); regmbc(0x15d); + regmbc(0x15f); regmbc(0x161); regmbc(0x23f); + regmbc(0x219); regmbc(0x1d74); regmbc(0x1d8a); + regmbc(0x1e61); regmbc(0x1e63); regmbc(0x1e65); + regmbc(0x1e67); regmbc(0x1e69); regmbc(0xa7a9); return; - case 't': CASEMBC(0x163) CASEMBC(0x165) CASEMBC(0x167) - CASEMBC(0x1e6b) CASEMBC(0x1e6f) CASEMBC(0x1e97) - regmbc('t'); REGMBC(0x163) REGMBC(0x165) REGMBC(0x167) - REGMBC(0x1e6b) REGMBC(0x1e6f) REGMBC(0x1e97) + case 't': case 0x163: case 0x165: case 0x167: case 0x1ab: + case 0x1ad: case 0x21b: case 0x288: case 0x1d75: case 0x1e6b: + case 0x1e6d: case 0x1e6f: case 0x1e71: case 0x1e97: case 0x2c66: + regmbc('t'); regmbc(0x163); regmbc(0x165); + regmbc(0x167); regmbc(0x1ab); regmbc(0x21b); + regmbc(0x1ad); regmbc(0x288); regmbc(0x1d75); + regmbc(0x1e6b); regmbc(0x1e6d); regmbc(0x1e6f); + regmbc(0x1e71); regmbc(0x1e97); regmbc(0x2c66); return; case 'u': case 0xf9: case 0xfa: case 0xfb: case 0xfc: - CASEMBC(0x169) CASEMBC(0x16b) CASEMBC(0x16d) CASEMBC(0x16f) - CASEMBC(0x171) CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4) - CASEMBC(0x1ee7) + case 0x169: case 0x16b: case 0x16d: case 0x16f: + case 0x171: case 0x173: case 0x1b0: case 0x1d4: + case 0x1d6: case 0x1d8: case 0x1da: case 0x1dc: + case 0x215: case 0x217: case 0x289: case 0x1e73: + case 0x1d7e: case 0x1d99: case 0x1e75: case 0x1e77: + case 0x1e79: case 0x1e7b: case 0x1ee5: case 0x1ee7: + case 0x1ee9: case 0x1eeb: case 0x1eed: case 0x1eef: + case 0x1ef1: regmbc('u'); regmbc(0xf9); regmbc(0xfa); - regmbc(0xfb); regmbc(0xfc); - REGMBC(0x169) REGMBC(0x16b) REGMBC(0x16d) - REGMBC(0x16f) REGMBC(0x171) REGMBC(0x173) - REGMBC(0x1b0) REGMBC(0x1d4) REGMBC(0x1ee7) + regmbc(0xfb); regmbc(0xfc); regmbc(0x169); + regmbc(0x16b); regmbc(0x16d); regmbc(0x16f); + regmbc(0x171); regmbc(0x173); regmbc(0x1d6); + regmbc(0x1d8); regmbc(0x1da); regmbc(0x1dc); + regmbc(0x215); regmbc(0x217); regmbc(0x1b0); + regmbc(0x1d4); regmbc(0x289); regmbc(0x1d7e); + regmbc(0x1d99); regmbc(0x1e73); regmbc(0x1e75); + regmbc(0x1e77); regmbc(0x1e79); regmbc(0x1e7b); + regmbc(0x1ee5); regmbc(0x1ee7); regmbc(0x1ee9); + regmbc(0x1eeb); regmbc(0x1eed); regmbc(0x1eef); + regmbc(0x1ef1); return; - case 'v': CASEMBC(0x1e7d) - regmbc('v'); REGMBC(0x1e7d) + case 'v': case 0x28b: case 0x1d8c: case 0x1e7d: case 0x1e7f: + regmbc('v'); regmbc(0x28b); regmbc(0x1d8c); + regmbc(0x1e7d); regmbc(0x1e7f); return; - case 'w': CASEMBC(0x175) CASEMBC(0x1e81) CASEMBC(0x1e83) - CASEMBC(0x1e85) CASEMBC(0x1e87) CASEMBC(0x1e98) - regmbc('w'); REGMBC(0x175) REGMBC(0x1e81) - REGMBC(0x1e83) REGMBC(0x1e85) REGMBC(0x1e87) - REGMBC(0x1e98) + case 'w': case 0x175: case 0x1e81: case 0x1e83: + case 0x1e85: case 0x1e87: case 0x1e89: case 0x1e98: + regmbc('w'); regmbc(0x175); regmbc(0x1e81); + regmbc(0x1e83); regmbc(0x1e85); regmbc(0x1e87); + regmbc(0x1e89); regmbc(0x1e98); return; - case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d) - regmbc('x'); REGMBC(0x1e8b) REGMBC(0x1e8d) + case 'x': case 0x1e8b: case 0x1e8d: + regmbc('x'); regmbc(0x1e8b); regmbc(0x1e8d); return; - case 'y': case 0xfd: case 0xff: - CASEMBC(0x177) CASEMBC(0x1e8f) CASEMBC(0x1e99) - CASEMBC(0x1ef3) CASEMBC(0x1ef7) CASEMBC(0x1ef9) + case 'y': case 0xfd: case 0xff: case 0x177: case 0x1b4: + case 0x233: case 0x24f: case 0x1e8f: case 0x1e99: case 0x1ef3: + case 0x1ef5: case 0x1ef7: case 0x1ef9: regmbc('y'); regmbc(0xfd); regmbc(0xff); - REGMBC(0x177) REGMBC(0x1e8f) REGMBC(0x1e99) - REGMBC(0x1ef3) REGMBC(0x1ef7) REGMBC(0x1ef9) + regmbc(0x177); regmbc(0x1b4); regmbc(0x233); + regmbc(0x24f); regmbc(0x1e8f); regmbc(0x1e99); + regmbc(0x1ef3); regmbc(0x1ef5); regmbc(0x1ef7); + regmbc(0x1ef9); return; - case 'z': CASEMBC(0x17a) CASEMBC(0x17c) CASEMBC(0x17e) - CASEMBC(0x1b6) CASEMBC(0x1e91) CASEMBC(0x1e95) - regmbc('z'); REGMBC(0x17a) REGMBC(0x17c) - REGMBC(0x17e) REGMBC(0x1b6) REGMBC(0x1e91) - REGMBC(0x1e95) + case 'z': case 0x17a: case 0x17c: case 0x17e: case 0x1b6: + case 0x1d76: case 0x1d8e: case 0x1e91: case 0x1e93: + case 0x1e95: case 0x2c6c: + regmbc('z'); regmbc(0x17a); regmbc(0x17c); + regmbc(0x17e); regmbc(0x1b6); regmbc(0x1d76); + regmbc(0x1d8e); regmbc(0x1e91); regmbc(0x1e93); + regmbc(0x1e95); regmbc(0x2c6c); return; } } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 133858f113..ecc8a4703c 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -711,7 +711,6 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl) static void nfa_emit_equi_class(int c) { #define EMIT2(c) EMIT(c); EMIT(NFA_CONCAT); -#define EMITMBC(c) EMIT(c); EMIT(NFA_CONCAT); { #define A_grave 0xc0 @@ -771,361 +770,519 @@ static void nfa_emit_equi_class(int c) #define y_diaeresis 0xff switch (c) { case 'A': case A_grave: case A_acute: case A_circumflex: - case A_virguilla: case A_diaeresis: case A_ring: - CASEMBC(0x100) CASEMBC(0x102) CASEMBC(0x104) - CASEMBC(0x1cd) CASEMBC(0x1de) CASEMBC(0x1e0) - CASEMBC(0x1ea2) - EMIT2('A'); EMIT2(A_grave); EMIT2(A_acute); - EMIT2(A_circumflex); EMIT2(A_virguilla); - EMIT2(A_diaeresis); EMIT2(A_ring); - EMITMBC(0x100) EMITMBC(0x102) EMITMBC(0x104) - EMITMBC(0x1cd) EMITMBC(0x1de) EMITMBC(0x1e0) - EMITMBC(0x1ea2) + case A_virguilla: case A_diaeresis: case A_ring: + case 0x100: case 0x102: case 0x104: case 0x1cd: + case 0x1de: case 0x1e0: case 0x1fa: case 0x200: + case 0x202: case 0x226: case 0x23a: case 0x1e00: + case 0x1ea0: case 0x1ea2: case 0x1ea4: case 0x1ea6: + case 0x1ea8: case 0x1eaa: case 0x1eac: case 0x1eae: + case 0x1eb0: case 0x1eb2: case 0x1eb4: case 0x1eb6: + EMIT2('A') EMIT2(A_grave) EMIT2(A_acute) + EMIT2(A_circumflex) EMIT2(A_virguilla) + EMIT2(A_diaeresis) EMIT2(A_ring) + EMIT2(0x100) EMIT2(0x102) EMIT2(0x104) + EMIT2(0x1cd) EMIT2(0x1de) EMIT2(0x1e0) + EMIT2(0x1fa) EMIT2(0x200) EMIT2(0x202) + EMIT2(0x226) EMIT2(0x23a) EMIT2(0x1e00) + EMIT2(0x1ea0) EMIT2(0x1ea2) EMIT2(0x1ea4) + EMIT2(0x1ea6) EMIT2(0x1ea8) EMIT2(0x1eaa) + EMIT2(0x1eac) EMIT2(0x1eae) EMIT2(0x1eb0) + EMIT2(0x1eb2) EMIT2(0x1eb6) EMIT2(0x1eb4) return; - case 'B': CASEMBC(0x1e02) CASEMBC(0x1e06) - EMIT2('B'); EMITMBC(0x1e02) EMITMBC(0x1e06) + case 'B': case 0x181: case 0x243: case 0x1e02: + case 0x1e04: case 0x1e06: + EMIT2('B') + EMIT2(0x181) EMIT2(0x243) EMIT2(0x1e02) + EMIT2(0x1e04) EMIT2(0x1e06) return; - case 'C': case C_cedilla: CASEMBC(0x106) CASEMBC(0x108) CASEMBC(0x10a) - CASEMBC(0x10c) - EMIT2('C'); EMIT2(C_cedilla); EMITMBC(0x106) EMITMBC(0x108) - EMITMBC(0x10a) EMITMBC(0x10c) + case 'C': case C_cedilla: case 0x106: case 0x108: + case 0x10a: case 0x10c: case 0x187: case 0x23b: + case 0x1e08: case 0xa792: + EMIT2('C') EMIT2(C_cedilla) + EMIT2(0x106) EMIT2(0x108) EMIT2(0x10a) + EMIT2(0x10c) EMIT2(0x187) EMIT2(0x23b) + EMIT2(0x1e08) EMIT2(0xa792) return; - case 'D': CASEMBC(0x10e) CASEMBC(0x110) CASEMBC(0x1e0a) - CASEMBC(0x1e0e) CASEMBC(0x1e10) - EMIT2('D'); EMITMBC(0x10e) EMITMBC(0x110) EMITMBC(0x1e0a) - EMITMBC(0x1e0e) EMITMBC(0x1e10) + case 'D': case 0x10e: case 0x110: case 0x18a: + case 0x1e0a: case 0x1e0c: case 0x1e0e: case 0x1e10: + case 0x1e12: + EMIT2('D') EMIT2(0x10e) EMIT2(0x110) EMIT2(0x18a) + EMIT2(0x1e0a) EMIT2(0x1e0c) EMIT2(0x1e0e) + EMIT2(0x1e10) EMIT2(0x1e12) return; case 'E': case E_grave: case E_acute: case E_circumflex: - case E_diaeresis: CASEMBC(0x112) CASEMBC(0x114) - CASEMBC(0x116) CASEMBC(0x118) CASEMBC(0x11a) - CASEMBC(0x1eba) CASEMBC(0x1ebc) - EMIT2('E'); EMIT2(E_grave); EMIT2(E_acute); - EMIT2(E_circumflex); EMIT2(E_diaeresis); - EMITMBC(0x112) EMITMBC(0x114) EMITMBC(0x116) - EMITMBC(0x118) EMITMBC(0x11a) EMITMBC(0x1eba) - EMITMBC(0x1ebc) + case E_diaeresis: case 0x112: case 0x114: case 0x116: + case 0x118: case 0x11a: case 0x204: case 0x206: + case 0x228: case 0x246: case 0x1e14: case 0x1e16: + case 0x1e18: case 0x1e1a: case 0x1e1c: case 0x1eb8: + case 0x1eba: case 0x1ebc: case 0x1ebe: case 0x1ec0: + case 0x1ec2: case 0x1ec4: case 0x1ec6: + EMIT2('E') EMIT2(E_grave) EMIT2(E_acute) + EMIT2(E_circumflex) EMIT2(E_diaeresis) + EMIT2(0x112) EMIT2(0x114) EMIT2(0x116) + EMIT2(0x118) EMIT2(0x11a) EMIT2(0x204) + EMIT2(0x206) EMIT2(0x228) EMIT2(0x246) + EMIT2(0x1e14) EMIT2(0x1e16) EMIT2(0x1e18) + EMIT2(0x1e1a) EMIT2(0x1e1c) EMIT2(0x1eb8) + EMIT2(0x1eba) EMIT2(0x1ebc) EMIT2(0x1ebe) + EMIT2(0x1ec0) EMIT2(0x1ec2) EMIT2(0x1ec4) + EMIT2(0x1ec6) return; - case 'F': CASEMBC(0x1e1e) - EMIT2('F'); EMITMBC(0x1e1e) + case 'F': case 0x191: case 0x1e1e: case 0xa798: + EMIT2('F') EMIT2(0x191) EMIT2(0x1e1e) EMIT2(0xa798) return; - case 'G': CASEMBC(0x11c) CASEMBC(0x11e) CASEMBC(0x120) - CASEMBC(0x122) CASEMBC(0x1e4) CASEMBC(0x1e6) - CASEMBC(0x1f4) CASEMBC(0x1e20) - EMIT2('G'); EMITMBC(0x11c) EMITMBC(0x11e) EMITMBC(0x120) - EMITMBC(0x122) EMITMBC(0x1e4) EMITMBC(0x1e6) - EMITMBC(0x1f4) EMITMBC(0x1e20) + case 'G': case 0x11c: case 0x11e: case 0x120: + case 0x122: case 0x193: case 0x1e4: case 0x1e6: + case 0x1f4: case 0x1e20: case 0xa7a0: + EMIT2('G') EMIT2(0x11c) EMIT2(0x11e) EMIT2(0x120) + EMIT2(0x122) EMIT2(0x193) EMIT2(0x1e4) + EMIT2(0x1e6) EMIT2(0x1f4) EMIT2(0x1e20) + EMIT2(0xa7a0) return; - case 'H': CASEMBC(0x124) CASEMBC(0x126) CASEMBC(0x1e22) - CASEMBC(0x1e26) CASEMBC(0x1e28) - EMIT2('H'); EMITMBC(0x124) EMITMBC(0x126) EMITMBC(0x1e22) - EMITMBC(0x1e26) EMITMBC(0x1e28) + case 'H': case 0x124: case 0x126: case 0x21e: + case 0x1e22: case 0x1e24: case 0x1e26: case 0x1e28: + case 0x1e2a: case 0x2c67: + EMIT2('H') EMIT2(0x124) EMIT2(0x126) EMIT2(0x21e) + EMIT2(0x1e22) EMIT2(0x1e24) EMIT2(0x1e26) + EMIT2(0x1e28) EMIT2(0x1e2a) EMIT2(0x2c67) return; case 'I': case I_grave: case I_acute: case I_circumflex: - case I_diaeresis: CASEMBC(0x128) CASEMBC(0x12a) - CASEMBC(0x12c) CASEMBC(0x12e) CASEMBC(0x130) - CASEMBC(0x1cf) CASEMBC(0x1ec8) - EMIT2('I'); EMIT2(I_grave); EMIT2(I_acute); - EMIT2(I_circumflex); EMIT2(I_diaeresis); - EMITMBC(0x128) EMITMBC(0x12a) - EMITMBC(0x12c) EMITMBC(0x12e) EMITMBC(0x130) - EMITMBC(0x1cf) EMITMBC(0x1ec8) + case I_diaeresis: case 0x128: case 0x12a: case 0x12c: + case 0x12e: case 0x130: case 0x197: case 0x1cf: + case 0x208: case 0x20a: case 0x1e2c: case 0x1e2e: + case 0x1ec8: case 0x1eca: + EMIT2('I') EMIT2(I_grave) EMIT2(I_acute) + EMIT2(I_circumflex) EMIT2(I_diaeresis) + EMIT2(0x128) EMIT2(0x12a) EMIT2(0x12c) + EMIT2(0x12e) EMIT2(0x130) EMIT2(0x197) + EMIT2(0x1cf) EMIT2(0x208) EMIT2(0x20a) + EMIT2(0x1e2c) EMIT2(0x1e2e) EMIT2(0x1ec8) + EMIT2(0x1eca) return; - case 'J': CASEMBC(0x134) - EMIT2('J'); EMITMBC(0x134) + case 'J': case 0x134: case 0x248: + EMIT2('J') EMIT2(0x134) EMIT2(0x248) return; - case 'K': CASEMBC(0x136) CASEMBC(0x1e8) CASEMBC(0x1e30) - CASEMBC(0x1e34) - EMIT2('K'); EMITMBC(0x136) EMITMBC(0x1e8) EMITMBC(0x1e30) - EMITMBC(0x1e34) + case 'K': case 0x136: case 0x198: case 0x1e8: case 0x1e30: + case 0x1e32: case 0x1e34: case 0x2c69: case 0xa740: + EMIT2('K') EMIT2(0x136) EMIT2(0x198) EMIT2(0x1e8) + EMIT2(0x1e30) EMIT2(0x1e32) EMIT2(0x1e34) + EMIT2(0x2c69) EMIT2(0xa740) return; - case 'L': CASEMBC(0x139) CASEMBC(0x13b) CASEMBC(0x13d) - CASEMBC(0x13f) CASEMBC(0x141) CASEMBC(0x1e3a) - EMIT2('L'); EMITMBC(0x139) EMITMBC(0x13b) EMITMBC(0x13d) - EMITMBC(0x13f) EMITMBC(0x141) EMITMBC(0x1e3a) + case 'L': case 0x139: case 0x13b: case 0x13d: + case 0x13f: case 0x141: case 0x23d: case 0x1e36: + case 0x1e38: case 0x1e3a: case 0x1e3c: case 0x2c60: + EMIT2('L') EMIT2(0x139) EMIT2(0x13b) + EMIT2(0x13d) EMIT2(0x13f) EMIT2(0x141) + EMIT2(0x23d) EMIT2(0x1e36) EMIT2(0x1e38) + EMIT2(0x1e3a) EMIT2(0x1e3c) EMIT2(0x2c60) return; - case 'M': CASEMBC(0x1e3e) CASEMBC(0x1e40) - EMIT2('M'); EMITMBC(0x1e3e) EMITMBC(0x1e40) + case 'M': case 0x1e3e: case 0x1e40: case 0x1e42: + EMIT2('M') EMIT2(0x1e3e) EMIT2(0x1e40) + EMIT2(0x1e42) return; - case 'N': case N_virguilla: CASEMBC(0x143) CASEMBC(0x145) - CASEMBC(0x147) CASEMBC(0x1e44) CASEMBC(0x1e48) - EMIT2('N'); EMIT2(N_virguilla); - EMITMBC(0x143) EMITMBC(0x145) - EMITMBC(0x147) EMITMBC(0x1e44) EMITMBC(0x1e48) + case 'N': case N_virguilla: + case 0x143: case 0x145: case 0x147: case 0x1f8: + case 0x1e44: case 0x1e46: case 0x1e48: case 0x1e4a: + case 0xa7a4: + EMIT2('N') EMIT2(N_virguilla) + EMIT2(0x143) EMIT2(0x145) EMIT2(0x147) + EMIT2(0x1f8) EMIT2(0x1e44) EMIT2(0x1e46) + EMIT2(0x1e48) EMIT2(0x1e4a) EMIT2(0xa7a4) return; case 'O': case O_grave: case O_acute: case O_circumflex: - case O_virguilla: case O_diaeresis: case O_slash: - CASEMBC(0x14c) CASEMBC(0x14e) CASEMBC(0x150) - CASEMBC(0x1a0) CASEMBC(0x1d1) CASEMBC(0x1ea) - CASEMBC(0x1ec) CASEMBC(0x1ece) - EMIT2('O'); EMIT2(O_grave); EMIT2(O_acute); - EMIT2(O_circumflex); EMIT2(O_virguilla); - EMIT2(O_diaeresis); EMIT2(O_slash); - EMITMBC(0x14c) EMITMBC(0x14e) EMITMBC(0x150) - EMITMBC(0x1a0) EMITMBC(0x1d1) EMITMBC(0x1ea) - EMITMBC(0x1ec) EMITMBC(0x1ece) + case O_virguilla: case O_diaeresis: case O_slash: + case 0x14c: case 0x14e: case 0x150: case 0x19f: + case 0x1a0: case 0x1d1: case 0x1ea: case 0x1ec: + case 0x1fe: case 0x20c: case 0x20e: case 0x22a: + case 0x22c: case 0x22e: case 0x230: case 0x1e4c: + case 0x1e4e: case 0x1e50: case 0x1e52: case 0x1ecc: + case 0x1ece: case 0x1ed0: case 0x1ed2: case 0x1ed4: + case 0x1ed6: case 0x1ed8: case 0x1eda: case 0x1edc: + case 0x1ede: case 0x1ee0: case 0x1ee2: + EMIT2('O') EMIT2(O_grave) EMIT2(O_acute) + EMIT2(O_circumflex) EMIT2(O_virguilla) + EMIT2(O_diaeresis) EMIT2(O_slash) + EMIT2(0x14c) EMIT2(0x14e) EMIT2(0x150) + EMIT2(0x19f) EMIT2(0x1a0) EMIT2(0x1d1) + EMIT2(0x1ea) EMIT2(0x1ec) EMIT2(0x1fe) + EMIT2(0x20c) EMIT2(0x20e) EMIT2(0x22a) + EMIT2(0x22c) EMIT2(0x22e) EMIT2(0x230) + EMIT2(0x1e4c) EMIT2(0x1e4e) EMIT2(0x1e50) + EMIT2(0x1e52) EMIT2(0x1ecc) EMIT2(0x1ece) + EMIT2(0x1ed0) EMIT2(0x1ed2) EMIT2(0x1ed4) + EMIT2(0x1ed6) EMIT2(0x1ed8) EMIT2(0x1eda) + EMIT2(0x1edc) EMIT2(0x1ede) EMIT2(0x1ee0) + EMIT2(0x1ee2) return; - case 'P': case 0x1e54: case 0x1e56: - EMIT2('P'); EMITMBC(0x1e54) EMITMBC(0x1e56) + case 'P': case 0x1a4: case 0x1e54: case 0x1e56: case 0x2c63: + EMIT2('P') EMIT2(0x1a4) EMIT2(0x1e54) EMIT2(0x1e56) + EMIT2(0x2c63) return; - case 'R': CASEMBC(0x154) CASEMBC(0x156) CASEMBC(0x158) - CASEMBC(0x1e58) CASEMBC(0x1e5e) - EMIT2('R'); EMITMBC(0x154) EMITMBC(0x156) EMITMBC(0x158) - EMITMBC(0x1e58) EMITMBC(0x1e5e) + case 'Q': case 0x24a: + EMIT2('Q') EMIT2(0x24a) return; - case 'S': CASEMBC(0x15a) CASEMBC(0x15c) CASEMBC(0x15e) - CASEMBC(0x160) CASEMBC(0x1e60) - EMIT2('S'); EMITMBC(0x15a) EMITMBC(0x15c) EMITMBC(0x15e) - EMITMBC(0x160) EMITMBC(0x1e60) + case 'R': case 0x154: case 0x156: case 0x158: case 0x210: + case 0x212: case 0x24c: case 0x1e58: case 0x1e5a: + case 0x1e5c: case 0x1e5e: case 0x2c64: case 0xa7a6: + EMIT2('R') EMIT2(0x154) EMIT2(0x156) EMIT2(0x158) + EMIT2(0x210) EMIT2(0x212) EMIT2(0x24c) EMIT2(0x1e58) + EMIT2(0x1e5a) EMIT2(0x1e5c) EMIT2(0x1e5e) EMIT2(0x2c64) + EMIT2(0xa7a6) return; - case 'T': CASEMBC(0x162) CASEMBC(0x164) CASEMBC(0x166) - CASEMBC(0x1e6a) CASEMBC(0x1e6e) - EMIT2('T'); EMITMBC(0x162) EMITMBC(0x164) EMITMBC(0x166) - EMITMBC(0x1e6a) EMITMBC(0x1e6e) + case 'S': case 0x15a: case 0x15c: case 0x15e: case 0x160: + case 0x218: case 0x1e60: case 0x1e62: case 0x1e64: + case 0x1e66: case 0x1e68: case 0x2c7e: case 0xa7a8: + EMIT2('S') EMIT2(0x15a) EMIT2(0x15c) EMIT2(0x15e) + EMIT2(0x160) EMIT2(0x218) EMIT2(0x1e60) EMIT2(0x1e62) + EMIT2(0x1e64) EMIT2(0x1e66) EMIT2(0x1e68) EMIT2(0x2c7e) + EMIT2(0xa7a8) + return; + + case 'T': case 0x162: case 0x164: case 0x166: case 0x1ac: + case 0x1ae: case 0x21a: case 0x23e: case 0x1e6a: case 0x1e6c: + case 0x1e6e: case 0x1e70: + EMIT2('T') EMIT2(0x162) EMIT2(0x164) EMIT2(0x166) + EMIT2(0x1ac) EMIT2(0x1ae) EMIT2(0x23e) EMIT2(0x21a) + EMIT2(0x1e6a) EMIT2(0x1e6c) EMIT2(0x1e6e) EMIT2(0x1e70) return; case 'U': case U_grave: case U_acute: case U_diaeresis: - case U_circumflex: CASEMBC(0x168) CASEMBC(0x16a) - CASEMBC(0x16c) CASEMBC(0x16e) CASEMBC(0x170) - CASEMBC(0x172) CASEMBC(0x1af) CASEMBC(0x1d3) - CASEMBC(0x1ee6) - EMIT2('U'); EMIT2(U_grave); EMIT2(U_acute); - EMIT2(U_diaeresis); EMIT2(U_circumflex); - EMITMBC(0x168) EMITMBC(0x16a) - EMITMBC(0x16c) EMITMBC(0x16e) EMITMBC(0x170) - EMITMBC(0x172) EMITMBC(0x1af) EMITMBC(0x1d3) - EMITMBC(0x1ee6) + case U_circumflex: case 0x168: case 0x16a: case 0x16c: + case 0x16e: case 0x170: case 0x172: case 0x1af: + case 0x1d3: case 0x1d5: case 0x1d7: case 0x1d9: + case 0x1db: case 0x214: case 0x216: case 0x244: + case 0x1e72: case 0x1e74: case 0x1e76: case 0x1e78: + case 0x1e7a: case 0x1ee4: case 0x1ee6: case 0x1ee8: + case 0x1eea: case 0x1eec: case 0x1eee: case 0x1ef0: + EMIT2('U') EMIT2(U_grave) EMIT2(U_acute) + EMIT2(U_diaeresis) EMIT2(U_circumflex) + EMIT2(0x168) EMIT2(0x16a) + EMIT2(0x16c) EMIT2(0x16e) EMIT2(0x170) + EMIT2(0x172) EMIT2(0x1af) EMIT2(0x1d3) + EMIT2(0x1d5) EMIT2(0x1d7) EMIT2(0x1d9) + EMIT2(0x1db) EMIT2(0x214) EMIT2(0x216) + EMIT2(0x244) EMIT2(0x1e72) EMIT2(0x1e74) + EMIT2(0x1e76) EMIT2(0x1e78) EMIT2(0x1e7a) + EMIT2(0x1ee4) EMIT2(0x1ee6) EMIT2(0x1ee8) + EMIT2(0x1eea) EMIT2(0x1eec) EMIT2(0x1eee) + EMIT2(0x1ef0) return; - case 'V': CASEMBC(0x1e7c) - EMIT2('V'); EMITMBC(0x1e7c) + case 'V': case 0x1b2: case 0x1e7c: case 0x1e7e: + EMIT2('V') EMIT2(0x1b2) EMIT2(0x1e7c) EMIT2(0x1e7e) return; - case 'W': CASEMBC(0x174) CASEMBC(0x1e80) CASEMBC(0x1e82) - CASEMBC(0x1e84) CASEMBC(0x1e86) - EMIT2('W'); EMITMBC(0x174) EMITMBC(0x1e80) EMITMBC(0x1e82) - EMITMBC(0x1e84) EMITMBC(0x1e86) + case 'W': case 0x174: case 0x1e80: case 0x1e82: case 0x1e84: + case 0x1e86: case 0x1e88: + EMIT2('W') EMIT2(0x174) EMIT2(0x1e80) EMIT2(0x1e82) + EMIT2(0x1e84) EMIT2(0x1e86) EMIT2(0x1e88) return; - case 'X': CASEMBC(0x1e8a) CASEMBC(0x1e8c) - EMIT2('X'); EMITMBC(0x1e8a) EMITMBC(0x1e8c) + case 'X': case 0x1e8a: case 0x1e8c: + EMIT2('X') EMIT2(0x1e8a) EMIT2(0x1e8c) return; - case 'Y': case Y_acute: CASEMBC(0x176) CASEMBC(0x178) - CASEMBC(0x1e8e) CASEMBC(0x1ef2) CASEMBC(0x1ef6) - CASEMBC(0x1ef8) - EMIT2('Y'); EMIT2(Y_acute); - EMITMBC(0x176) EMITMBC(0x178) - EMITMBC(0x1e8e) EMITMBC(0x1ef2) EMITMBC(0x1ef6) - EMITMBC(0x1ef8) + case 'Y': case Y_acute: case 0x176: case 0x178: + case 0x1b3: case 0x232: case 0x24e: case 0x1e8e: + case 0x1ef2: case 0x1ef4: case 0x1ef6: case 0x1ef8: + EMIT2('Y') EMIT2(Y_acute) + EMIT2(0x176) EMIT2(0x178) EMIT2(0x1b3) + EMIT2(0x232) EMIT2(0x24e) EMIT2(0x1e8e) + EMIT2(0x1ef2) EMIT2(0x1ef4) EMIT2(0x1ef6) + EMIT2(0x1ef8) return; - case 'Z': CASEMBC(0x179) CASEMBC(0x17b) CASEMBC(0x17d) - CASEMBC(0x1b5) CASEMBC(0x1e90) CASEMBC(0x1e94) - EMIT2('Z'); EMITMBC(0x179) EMITMBC(0x17b) EMITMBC(0x17d) - EMITMBC(0x1b5) EMITMBC(0x1e90) EMITMBC(0x1e94) + case 'Z': case 0x179: case 0x17b: case 0x17d: + case 0x1b5: case 0x1e90: case 0x1e92: case 0x1e94: + case 0x2c6b: + EMIT2('Z') EMIT2(0x179) EMIT2(0x17b) EMIT2(0x17d) + EMIT2(0x1b5) EMIT2(0x1e90) EMIT2(0x1e92) + EMIT2(0x1e94) EMIT2(0x2c6b) return; case 'a': case a_grave: case a_acute: case a_circumflex: - case a_virguilla: case a_diaeresis: case a_ring: - CASEMBC(0x101) CASEMBC(0x103) CASEMBC(0x105) - CASEMBC(0x1ce) CASEMBC(0x1df) CASEMBC(0x1e1) - CASEMBC(0x1ea3) - EMIT2('a'); EMIT2(a_grave); EMIT2(a_acute); - EMIT2(a_circumflex); EMIT2(a_virguilla); - EMIT2(a_diaeresis); EMIT2(a_ring); - EMITMBC(0x101) EMITMBC(0x103) EMITMBC(0x105) - EMITMBC(0x1ce) EMITMBC(0x1df) EMITMBC(0x1e1) - EMITMBC(0x1ea3) + case a_virguilla: case a_diaeresis: case a_ring: + case 0x101: case 0x103: case 0x105: case 0x1ce: + case 0x1df: case 0x1e1: case 0x1fb: case 0x201: + case 0x203: case 0x227: case 0x1d8f: case 0x1e01: + case 0x1e9a: case 0x1ea1: case 0x1ea3: case 0x1ea5: + case 0x1ea7: case 0x1ea9: case 0x1eab: case 0x1ead: + case 0x1eaf: case 0x1eb1: case 0x1eb3: case 0x1eb5: + case 0x1eb7: case 0x2c65: + EMIT2('a') EMIT2(a_grave) EMIT2(a_acute) + EMIT2(a_circumflex) EMIT2(a_virguilla) + EMIT2(a_diaeresis) EMIT2(a_ring) + EMIT2(0x101) EMIT2(0x103) EMIT2(0x105) + EMIT2(0x1ce) EMIT2(0x1df) EMIT2(0x1e1) + EMIT2(0x1fb) EMIT2(0x201) EMIT2(0x203) + EMIT2(0x227) EMIT2(0x1d8f) EMIT2(0x1e01) + EMIT2(0x1e9a) EMIT2(0x1ea1) EMIT2(0x1ea3) + EMIT2(0x1ea5) EMIT2(0x1ea7) EMIT2(0x1ea9) + EMIT2(0x1eab) EMIT2(0x1ead) EMIT2(0x1eaf) + EMIT2(0x1eb1) EMIT2(0x1eb3) EMIT2(0x1eb5) + EMIT2(0x1eb7) EMIT2(0x2c65) return; - case 'b': CASEMBC(0x1e03) CASEMBC(0x1e07) - EMIT2('b'); EMITMBC(0x1e03) EMITMBC(0x1e07) + case 'b': case 0x180: case 0x253: case 0x1d6c: case 0x1d80: + case 0x1e03: case 0x1e05: case 0x1e07: + EMIT2('b') EMIT2(0x180) EMIT2(0x253) EMIT2(0x1d6c) + EMIT2(0x1d80) EMIT2(0x1e03) EMIT2(0x1e05) EMIT2(0x1e07) return; - case 'c': case c_cedilla: CASEMBC(0x107) CASEMBC(0x109) - CASEMBC(0x10b) CASEMBC(0x10d) - EMIT2('c'); EMIT2(c_cedilla); - EMITMBC(0x107) EMITMBC(0x109) - EMITMBC(0x10b) EMITMBC(0x10d) + case 'c': case c_cedilla: case 0x107: case 0x109: case 0x10b: + case 0x10d: case 0x188: case 0x23c: case 0x1e09: case 0xa793: + case 0xa794: + EMIT2('c') EMIT2(c_cedilla) + EMIT2(0x107) EMIT2(0x109) EMIT2(0x10b) + EMIT2(0x10d) EMIT2(0x188) EMIT2(0x23c) + EMIT2(0x1e09) EMIT2(0xa793) EMIT2(0xa794) return; - case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b) - CASEMBC(0x1e0f) CASEMBC(0x1e11) - EMIT2('d'); EMITMBC(0x10f) EMITMBC(0x111) EMITMBC(0x1e0b) - EMITMBC(0x1e0f) EMITMBC(0x1e11) + case 'd': case 0x10f: case 0x111: case 0x257: case 0x1d6d: + case 0x1d81: case 0x1d91: case 0x1e0b: case 0x1e0d: case 0x1e0f: + case 0x1e11: case 0x1e13: + EMIT2('d') EMIT2(0x10f) EMIT2(0x111) + EMIT2(0x257) EMIT2(0x1d6d) EMIT2(0x1d81) + EMIT2(0x1d91) EMIT2(0x1e0b) EMIT2(0x1e0d) + EMIT2(0x1e0f) EMIT2(0x1e11) EMIT2(0x1e13) return; case 'e': case e_grave: case e_acute: case e_circumflex: - case e_diaeresis: CASEMBC(0x113) CASEMBC(0x115) - CASEMBC(0x117) CASEMBC(0x119) CASEMBC(0x11b) - CASEMBC(0x1ebb) CASEMBC(0x1ebd) - EMIT2('e'); EMIT2(e_grave); EMIT2(e_acute); - EMIT2(e_circumflex); EMIT2(e_diaeresis); - EMITMBC(0x113) EMITMBC(0x115) - EMITMBC(0x117) EMITMBC(0x119) EMITMBC(0x11b) - EMITMBC(0x1ebb) EMITMBC(0x1ebd) + case e_diaeresis: case 0x113: case 0x115: case 0x117: + case 0x119: case 0x11b: case 0x205: case 0x207: + case 0x229: case 0x247: case 0x1d92: case 0x1e15: + case 0x1e17: case 0x1e19: case 0x1e1b: case 0x1e1d: + case 0x1eb9: case 0x1ebb: case 0x1ebd: case 0x1ebf: + case 0x1ec1: case 0x1ec3: case 0x1ec5: case 0x1ec7: + EMIT2('e') EMIT2(e_grave) EMIT2(e_acute) + EMIT2(e_circumflex) EMIT2(e_diaeresis) + EMIT2(0x113) EMIT2(0x115) + EMIT2(0x117) EMIT2(0x119) EMIT2(0x11b) + EMIT2(0x205) EMIT2(0x207) EMIT2(0x229) + EMIT2(0x247) EMIT2(0x1d92) EMIT2(0x1e15) + EMIT2(0x1e17) EMIT2(0x1e19) EMIT2(0x1e1b) + EMIT2(0x1e1d) EMIT2(0x1eb9) EMIT2(0x1ebb) + EMIT2(0x1ebd) EMIT2(0x1ebf) EMIT2(0x1ec1) + EMIT2(0x1ec3) EMIT2(0x1ec5) EMIT2(0x1ec7) return; - case 'f': CASEMBC(0x1e1f) - EMIT2('f'); EMITMBC(0x1e1f) + case 'f': case 0x192: case 0x1d6e: case 0x1d82: + case 0x1e1f: case 0xa799: + EMIT2('f') EMIT2(0x192) EMIT2(0x1d6e) EMIT2(0x1d82) + EMIT2(0x1e1f) EMIT2(0xa799) return; - case 'g': CASEMBC(0x11d) CASEMBC(0x11f) CASEMBC(0x121) - CASEMBC(0x123) CASEMBC(0x1e5) CASEMBC(0x1e7) - CASEMBC(0x1f5) CASEMBC(0x1e21) - EMIT2('g'); EMITMBC(0x11d) EMITMBC(0x11f) EMITMBC(0x121) - EMITMBC(0x123) EMITMBC(0x1e5) EMITMBC(0x1e7) - EMITMBC(0x1f5) EMITMBC(0x1e21) + case 'g': case 0x11d: case 0x11f: case 0x121: case 0x123: + case 0x1e5: case 0x1e7: case 0x1f5: case 0x260: case 0x1d83: + case 0x1e21: case 0xa7a1: + EMIT2('g') EMIT2(0x11d) EMIT2(0x11f) EMIT2(0x121) + EMIT2(0x123) EMIT2(0x1e5) EMIT2(0x1e7) + EMIT2(0x1f5) EMIT2(0x260) EMIT2(0x1d83) + EMIT2(0x1e21) EMIT2(0xa7a1) return; - case 'h': CASEMBC(0x125) CASEMBC(0x127) CASEMBC(0x1e23) - CASEMBC(0x1e27) CASEMBC(0x1e29) CASEMBC(0x1e96) - EMIT2('h'); EMITMBC(0x125) EMITMBC(0x127) EMITMBC(0x1e23) - EMITMBC(0x1e27) EMITMBC(0x1e29) EMITMBC(0x1e96) + case 'h': case 0x125: case 0x127: case 0x21f: case 0x1e23: + case 0x1e25: case 0x1e27: case 0x1e29: case 0x1e2b: + case 0x1e96: case 0x2c68: case 0xa795: + EMIT2('h') EMIT2(0x125) EMIT2(0x127) EMIT2(0x21f) + EMIT2(0x1e23) EMIT2(0x1e25) EMIT2(0x1e27) + EMIT2(0x1e29) EMIT2(0x1e2b) EMIT2(0x1e96) + EMIT2(0x2c68) EMIT2(0xa795) return; case 'i': case i_grave: case i_acute: case i_circumflex: - case i_diaeresis: CASEMBC(0x129) CASEMBC(0x12b) - CASEMBC(0x12d) CASEMBC(0x12f) CASEMBC(0x1d0) - CASEMBC(0x1ec9) - EMIT2('i'); EMIT2(i_grave); EMIT2(i_acute); - EMIT2(i_circumflex); EMIT2(i_diaeresis); - EMITMBC(0x129) EMITMBC(0x12b) - EMITMBC(0x12d) EMITMBC(0x12f) EMITMBC(0x1d0) - EMITMBC(0x1ec9) + case i_diaeresis: case 0x129: case 0x12b: case 0x12d: + case 0x12f: case 0x1d0: case 0x209: case 0x20b: + case 0x268: case 0x1d96: case 0x1e2d: case 0x1e2f: + case 0x1ec9: case 0x1ecb: + EMIT2('i') EMIT2(i_grave) EMIT2(i_acute) + EMIT2(i_circumflex) EMIT2(i_diaeresis) + EMIT2(0x129) EMIT2(0x12b) EMIT2(0x12d) + EMIT2(0x12f) EMIT2(0x1d0) EMIT2(0x209) + EMIT2(0x20b) EMIT2(0x268) EMIT2(0x1d96) + EMIT2(0x1e2d) EMIT2(0x1e2f) EMIT2(0x1ec9) + EMIT2(0x1ecb) EMIT2(0x1ecb) return; - case 'j': CASEMBC(0x135) CASEMBC(0x1f0) - EMIT2('j'); EMITMBC(0x135) EMITMBC(0x1f0) + case 'j': case 0x135: case 0x1f0: case 0x249: + EMIT2('j') EMIT2(0x135) EMIT2(0x1f0) EMIT2(0x249) return; - case 'k': CASEMBC(0x137) CASEMBC(0x1e9) CASEMBC(0x1e31) - CASEMBC(0x1e35) - EMIT2('k'); EMITMBC(0x137) EMITMBC(0x1e9) EMITMBC(0x1e31) - EMITMBC(0x1e35) + case 'k': case 0x137: case 0x199: case 0x1e9: case 0x1d84: + case 0x1e31: case 0x1e33: case 0x1e35: case 0x2c6a: case 0xa741: + EMIT2('k') EMIT2(0x137) EMIT2(0x199) EMIT2(0x1e9) + EMIT2(0x1d84) EMIT2(0x1e31) EMIT2(0x1e33) + EMIT2(0x1e35) EMIT2(0x2c6a) EMIT2(0xa741) return; - case 'l': CASEMBC(0x13a) CASEMBC(0x13c) CASEMBC(0x13e) - CASEMBC(0x140) CASEMBC(0x142) CASEMBC(0x1e3b) - EMIT2('l'); EMITMBC(0x13a) EMITMBC(0x13c) EMITMBC(0x13e) - EMITMBC(0x140) EMITMBC(0x142) EMITMBC(0x1e3b) + case 'l': case 0x13a: case 0x13c: case 0x13e: case 0x140: + case 0x142: case 0x19a: case 0x1e37: case 0x1e39: case 0x1e3b: + case 0x1e3d: case 0x2c61: + EMIT2('l') EMIT2(0x13a) EMIT2(0x13c) + EMIT2(0x13e) EMIT2(0x140) EMIT2(0x142) + EMIT2(0x19a) EMIT2(0x1e37) EMIT2(0x1e39) + EMIT2(0x1e3b) EMIT2(0x1e3d) EMIT2(0x2c61) return; - case 'm': CASEMBC(0x1e3f) CASEMBC(0x1e41) - EMIT2('m'); EMITMBC(0x1e3f) EMITMBC(0x1e41) + case 'm': case 0x1d6f: case 0x1e3f: case 0x1e41: case 0x1e43: + EMIT2('m') EMIT2(0x1d6f) EMIT2(0x1e3f) + EMIT2(0x1e41) EMIT2(0x1e43) return; - case 'n': case n_virguilla: CASEMBC(0x144) CASEMBC(0x146) - CASEMBC(0x148) CASEMBC(0x149) CASEMBC(0x1e45) - CASEMBC(0x1e49) - EMIT2('n'); EMIT2(n_virguilla); - EMITMBC(0x144) EMITMBC(0x146) - EMITMBC(0x148) EMITMBC(0x149) EMITMBC(0x1e45) - EMITMBC(0x1e49) + case 'n': case n_virguilla: case 0x144: case 0x146: case 0x148: + case 0x149: case 0x1f9: case 0x1d70: case 0x1d87: case 0x1e45: + case 0x1e47: case 0x1e49: case 0x1e4b: case 0xa7a5: + EMIT2('n') EMIT2(n_virguilla) + EMIT2(0x144) EMIT2(0x146) EMIT2(0x148) + EMIT2(0x149) EMIT2(0x1f9) EMIT2(0x1d70) + EMIT2(0x1d87) EMIT2(0x1e45) EMIT2(0x1e47) + EMIT2(0x1e49) EMIT2(0x1e4b) EMIT2(0xa7a5) return; case 'o': case o_grave: case o_acute: case o_circumflex: - case o_virguilla: case o_diaeresis: case o_slash: - CASEMBC(0x14d) CASEMBC(0x14f) CASEMBC(0x151) - CASEMBC(0x1a1) CASEMBC(0x1d2) CASEMBC(0x1eb) - CASEMBC(0x1ed) CASEMBC(0x1ecf) - EMIT2('o'); EMIT2(o_grave); EMIT2(o_acute); - EMIT2(o_circumflex); EMIT2(o_virguilla); - EMIT2(o_diaeresis); EMIT2(o_slash); - EMITMBC(0x14d) EMITMBC(0x14f) EMITMBC(0x151) - EMITMBC(0x1a1) EMITMBC(0x1d2) EMITMBC(0x1eb) - EMITMBC(0x1ed) EMITMBC(0x1ecf) + case o_virguilla: case o_diaeresis: case o_slash: + case 0x14d: case 0x14f: case 0x151: case 0x1a1: + case 0x1d2: case 0x1eb: case 0x1ed: case 0x1ff: + case 0x20d: case 0x20f: case 0x22b: case 0x22d: + case 0x22f: case 0x231: case 0x275: case 0x1e4d: + case 0x1e4f: case 0x1e51: case 0x1e53: case 0x1ecd: + case 0x1ecf: case 0x1ed1: case 0x1ed3: case 0x1ed5: + case 0x1ed7: case 0x1ed9: case 0x1edb: case 0x1edd: + case 0x1edf: case 0x1ee1: case 0x1ee3: + EMIT2('o') EMIT2(o_grave) EMIT2(o_acute) + EMIT2(o_circumflex) EMIT2(o_virguilla) + EMIT2(o_diaeresis) EMIT2(o_slash) + EMIT2(0x14d) EMIT2(0x14f) EMIT2(0x151) + EMIT2(0x1a1) EMIT2(0x1d2) EMIT2(0x1eb) + EMIT2(0x1ed) EMIT2(0x1ff) EMIT2(0x20d) + EMIT2(0x20f) EMIT2(0x22b) EMIT2(0x22d) + EMIT2(0x22f) EMIT2(0x231) EMIT2(0x275) + EMIT2(0x1e4d) EMIT2(0x1e4f) EMIT2(0x1e51) + EMIT2(0x1e53) EMIT2(0x1ecd) EMIT2(0x1ecf) + EMIT2(0x1ed1) EMIT2(0x1ed3) EMIT2(0x1ed5) + EMIT2(0x1ed7) EMIT2(0x1ed9) EMIT2(0x1edb) + EMIT2(0x1edd) EMIT2(0x1edf) EMIT2(0x1ee1) + EMIT2(0x1ee3) return; - case 'p': CASEMBC(0x1e55) CASEMBC(0x1e57) - EMIT2('p'); EMITMBC(0x1e55) EMITMBC(0x1e57) + case 'p': case 0x1a5: case 0x1d71: case 0x1d7d: case 0x1d88: + case 0x1e55: case 0x1e57: + EMIT2('p') EMIT2(0x1a5) EMIT2(0x1d71) EMIT2(0x1d7d) + EMIT2(0x1d88) EMIT2(0x1e55) EMIT2(0x1e57) return; - case 'r': CASEMBC(0x155) CASEMBC(0x157) CASEMBC(0x159) - CASEMBC(0x1e59) CASEMBC(0x1e5f) - EMIT2('r'); EMITMBC(0x155) EMITMBC(0x157) EMITMBC(0x159) - EMITMBC(0x1e59) EMITMBC(0x1e5f) + case 'q': case 0x24b: case 0x2a0: + EMIT2('q') EMIT2(0x24b) EMIT2(0x2a0) return; - case 's': CASEMBC(0x15b) CASEMBC(0x15d) CASEMBC(0x15f) - CASEMBC(0x161) CASEMBC(0x1e61) - EMIT2('s'); EMITMBC(0x15b) EMITMBC(0x15d) EMITMBC(0x15f) - EMITMBC(0x161) EMITMBC(0x1e61) + case 'r': case 0x155: case 0x157: case 0x159: case 0x211: + case 0x213: case 0x24d: case 0x27d: case 0x1d72: case 0x1d73: + case 0x1d89: case 0x1e59: case 0x1e5b: case 0x1e5d: case 0x1e5f: + case 0xa7a7: + EMIT2('r') EMIT2(0x155) EMIT2(0x157) EMIT2(0x159) + EMIT2(0x211) EMIT2(0x213) EMIT2(0x24d) EMIT2(0x27d) + EMIT2(0x1d72) EMIT2(0x1d73) EMIT2(0x1d89) EMIT2(0x1e59) + EMIT2(0x1e5b) EMIT2(0x1e5d) EMIT2(0x1e5f) EMIT2(0xa7a7) return; - case 't': CASEMBC(0x163) CASEMBC(0x165) CASEMBC(0x167) - CASEMBC(0x1e6b) CASEMBC(0x1e6f) CASEMBC(0x1e97) - EMIT2('t'); EMITMBC(0x163) EMITMBC(0x165) EMITMBC(0x167) - EMITMBC(0x1e6b) EMITMBC(0x1e6f) EMITMBC(0x1e97) + case 's': case 0x15b: case 0x15d: case 0x15f: case 0x161: + case 0x219: case 0x23f: case 0x1d74: case 0x1d8a: case 0x1e61: + case 0x1e63: case 0x1e65: case 0x1e67: case 0x1e69: case 0xa7a9: + EMIT2('s') EMIT2(0x15b) EMIT2(0x15d) EMIT2(0x15f) + EMIT2(0x161) EMIT2(0x219) EMIT2(0x23f) EMIT2(0x1d74) + EMIT2(0x1d8a) EMIT2(0x1e61) EMIT2(0x1e63) EMIT2(0x1e65) + EMIT2(0x1e67) EMIT2(0x1e69) EMIT2(0xa7a9) + return; + + case 't': case 0x163: case 0x165: case 0x167: case 0x1ab: + case 0x1ad: case 0x21b: case 0x288: case 0x1d75: case 0x1e6b: + case 0x1e6d: case 0x1e6f: case 0x1e71: case 0x1e97: case 0x2c66: + EMIT2('t') EMIT2(0x163) EMIT2(0x165) EMIT2(0x167) + EMIT2(0x1ab) EMIT2(0x1ad) EMIT2(0x21b) EMIT2(0x288) + EMIT2(0x1d75) EMIT2(0x1e6b) EMIT2(0x1e6d) EMIT2(0x1e6f) + EMIT2(0x1e71) EMIT2(0x1e97) EMIT2(0x2c66) return; case 'u': case u_grave: case u_acute: case u_circumflex: - case u_diaeresis: CASEMBC(0x169) CASEMBC(0x16b) - CASEMBC(0x16d) CASEMBC(0x16f) CASEMBC(0x171) - CASEMBC(0x173) CASEMBC(0x1b0) CASEMBC(0x1d4) - CASEMBC(0x1ee7) - EMIT2('u'); EMIT2(u_grave); EMIT2(u_acute); - EMIT2(u_circumflex); EMIT2(u_diaeresis); - EMITMBC(0x169) EMITMBC(0x16b) - EMITMBC(0x16d) EMITMBC(0x16f) EMITMBC(0x171) - EMITMBC(0x173) EMITMBC(0x1b0) EMITMBC(0x1d4) - EMITMBC(0x1ee7) + case u_diaeresis: case 0x169: case 0x16b: case 0x16d: + case 0x16f: case 0x171: case 0x173: case 0x1b0: case 0x1d4: + case 0x1d6: case 0x1d8: case 0x1da: case 0x1dc: case 0x215: + case 0x217: case 0x289: case 0x1d7e: case 0x1d99: case 0x1e73: + case 0x1e75: case 0x1e77: case 0x1e79: case 0x1e7b: + case 0x1ee5: case 0x1ee7: case 0x1ee9: case 0x1eeb: + case 0x1eed: case 0x1eef: case 0x1ef1: + EMIT2('u') EMIT2(u_grave) EMIT2(u_acute) + EMIT2(u_circumflex) EMIT2(u_diaeresis) + EMIT2(0x169) EMIT2(0x16b) + EMIT2(0x16d) EMIT2(0x16f) EMIT2(0x171) + EMIT2(0x173) EMIT2(0x1d6) EMIT2(0x1d8) + EMIT2(0x215) EMIT2(0x217) EMIT2(0x1b0) + EMIT2(0x1d4) EMIT2(0x1da) EMIT2(0x1dc) + EMIT2(0x289) EMIT2(0x1e73) EMIT2(0x1d7e) + EMIT2(0x1d99) EMIT2(0x1e75) EMIT2(0x1e77) + EMIT2(0x1e79) EMIT2(0x1e7b) EMIT2(0x1ee5) + EMIT2(0x1ee7) EMIT2(0x1ee9) EMIT2(0x1eeb) + EMIT2(0x1eed) EMIT2(0x1eef) EMIT2(0x1ef1) return; - case 'v': CASEMBC(0x1e7d) - EMIT2('v'); EMITMBC(0x1e7d) + case 'v': case 0x28b: case 0x1d8c: case 0x1e7d: case 0x1e7f: + EMIT2('v') EMIT2(0x28b) EMIT2(0x1d8c) EMIT2(0x1e7d) + EMIT2(0x1e7f) return; - case 'w': CASEMBC(0x175) CASEMBC(0x1e81) CASEMBC(0x1e83) - CASEMBC(0x1e85) CASEMBC(0x1e87) CASEMBC(0x1e98) - EMIT2('w'); EMITMBC(0x175) EMITMBC(0x1e81) EMITMBC(0x1e83) - EMITMBC(0x1e85) EMITMBC(0x1e87) EMITMBC(0x1e98) + case 'w': case 0x175: case 0x1e81: case 0x1e83: case 0x1e85: + case 0x1e87: case 0x1e89: case 0x1e98: + EMIT2('w') EMIT2(0x175) EMIT2(0x1e81) EMIT2(0x1e83) + EMIT2(0x1e85) EMIT2(0x1e87) EMIT2(0x1e89) EMIT2(0x1e98) return; - case 'x': CASEMBC(0x1e8b) CASEMBC(0x1e8d) - EMIT2('x'); EMITMBC(0x1e8b) EMITMBC(0x1e8d) + case 'x': case 0x1e8b: case 0x1e8d: + EMIT2('x') EMIT2(0x1e8b) EMIT2(0x1e8d) return; - case 'y': case y_acute: case y_diaeresis: CASEMBC(0x177) - CASEMBC(0x1e8f) CASEMBC(0x1e99) CASEMBC(0x1ef3) - CASEMBC(0x1ef7) CASEMBC(0x1ef9) - EMIT2('y'); EMIT2(y_acute); EMIT2(y_diaeresis); - EMITMBC(0x177) - EMITMBC(0x1e8f) EMITMBC(0x1e99) EMITMBC(0x1ef3) - EMITMBC(0x1ef7) EMITMBC(0x1ef9) + case 'y': case y_acute: case y_diaeresis: case 0x177: + case 0x1b4: case 0x233: case 0x24f: case 0x1e8f: + case 0x1e99: case 0x1ef3: case 0x1ef5: case 0x1ef7: + case 0x1ef9: + EMIT2('y') EMIT2(y_acute) EMIT2(y_diaeresis) + EMIT2(0x177) EMIT2(0x1b4) EMIT2(0x233) EMIT2(0x24f) + EMIT2(0x1e8f) EMIT2(0x1e99) EMIT2(0x1ef3) + EMIT2(0x1ef5) EMIT2(0x1ef7) EMIT2(0x1ef9) return; - case 'z': CASEMBC(0x17a) CASEMBC(0x17c) CASEMBC(0x17e) - CASEMBC(0x1b6) CASEMBC(0x1e91) CASEMBC(0x1e95) - EMIT2('z'); EMITMBC(0x17a) EMITMBC(0x17c) EMITMBC(0x17e) - EMITMBC(0x1b6) EMITMBC(0x1e91) EMITMBC(0x1e95) + case 'z': case 0x17a: case 0x17c: case 0x17e: case 0x1b6: + case 0x1d76: case 0x1d8e: case 0x1e91: case 0x1e93: + case 0x1e95: case 0x2c6c: + EMIT2('z') EMIT2(0x17a) EMIT2(0x17c) EMIT2(0x17e) + EMIT2(0x1b6) EMIT2(0x1d76) EMIT2(0x1d8e) EMIT2(0x1e91) + EMIT2(0x1e93) EMIT2(0x1e95) EMIT2(0x2c6c) return; - /* default: character itself */ + // default: character itself } } EMIT2(c); #undef EMIT2 -#undef EMITMBC +#undef EMIT2 } /* diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index eab47dbccc..191cd948ac 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -1,21 +1,21 @@ " Tests for regexp in utf8 encoding func s:equivalence_test() - let str = "AÀÃÂÃÄÅĀĂĄÇǞǠẢ BḂḆ CÇĆĈĊČ DÄŽÄḊḎḠEÈÉÊËĒĔĖĘĚẺẼ FḞ GĜĞĠĢǤǦǴḠ HĤĦḢḦḨ IÃŒÃÃŽÃĨĪĬĮİÇỈ JÄ´ KĶǨḰḴ LĹĻĽĿÅḺ MḾṀ NÑŃŅŇṄṈ OÃ’Ã“Ã”Ã•Ã–Ã˜ÅŒÅŽÅÆ Ç‘ǪǬỎ PṔṖ Q RŔŖŘṘṞ SŚŜŞŠṠ TŢŤŦṪṮ UÙÚÛÜŨŪŬŮŰŲƯǓỦ Vá¹¼ WŴẀẂẄẆ XẊẌ YÃŶŸẎỲỶỸ ZŹŻŽƵáºáº” aàáâãäåÄăąǎǟǡả bḃḇ cÃ§Ä‡Ä‰Ä‹Ä dÄđḋá¸á¸‘ eèéêëēĕėęěẻẽ fḟ gÄğġģǥǧǵḡ hĥħḣḧḩẖ iìíîïĩīĭįÇỉ jĵǰ kķǩḱḵ lĺļľŀłḻ mḿṠnñńņňʼnṅṉ oòóôõöøÅÅőơǒǫǭỠpṕṗ q rŕŗřṙṟ sÅ›Åşšṡ tţťŧṫṯẗ uùúûüũūŭůűųưǔủ vá¹½ wŵáºáºƒáº…ẇẘ xẋẠyýÿŷáºáº™á»³á»·á»¹ zźżžƶẑẕ" + let str = "AÀÃÂÃÄÅĀĂĄÇǞǠǺȂȦȺḀẠẢẤẦẨẪẬẮẰẲẴẶ BÆÉƒá¸‚ḄḆ CÇĆĈĊČƇȻḈꞒ DÄŽÄÆŠá¸Šá¸Œá¸Žá¸á¸’ EÈÉÊËĒĔĖĘĚȄȆȨɆḔḖḘḚḜẸẺẼẾỀỂỄỆ FƑḞꞘ GĜĞĠĢƓǤǦǴḠꞠ HĤĦȞḢḤḦḨḪⱧ IÃŒÃÃŽÃĨĪĬĮİƗÇȈȊḬḮỈỊ JĴɈ KÄ¶Æ˜Ç¨á¸°á¸²á¸´â±©ê€ LĹĻĽĿÅȽḶḸḺḼⱠ MḾṀṂ NÑŃŅŇǸṄṆṈṊꞤ OÃ’Ã“Ã”Ã•Ã–Ã˜ÅŒÅŽÅÆŸÆ Ç‘ǪǬǾȌȎȪȬȮȰṌṎá¹á¹’ỌỎá»á»’ỔỖỘỚỜỞỠỢ PƤṔṖⱣ QÉŠ RŔŖŘÈȒɌṘṚṜṞⱤꞦ SŚŜŞŠȘṠṢṤṦṨⱾꞨ TŢŤŦƬƮȚȾṪṬṮṰ UÙÚÛÜŨŪŬŮŰƯǕǙǛǓǗȔȖɄṲṴṶṸṺỤỦỨỪỬỮỰ VƲṼṾ WŴẀẂẄẆẈ XẊẌ YÃŶŸƳȲɎẎỲỴỶỸ ZŹŻŽƵáºáº’ẔⱫ aàáâãäåÄăąǎǟǡǻȃȧá¶á¸áºšáº¡áº£áº¥áº§áº©áº«áº­áº¯áº±áº³áºµáº·â±¥ bƀɓᵬᶀḃḅḇ cÃ§Ä‡Ä‰Ä‹ÄÆˆÈ¼á¸‰êž“êž” dÄđɗᵭá¶á¶‘ḋá¸á¸á¸‘ḓ eèéêëēĕėęěȅȇȩɇᶒḕḗḙḛá¸áº¹áº»áº½áº¿á»á»ƒá»…ệ fƒᵮᶂḟꞙ gÄğġģǥǧǵɠᶃḡꞡ hĥħȟḣḥḧḩḫẖⱨꞕ iìíîïĩīĭįÇȉȋɨᶖḭḯỉị jĵǰɉ kķƙǩᶄḱḳḵⱪê lĺļľŀłƚḷḹḻḽⱡ mᵯḿá¹á¹ƒ nñńņňʼnǹᵰᶇṅṇṉṋꞥ oòóôõöøÅÅőơǒǫǭǿÈÈȫȭȯȱɵá¹á¹á¹‘ṓá»á»á»‘ồổỗộớá»á»Ÿá»¡á»£ pƥᵱᵽᶈṕṗ qɋʠ rŕŗřȑȓÉɽᵲᵳᶉṛá¹á¹Ÿêž§ sÅ›Åşšșȿᵴᶊṡṣṥṧṩꞩ tţťŧƫƭțʈᵵṫṭṯṱẗⱦ uùúûüũūŭůűųǚǖưǔǘǜȕȗʉᵾᶙṳṵṷṹṻụủứừửữự vʋᶌṽṿ wŵáºáºƒáº…ẇẉẘ xẋẠyýÿŷƴȳÉáºáº™á»³á»µá»·á»¹ zźżžƶᵶᶎẑẓẕⱬ" let groups = split(str) for group1 in groups - for c in split(group1, '\zs') - " next statement confirms that equivalence class matches every - " character in group - call assert_match('^[[=' . c . '=]]*$', group1) - for group2 in groups - if group2 != group1 - " next statement converts that equivalence class doesn't match - " character in any other group - call assert_equal(-1, match(group2, '[[=' . c . '=]]')) - endif + for c in split(group1, '\zs') + " next statement confirms that equivalence class matches every + " character in group + call assert_match('^[[=' .. c .. '=]]*$', group1) + for group2 in groups + if group2 != group1 + " next statement converts that equivalence class doesn't match + " character in any other group + call assert_equal(-1, match(group2, '[[=' .. c .. '=]]'), c) + endif + endfor endfor - endfor endfor endfunc @@ -223,7 +223,7 @@ endfunc func Test_reversed_range() for re in range(0, 2) exe 'set re=' . re - call assert_fails('call match("abc def", "[c-a]")', 'E944:') + call assert_fails('call match("abc def", "[c-a]")', 'E944:', re) endfor set re=0 endfunc -- cgit From c4624b9543bb7a12572312f605b1e96d07365338 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 17:03:04 +0800 Subject: vim-patch:8.2.4546: duplicate #undef Problem: Duplicate #undef. Solution: Remove one #undef. (closes vim/vim#9932) https://github.com/vim/vim/commit/0a4e098f32f3c83273ff63c02c8d0d5cdd7c897c --- src/nvim/regexp_nfa.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index ecc8a4703c..7e316624f8 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1282,7 +1282,6 @@ static void nfa_emit_equi_class(int c) EMIT2(c); #undef EMIT2 -#undef EMIT2 } /* -- cgit From 536dc391f64685afd452df0e4362396a7039e655 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 20:18:45 +0800 Subject: vim-patch:8.2.4593: unnecessary call to redraw_later() (#17775) Problem: Unnecessary call to redraw_later(). Solution: Remove the call to redraw_later() in op_yank(). (closes vim/vim#9971) https://github.com/vim/vim/commit/95d2e7634ccd8e0da78086002509a856999e180c --- src/nvim/ops.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 244a1881aa..fc66fb5f06 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2835,9 +2835,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) curr->y_size = j; xfree(reg->y_array); } - if (curwin->w_p_rnu) { - redraw_later(curwin, SOME_VALID); // cursor moved to start - } + if (message) { // Display message about yank? if (yank_type == kMTCharWise && yanklines == 1) { yanklines = 0; -- cgit From 2ab52bd9889790dc7e47a09e801751aada418727 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 21:56:47 +0800 Subject: refactor(tinput_wait_enqueue): use rbuffer_read() when pasting (#17754) When pasting, all of key buffer can be consumed, and in case of phase 3 the paste event must be put exactly once, so using rbuffer_read() should be better here. --- src/nvim/tui/input.c | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 917847608a..24958ce041 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -115,34 +115,28 @@ static void tinput_done_event(void **argv) static void tinput_wait_enqueue(void **argv) { TermInput *input = argv[0]; - if (rbuffer_size(input->key_buffer) == 0 && input->paste == 3) { - // End streamed paste with an empty string. - const String keys = { .data = "", .size = 0 }; - String copy = copy_string(keys); - multiqueue_put(main_loop.events, tinput_paste_event, 3, - copy.data, copy.size, (intptr_t)input->paste); - } - RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { - const String keys = { .data = buf, .size = len }; - if (input->paste) { - String copy = copy_string(keys); - if (ui_client_channel_id) { - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(copy_string(keys))); // 'data' - ADD(args, BOOLEAN_OBJ(true)); // 'crlf' - ADD(args, INTEGER_OBJ(input->paste)); // 'phase' - rpc_send_event(ui_client_channel_id, "nvim_paste", args); - } else { - multiqueue_put(main_loop.events, tinput_paste_event, 3, - copy.data, copy.size, (intptr_t)input->paste); - } - if (input->paste == 1) { - // Paste phase: "continue" - input->paste = 2; - } - rbuffer_consumed(input->key_buffer, len); - rbuffer_reset(input->key_buffer); + if (input->paste) { // produce exactly one paste event + const size_t len = rbuffer_size(input->key_buffer); + String keys = { .data = xmallocz(len), .size = len }; + rbuffer_read(input->key_buffer, keys.data, len); + if (ui_client_channel_id) { + Array args = ARRAY_DICT_INIT; + ADD(args, STRING_OBJ(keys)); // 'data' + ADD(args, BOOLEAN_OBJ(true)); // 'crlf' + ADD(args, INTEGER_OBJ(input->paste)); // 'phase' + rpc_send_event(ui_client_channel_id, "nvim_paste", args); } else { + multiqueue_put(main_loop.events, tinput_paste_event, 3, + keys.data, keys.size, (intptr_t)input->paste); + } + if (input->paste == 1) { + // Paste phase: "continue" + input->paste = 2; + } + rbuffer_reset(input->key_buffer); + } else { // enqueue input for the main thread or Nvim server + RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) { + const String keys = { .data = buf, .size = len }; size_t consumed; if (ui_client_channel_id) { Array args = ARRAY_DICT_INIT; -- cgit From b1207e5080087fa0ad05d13787a02ebee3f20ac0 Mon Sep 17 00:00:00 2001 From: Javier López Date: Sat, 19 Mar 2022 15:32:18 -0500 Subject: docs: properly escape to avoid doxygen weirdness If this is not properly escaped doxygen 1.9.3 will not work correctly, and the documentation generated in local machines will differ with what is generated in CI. --- src/nvim/api/win_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index b0267f5ed0..42e880dc19 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -126,7 +126,7 @@ /// [ "â•”", "â•" ,"â•—", "â•‘", "â•", "â•", "╚", "â•‘" ]. /// If the number of chars are less than eight, they will be repeated. Thus /// an ASCII border could be specified as -/// [ "/", "-", "\\", "|" ], +/// [ "/", "-", \"\\\\\", "|" ], /// or all chars the same as /// [ "x" ]. /// An empty string can be used to turn off a specific border, for instance, -- cgit From 77eb6f9dc75ebe00aa835441ad623ba46d7108bb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 20 Mar 2022 08:08:50 +0800 Subject: fix(api, lua): return NIL on failure to find converted function (#17779) --- src/nvim/api/private/converter.c | 3 +-- src/nvim/lua/converter.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index 82ec1ad0d8..a26383ec7d 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -65,8 +65,7 @@ typedef struct { #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ ufunc_T *fp = find_func(fun); \ - assert(fp != NULL); \ - if (fp->uf_cb == nlua_CFunction_func_call) { \ + if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \ LuaRef ref = api_new_luaref(((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ kvi_push(edata->stack, LUAREF_OBJ(ref)); \ } else { \ diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 0bb224c729..ef49a03660 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -477,8 +477,7 @@ static bool typval_conv_special = false; #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ ufunc_T *fp = find_func(fun); \ - assert(fp != NULL); \ - if (fp->uf_cb == nlua_CFunction_func_call) { \ + if (fp != NULL && fp->uf_cb == nlua_CFunction_func_call) { \ nlua_pushref(lstate, ((LuaCFunctionState *)fp->uf_cb_state)->lua_callable.func_ref); \ } else { \ TYPVAL_ENCODE_CONV_NIL(tv); \ -- cgit From be35d3c5ad501abb029279f8e1812d0e4525284f Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Sat, 19 Mar 2022 18:57:58 -0600 Subject: feat(api): remove Lua autocommand callbacks when they return true (#17784) This copies the semantics of nvim_buf_attach callbacks, and is a convenient way to create oneshot autocommands gated by some condition. --- src/nvim/autocmd.c | 11 +++++++++-- src/nvim/eval.c | 12 ++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index df336d8703..a36f2c97b5 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2013,6 +2013,7 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) } AutoCmd *ac = acp->nextcmd; + bool oneshot = ac->once; if (p_verbose >= 9) { verbose_enter_scroll(); @@ -2024,7 +2025,13 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) if (ac->exec.type == CALLABLE_CB) { typval_T argsin = TV_INITIAL_VALUE; typval_T rettv = TV_INITIAL_VALUE; - callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv); + if (callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv)) { + if (ac->exec.callable.cb.type == kCallbackLua) { + // If a Lua callback returns 'true' then the autocommand is removed + oneshot = true; + } + } + // TODO(tjdevries): // @@ -2042,7 +2049,7 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) } // Remove one-shot ("once") autocmd in anticipation of its execution. - if (ac->once) { + if (oneshot) { aucmd_del(ac); } autocmd_nested = ac->nested; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index af7c3d4985..33e8469768 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7730,6 +7730,7 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co partial_T *partial; char_u *name; Array args = ARRAY_DICT_INIT; + Object rv; switch (callback->type) { case kCallbackFuncref: name = callback->data.funcref; @@ -7742,10 +7743,13 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co break; case kCallbackLua: - nlua_call_ref(callback->data.luaref, NULL, args, false, NULL); - - return false; - break; + rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL); + switch (rv.type) { + case kObjectTypeBoolean: + return rv.data.boolean; + default: + return false; + } case kCallbackNone: return false; -- cgit From 6eca9b69c4a1f40f27a6b41961af787327259de8 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sat, 19 Mar 2022 13:48:03 +0100 Subject: feat(ui): allow conceal to be defined in decorations Unlike syntax conceal, change highlight of concealed char Can be used in tree-sitter using "conceal" metadata. --- src/nvim/api/extmark.c | 16 ++++++++++++++++ src/nvim/api/keysets.lua | 1 + src/nvim/decoration.c | 15 +++++++++++++++ src/nvim/decoration.h | 13 +++++++++++-- src/nvim/screen.c | 17 +++++++++++++++-- 5 files changed, 58 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index c02688a815..797b64e2af 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -467,6 +467,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// as the mark and 'cursorline' is enabled. /// Note: ranges are unsupported and decorations are only /// applied to start_row +/// - conceal: string which should be either empty or a single +/// character. Enable concealing similar to |:syn-conceal|. +/// When a character is supplied it is used as |:syn-cchar|. +/// "hl_group" is used as highlight for the cchar if provided, +/// otherwise it defaults to |hl-Conceal|. /// /// @param[out] err Error details, if any /// @return Id of the created/updated extmark @@ -563,6 +568,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } } + if (opts->conceal.type == kObjectTypeString) { + String c = opts->conceal.data.string; + decor.conceal = true; + if (c.size) { + decor.conceal_char = utf_ptr2char((const char_u *)c.data); + } + } else if (HAS_KEY(opts->conceal)) { + api_set_error(err, kErrorTypeValidation, "conceal is not a String"); + goto error; + } + if (opts->virt_text.type == kObjectTypeArray) { decor.virt_text = parse_virt_text(opts->virt_text.data.array, err, &decor.virt_text_width); diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 435e8195dd..b6264cdfab 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -27,6 +27,7 @@ return { "number_hl_group"; "line_hl_group"; "cursorline_hl_group"; + "conceal"; }; keymap = { "noremap"; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index fb709d12ff..a0f189ca38 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -312,6 +312,10 @@ next_mark: int attr = 0; size_t j = 0; + bool conceal = 0; + int conceal_char = 0; + int conceal_attr = 0; + for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange item = kv_A(state->active, i); bool active = false, keep = true; @@ -336,6 +340,14 @@ next_mark: if (active && item.attr_id > 0) { attr = hl_combine_attr(attr, item.attr_id); } + if (active && item.decor.conceal) { + conceal = true; + if (item.start_row == state->row && item.start_col == col && item.decor.conceal_char) { + conceal_char = item.decor.conceal_char; + state->col_until = MIN(state->col_until, item.start_col); + conceal_attr = item.attr_id; + } + } if ((item.start_row == state->row && item.start_col <= col) && kv_size(item.decor.virt_text) && item.decor.virt_text_pos == kVTOverlay && item.win_col == -1) { @@ -349,6 +361,9 @@ next_mark: } kv_size(state->active) = j; state->current = attr; + state->conceal = conceal; + state->conceal_char = conceal_char; + state->conceal_attr = conceal_attr; return attr; } diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 8df53caa0a..97ab218f86 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -47,6 +47,7 @@ struct Decoration { bool virt_text_hide; bool hl_eol; bool virt_lines_above; + bool conceal; // TODO(bfredl): style, etc DecorPriority priority; int col; // fixed col value, like win_col @@ -56,9 +57,13 @@ struct Decoration { int number_hl_id; int line_hl_id; int cursorline_hl_id; + // TODO(bfredl): in principle this should be a schar_T, but we + // probably want some kind of glyph cache for that.. + int conceal_char; }; -#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \ - false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 } +#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \ + kHlModeUnknown, false, false, false, false, DECOR_PRIORITY_BASE, \ + 0, 0, NULL, 0, 0, 0, 0, 0 } typedef struct { int start_row; @@ -80,6 +85,10 @@ typedef struct { int col_until; int current; int eol_col; + + bool conceal; + int conceal_char; + int conceal_attr; } DecorState; EXTERN DecorState decor_state INIT(= { 0 }); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 60de4e479e..0e1f469a4b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2682,6 +2682,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // Repeat for the whole displayed line. for (;;) { int has_match_conc = 0; ///< match wants to conceal + int decor_conceal = 0; + bool did_decrement_ptr = false; // Skip this quickly when working on the text. @@ -3506,6 +3508,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc char_attr = hl_combine_attr(extmark_attr, char_attr); } } + + decor_conceal = decor_state.conceal; + if (decor_conceal && decor_state.conceal_char) { + decor_conceal = 2; // really?? + } } // Found last space before word: check for line break. @@ -3809,19 +3816,25 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (wp->w_p_cole > 0 && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp)) - && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0) + && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0) && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; - if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) + if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1 || decor_conceal > 1) && (syn_get_sub_char() != NUL || (has_match_conc && match_conc) + || (decor_conceal && decor_state.conceal_char) || wp->w_p_cole == 1) && wp->w_p_cole != 3) { // First time at this concealed item: display one // character. if (has_match_conc && match_conc) { c = match_conc; + } else if (decor_conceal && decor_state.conceal_char) { + c = decor_state.conceal_char; + if (decor_state.conceal_attr) { + char_attr = decor_state.conceal_attr; + } } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); } else if (wp->w_p_lcs_chars.conceal != NUL) { -- cgit From 6566a4bdbd800ff1850a001a5c40f65b3dc13e46 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 10:51:38 +0000 Subject: vim-patch:8.1.1734: the evalfunc.c file is too big Problem: The evalfunc.c file is too big. Solution: Move some functions to other files. https://github.com/vim/vim/commit/29b7d7a9aac591f920edb89241c8cde27378e50b --- src/nvim/eval.c | 25 --- src/nvim/eval/funcs.c | 184 +---------------- src/nvim/highlight_group.c | 487 +++++++++++++++++++++++++++++++++++++++++++++ src/nvim/window.c | 280 -------------------------- 4 files changed, 488 insertions(+), 488 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 33e8469768..7f937a3137 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -68,7 +68,6 @@ static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_write2 = N_("E80: Error while writing: %s"); static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); @@ -7323,30 +7322,6 @@ void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buf tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } -int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) -{ - dictitem_T *di; - - if (tv->v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return FAIL; - } - - if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) { - *conceal_char = tv_get_string(&di->di_tv); - } - - if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) { - *win = find_win_by_nr_or_id(&di->di_tv); - if (*win == NULL) { - emsg(_(e_invalwindow)); - return FAIL; - } - } - - return OK; -} - void return_register(int regname, typval_T *rettv) { char_u buf[2] = { regname, 0 }; diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 6e98f229b2..7a3b9e47f9 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1124,7 +1124,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -static win_T *get_optional_window(typval_T *argvars, int idx) +win_T *get_optional_window(typval_T *argvars, int idx) { win_T *win = curwin; @@ -3669,61 +3669,6 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_buf_local_marks(buf, rettv->vval.v_list); } -/// "getmatches()" function -static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - matchitem_T *cur; - int i; - win_T *win = get_optional_window(argvars, 0); - - if (win == NULL) { - return; - } - - tv_list_alloc_ret(rettv, kListLenMayKnow); - cur = win->w_match_head; - while (cur != NULL) { - dict_T *dict = tv_dict_alloc(); - if (cur->match.regprog == NULL) { - // match added with matchaddpos() - for (i = 0; i < MAXPOSMATCH; i++) { - llpos_T *llpos; - char buf[30]; // use 30 to avoid compiler warning - - llpos = &cur->pos.pos[i]; - if (llpos->lnum == 0) { - break; - } - list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); - tv_list_append_number(l, (varnumber_T)llpos->lnum); - if (llpos->col > 0) { - tv_list_append_number(l, (varnumber_T)llpos->col); - tv_list_append_number(l, (varnumber_T)llpos->len); - } - int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); - assert((size_t)len < sizeof(buf)); - tv_dict_add_list(dict, buf, (size_t)len, l); - } - } else { - tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); - } - tv_dict_add_str(dict, S_LEN("group"), - (const char *)syn_id2name(cur->hlg_id)); - tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); - tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); - - if (cur->conceal_char) { - char buf[MB_MAXBYTES + 1]; - - buf[utf_char2bytes(cur->conceal_char, (char_u *)buf)] = NUL; - tv_dict_add_str(dict, S_LEN("conceal"), buf); - } - - tv_list_append_dict(rettv->vval.v_list, dict); - cur = cur->next; - } -} - /// "getmousepos()" function static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -6108,133 +6053,6 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, kSomeMatch); } -/// "matchadd()" function -static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - char grpbuf[NUMBUFLEN]; - char patbuf[NUMBUFLEN]; - // group - const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); - // pattern - const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); - // default priority - int prio = 10; - int id = -1; - bool error = false; - const char *conceal_char = NULL; - win_T *win = curwin; - - rettv->vval.v_number = -1; - - if (grp == NULL || pat == NULL) { - return; - } - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { - return; - } - } - } - if (error) { - return; - } - if (id >= 1 && id <= 3) { - semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id); - return; - } - - rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char); -} - -static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = -1; - - char buf[NUMBUFLEN]; - const char *const group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) { - return; - } - - if (argvars[1].v_type != VAR_LIST) { - semsg(_(e_listarg), "matchaddpos()"); - return; - } - - list_T *l; - l = argvars[1].vval.v_list; - if (l == NULL) { - return; - } - - bool error = false; - int prio = 10; - int id = -1; - const char *conceal_char = NULL; - win_T *win = curwin; - - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { - return; - } - } - } - if (error == true) { - return; - } - - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) { - semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id); - return; - } - - rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); -} - -/// "matcharg()" function -static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const int id = tv_get_number(&argvars[0]); - - tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 - ? 2 - : 0)); - - if (id >= 1 && id <= 3) { - matchitem_T *const m = get_match(curwin, id); - - if (m != NULL) { - tv_list_append_string(rettv->vval.v_list, - (const char *)syn_id2name(m->hlg_id), -1); - tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); - } else { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } - } -} - -/// "matchdelete()" function -static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *win = get_optional_window(argvars, 1); - if (win == NULL) { - rettv->vval.v_number = -1; - } else { - rettv->vval.v_number = match_delete(win, - (int)tv_get_number(&argvars[0]), true); - } -} - /// "matchend()" function static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index f342ada3db..786b4631f5 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -2,11 +2,14 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com // highlight_group.c: code for managing highlight groups +// Includes highlighting matches #include "nvim/autocmd.h" #include "nvim/api/private/helpers.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" +#include "nvim/eval/funcs.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/garray.h" #include "nvim/highlight.h" @@ -14,6 +17,7 @@ #include "nvim/lua/executor.h" #include "nvim/map.h" #include "nvim/option.h" +#include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/screen.h" @@ -26,6 +30,8 @@ #define MAX_SYN_NAME 200 +static char *e_invalwindow = N_("E957: Invalid window number"); + // builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -2797,3 +2803,484 @@ int name_to_ctermcolor(const char *name) TriState bold = kNone; return lookup_color(i, false, &bold); } + +/// Add match to the match list of window 'wp'. The pattern 'pat' will be +/// highlighted with the group 'grp' with priority 'prio'. +/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). +/// +/// @param[in] id a desired ID 'id' can be specified +/// (greater than or equal to 1). -1 must be specified if no +/// particular ID is desired +/// @param[in] conceal_char pointer to conceal replacement char +/// @return ID of added match, -1 on failure. +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, + list_T *pos_list, const char *const conceal_char) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + matchitem_T *cur; + matchitem_T *prev; + matchitem_T *m; + int hlg_id; + regprog_T *regprog = NULL; + int rtype = SOME_VALID; + + if (*grp == NUL || (pat != NULL && *pat == NUL)) { + return -1; + } + if (id < -1 || id == 0) { + semsg(_("E799: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), + (int64_t)id); + return -1; + } + if (id != -1) { + cur = wp->w_match_head; + while (cur != NULL) { + if (cur->id == id) { + semsg(_("E801: ID already taken: %" PRId64), (int64_t)id); + return -1; + } + cur = cur->next; + } + } + if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) { + return -1; + } + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { + semsg(_(e_invarg2), pat); + return -1; + } + + // Find available match ID. + while (id == -1) { + cur = wp->w_match_head; + while (cur != NULL && cur->id != wp->w_next_match_id) { + cur = cur->next; + } + if (cur == NULL) { + id = wp->w_next_match_id; + } + wp->w_next_match_id++; + } + + // Build new match. + m = xcalloc(1, sizeof(matchitem_T)); + m->id = id; + m->priority = prio; + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); + m->hlg_id = hlg_id; + m->match.regprog = regprog; + m->match.rmm_ic = false; + m->match.rmm_maxcol = 0; + m->conceal_char = 0; + if (conceal_char != NULL) { + m->conceal_char = utf_ptr2char((const char_u *)conceal_char); + } + + // Set up position matches + if (pos_list != NULL) { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + + int i = 0; + TV_LIST_ITER(pos_list, li, { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; + bool error = false; + + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { + const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; + const listitem_T *subli = tv_list_first(subl); + if (subli == NULL) { + semsg(_("E5030: Empty list at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); + goto fail; + } + lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { + goto fail; + } + if (lnum <= 0) { + continue; + } + m->pos.pos[i].lnum = lnum; + subli = TV_LIST_ITEM_NEXT(subl, subli); + if (subli != NULL) { + col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { + goto fail; + } + if (col < 0) { + continue; + } + subli = TV_LIST_ITEM_NEXT(subl, subli); + if (subli != NULL) { + len = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (len < 0) { + continue; + } + if (error) { + goto fail; + } + } + } + m->pos.pos[i].col = col; + m->pos.pos[i].len = len; + } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { + if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { + continue; + } + m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; + m->pos.pos[i].col = 0; + m->pos.pos[i].len = 0; + } else { + semsg(_("E5031: List or number required at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); + goto fail; + } + if (toplnum == 0 || lnum < toplnum) { + toplnum = lnum; + } + if (botlnum == 0 || lnum >= botlnum) { + botlnum = lnum + 1; + } + i++; + if (i >= MAXPOSMATCH) { + break; + } + }); + + // Calculate top and bottom lines for redrawing area + if (toplnum != 0) { + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > toplnum) { + wp->w_buffer->b_mod_top = toplnum; + } + if (wp->w_buffer->b_mod_bot < botlnum) { + wp->w_buffer->b_mod_bot = botlnum; + } + } else { + wp->w_buffer->b_mod_set = true; + wp->w_buffer->b_mod_top = toplnum; + wp->w_buffer->b_mod_bot = botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + m->pos.toplnum = toplnum; + m->pos.botlnum = botlnum; + rtype = VALID; + } + } + + // Insert new match. The match list is in ascending order with regard to + // the match priorities. + cur = wp->w_match_head; + prev = cur; + while (cur != NULL && prio >= cur->priority) { + prev = cur; + cur = cur->next; + } + if (cur == prev) { + wp->w_match_head = m; + } else { + prev->next = m; + } + m->next = cur; + + redraw_later(wp, rtype); + return id; + +fail: + xfree(m); + return -1; +} + +/// Delete match with ID 'id' in the match list of window 'wp'. +/// +/// @param perr print error messages if true. +int match_delete(win_T *wp, int id, bool perr) +{ + matchitem_T *cur = wp->w_match_head; + matchitem_T *prev = cur; + int rtype = SOME_VALID; + + if (id < 1) { + if (perr) { + semsg(_("E802: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), + (int64_t)id); + } + return -1; + } + while (cur != NULL && cur->id != id) { + prev = cur; + cur = cur->next; + } + if (cur == NULL) { + if (perr) { + semsg(_("E803: ID not found: %" PRId64), (int64_t)id); + } + return -1; + } + if (cur == prev) { + wp->w_match_head = cur->next; + } else { + prev->next = cur->next; + } + vim_regfree(cur->match.regprog); + xfree(cur->pattern); + if (cur->pos.toplnum != 0) { + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { + wp->w_buffer->b_mod_top = cur->pos.toplnum; + } + if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + } + } else { + wp->w_buffer->b_mod_set = true; + wp->w_buffer->b_mod_top = cur->pos.toplnum; + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + rtype = VALID; + } + xfree(cur); + redraw_later(wp, rtype); + return 0; +} + +/// Delete all matches in the match list of window 'wp'. +void clear_matches(win_T *wp) +{ + matchitem_T *m; + + while (wp->w_match_head != NULL) { + m = wp->w_match_head->next; + vim_regfree(wp->w_match_head->match.regprog); + xfree(wp->w_match_head->pattern); + xfree(wp->w_match_head); + wp->w_match_head = m; + } + redraw_later(wp, SOME_VALID); +} + +/// Get match from ID 'id' in window 'wp'. +/// Return NULL if match not found. +matchitem_T *get_match(win_T *wp, int id) +{ + matchitem_T *cur = wp->w_match_head; + + while (cur != NULL && cur->id != id) { + cur = cur->next; + } + return cur; +} + +int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) +{ + dictitem_T *di; + + if (tv->v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return FAIL; + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) { + *conceal_char = tv_get_string(&di->di_tv); + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) { + *win = find_win_by_nr_or_id(&di->di_tv); + if (*win == NULL) { + emsg(_(e_invalwindow)); + return FAIL; + } + } + + return OK; +} + +/// "getmatches()" function +void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + matchitem_T *cur; + int i; + win_T *win = get_optional_window(argvars, 0); + + if (win == NULL) { + return; + } + + tv_list_alloc_ret(rettv, kListLenMayKnow); + cur = win->w_match_head; + while (cur != NULL) { + dict_T *dict = tv_dict_alloc(); + if (cur->match.regprog == NULL) { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; i++) { + llpos_T *llpos; + char buf[30]; // use 30 to avoid compiler warning + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) { + break; + } + list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); + tv_list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) { + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); + } + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); + } + } else { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); + } + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); + + if (cur->conceal_char) { + char buf[MB_MAXBYTES + 1]; + + buf[utf_char2bytes(cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); + } + + tv_list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } +} + +/// "matchadd()" function +void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + // group + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + // pattern + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + // default priority + int prio = 10; + int id = -1; + bool error = false; + const char *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; + } + } + } + if (error) { + return; + } + if (id >= 1 && id <= 3) { + semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id); + return; + } + + rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char); +} + +/// "matchaddpo()" function +void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = -1; + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + semsg(_(e_listarg), "matchaddpos()"); + return; + } + + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } + + bool error = false; + int prio = 10; + int id = -1; + const char *conceal_char = NULL; + win_T *win = curwin; + + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; + } + } + } + if (error == true) { + return; + } + + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id); + return; + } + + rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); +} + +/// "matcharg()" function +void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const int id = (int)tv_get_number(&argvars[0]); + + tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 + ? 2 + : 0)); + + if (id >= 1 && id <= 3) { + matchitem_T *const m = get_match(curwin, id); + + if (m != NULL) { + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); + } else { + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + } + } +} + +/// "matchdelete()" function +void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *win = get_optional_window(argvars, 1); + if (win == NULL) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = match_delete(win, + (int)tv_get_number(&argvars[0]), true); + } +} + diff --git a/src/nvim/window.c b/src/nvim/window.c index 8b9f1e024d..1f9cfbf4fd 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6953,286 +6953,6 @@ void restore_buffer(bufref_T *save_curbuf) } } - -/// Add match to the match list of window 'wp'. The pattern 'pat' will be -/// highlighted with the group 'grp' with priority 'prio'. -/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). -/// -/// @param[in] id a desired ID 'id' can be specified -/// (greater than or equal to 1). -1 must be specified if no -/// particular ID is desired -/// @param[in] conceal_char pointer to conceal replacement char -/// @return ID of added match, -1 on failure. -int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, - list_T *pos_list, const char *const conceal_char) - FUNC_ATTR_NONNULL_ARG(1, 2) -{ - matchitem_T *cur; - matchitem_T *prev; - matchitem_T *m; - int hlg_id; - regprog_T *regprog = NULL; - int rtype = SOME_VALID; - - if (*grp == NUL || (pat != NULL && *pat == NUL)) { - return -1; - } - if (id < -1 || id == 0) { - semsg(_("E799: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - return -1; - } - if (id != -1) { - cur = wp->w_match_head; - while (cur != NULL) { - if (cur->id == id) { - semsg(_("E801: ID already taken: %" PRId64), (int64_t)id); - return -1; - } - cur = cur->next; - } - } - if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) { - return -1; - } - if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { - semsg(_(e_invarg2), pat); - return -1; - } - - // Find available match ID. - while (id == -1) { - cur = wp->w_match_head; - while (cur != NULL && cur->id != wp->w_next_match_id) { - cur = cur->next; - } - if (cur == NULL) { - id = wp->w_next_match_id; - } - wp->w_next_match_id++; - } - - // Build new match. - m = xcalloc(1, sizeof(matchitem_T)); - m->id = id; - m->priority = prio; - m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); - m->hlg_id = hlg_id; - m->match.regprog = regprog; - m->match.rmm_ic = FALSE; - m->match.rmm_maxcol = 0; - m->conceal_char = 0; - if (conceal_char != NULL) { - m->conceal_char = utf_ptr2char((const char_u *)conceal_char); - } - - // Set up position matches - if (pos_list != NULL) { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - - int i = 0; - TV_LIST_ITER(pos_list, li, { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - bool error = false; - - if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { - const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; - const listitem_T *subli = tv_list_first(subl); - if (subli == NULL) { - semsg(_("E5030: Empty list at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (lnum <= 0) { - continue; - } - m->pos.pos[i].lnum = lnum; - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (col < 0) { - continue; - } - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (len < 0) { - continue; - } - if (error) { - goto fail; - } - } - } - m->pos.pos[i].col = col; - m->pos.pos[i].len = len; - } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { - if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { - continue; - } - m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; - m->pos.pos[i].col = 0; - m->pos.pos[i].len = 0; - } else { - semsg(_("E5031: List or number required at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - if (toplnum == 0 || lnum < toplnum) { - toplnum = lnum; - } - if (botlnum == 0 || lnum >= botlnum) { - botlnum = lnum + 1; - } - i++; - if (i >= MAXPOSMATCH) { - break; - } - }); - - // Calculate top and bottom lines for redrawing area - if (toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > toplnum) { - wp->w_buffer->b_mod_top = toplnum; - } - if (wp->w_buffer->b_mod_bot < botlnum) { - wp->w_buffer->b_mod_bot = botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = toplnum; - wp->w_buffer->b_mod_bot = botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - m->pos.toplnum = toplnum; - m->pos.botlnum = botlnum; - rtype = VALID; - } - } - - // Insert new match. The match list is in ascending order with regard to - // the match priorities. - cur = wp->w_match_head; - prev = cur; - while (cur != NULL && prio >= cur->priority) { - prev = cur; - cur = cur->next; - } - if (cur == prev) { - wp->w_match_head = m; - } else { - prev->next = m; - } - m->next = cur; - - redraw_later(wp, rtype); - return id; - -fail: - xfree(m); - return -1; -} - - -/// Delete match with ID 'id' in the match list of window 'wp'. -/// -/// @param perr print error messages if true. -int match_delete(win_T *wp, int id, bool perr) -{ - matchitem_T *cur = wp->w_match_head; - matchitem_T *prev = cur; - int rtype = SOME_VALID; - - if (id < 1) { - if (perr) { - semsg(_("E802: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - } - return -1; - } - while (cur != NULL && cur->id != id) { - prev = cur; - cur = cur->next; - } - if (cur == NULL) { - if (perr) { - semsg(_("E803: ID not found: %" PRId64), (int64_t)id); - } - return -1; - } - if (cur == prev) { - wp->w_match_head = cur->next; - } else { - prev->next = cur->next; - } - vim_regfree(cur->match.regprog); - xfree(cur->pattern); - if (cur->pos.toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { - wp->w_buffer->b_mod_top = cur->pos.toplnum; - } - if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = cur->pos.toplnum; - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - rtype = VALID; - } - xfree(cur); - redraw_later(wp, rtype); - return 0; -} - -/* - * Delete all matches in the match list of window 'wp'. - */ -void clear_matches(win_T *wp) -{ - matchitem_T *m; - - while (wp->w_match_head != NULL) { - m = wp->w_match_head->next; - vim_regfree(wp->w_match_head->match.regprog); - xfree(wp->w_match_head->pattern); - xfree(wp->w_match_head); - wp->w_match_head = m; - } - redraw_later(wp, SOME_VALID); -} - -/* - * Get match from ID 'id' in window 'wp'. - * Return NULL if match not found. - */ -matchitem_T *get_match(win_T *wp, int id) -{ - matchitem_T *cur = wp->w_match_head; - - while (cur != NULL && cur->id != id) { - cur = cur->next; - } - return cur; -} - - /// Check that "topfrp" and its children are at the right height. /// /// @param topfrp top frame pointer -- cgit From 3c62a3f9ddc6a2cc805bc2f5837abd041949779d Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 14:57:41 +0000 Subject: vim-patch:8.1.1742: still some match functions in evalfunc.c Problem: Still some match functions in evalfunc.c. Solution: Move them to highlight.c. https://github.com/vim/vim/commit/7dfb016d25e3e3e1f4411026dda21d1536f21acc --- src/nvim/eval/funcs.c | 113 ---------------------------- src/nvim/ex_docmd.c | 64 ---------------- src/nvim/highlight_group.c | 178 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 177 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7a3b9e47f9..7ee32ec8cd 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1138,16 +1138,6 @@ win_T *get_optional_window(typval_T *argvars, int idx) return win; } -/// "clearmatches()" function -static void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *win = get_optional_window(argvars, 0); - - if (win != NULL) { - clear_matches(win); - } -} - /// "col(string)" function static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -8939,109 +8929,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// "setmatches()" function -static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_T *d; - list_T *s = NULL; - win_T *win = get_optional_window(argvars, 1); - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - if (win == NULL) { - return; - } - - list_T *const l = argvars[0].vval.v_list; - // To some extent make sure that we are dealing with a list from - // "getmatches()". - int li_idx = 0; - TV_LIST_ITER_CONST(l, li, { - if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT - || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { - semsg(_("E474: List item %d is either not a dictionary " - "or an empty one"), li_idx); - return; - } - if (!(tv_dict_find(d, S_LEN("group")) != NULL - && (tv_dict_find(d, S_LEN("pattern")) != NULL - || tv_dict_find(d, S_LEN("pos1")) != NULL) - && tv_dict_find(d, S_LEN("priority")) != NULL - && tv_dict_find(d, S_LEN("id")) != NULL)) { - semsg(_("E474: List item %d is missing one of the required keys"), - li_idx); - return; - } - li_idx++; - }); - - clear_matches(win); - bool match_add_failed = false; - TV_LIST_ITER_CONST(l, li, { - int i = 0; - - d = TV_LIST_ITEM_TV(li)->vval.v_dict; - dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); - if (di == NULL) { - if (s == NULL) { - s = tv_list_alloc(9); - } - - // match from matchaddpos() - for (i = 1; i < 9; i++) { - char buf[30]; // use 30 to avoid compiler warning - snprintf(buf, sizeof(buf), "pos%d", i); - dictitem_T *const pos_di = tv_dict_find(d, buf, -1); - if (pos_di != NULL) { - if (pos_di->di_tv.v_type != VAR_LIST) { - return; - } - - tv_list_append_tv(s, &pos_di->di_tv); - tv_list_ref(s); - } else { - break; - } - } - } - - // Note: there are three number buffers involved: - // - group_buf below. - // - numbuf in tv_dict_get_string(). - // - mybuf in tv_get_string(). - // - // If you change this code make sure that buffers will not get - // accidentally reused. - char group_buf[NUMBUFLEN]; - const char *const group = tv_dict_get_string_buf(d, "group", group_buf); - const int priority = (int)tv_dict_get_number(d, "priority"); - const int id = (int)tv_dict_get_number(d, "id"); - dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); - const char *const conceal = (conceal_di != NULL - ? tv_get_string(&conceal_di->di_tv) - : NULL); - if (i == 0) { - if (match_add(win, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal) != id) { - match_add_failed = true; - } - } else { - if (match_add(win, group, NULL, priority, id, s, conceal) != id) { - match_add_failed = true; - } - tv_list_unref(s); - s = NULL; - } - }); - if (!match_add_failed) { - rettv->vval.v_number = 0; - } -} - /// "setpos()" function static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 511dd44c9f..a5732a006d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9541,70 +9541,6 @@ static void ex_nohlsearch(exarg_T *eap) redraw_all_later(SOME_VALID); } -/// ":[N]match {group} {pattern}" -/// Sets nextcmd to the start of the next command, if any. Also called when -/// skipping commands to find the next command. -static void ex_match(exarg_T *eap) -{ - char_u *p; - char_u *g = NULL; - char_u *end; - int c; - int id; - - if (eap->line2 <= 3) { - id = eap->line2; - } else { - emsg(e_invcmd); - return; - } - - // First clear any old pattern. - if (!eap->skip) { - match_delete(curwin, id, false); - } - - if (ends_excmd(*eap->arg)) { - end = eap->arg; - } else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { - end = eap->arg + 4; - } else { - p = skiptowhite(eap->arg); - if (!eap->skip) { - g = vim_strnsave(eap->arg, p - eap->arg); - } - p = skipwhite(p); - if (*p == NUL) { - // There must be two arguments. - xfree(g); - semsg(_(e_invarg2), eap->arg); - return; - } - end = skip_regexp(p + 1, *p, true, NULL); - if (!eap->skip) { - if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { - xfree(g); - eap->errmsg = e_trailing; - return; - } - if (*end != *p) { - xfree(g); - semsg(_(e_invarg2), p); - return; - } - - c = *end; - *end = NUL; - match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, - NULL, NULL); - xfree(g); - *end = c; - } - } - eap->nextcmd = find_nextcmd(end); -} - static void ex_fold(exarg_T *eap) { if (foldManualAllowed(true)) { diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 786b4631f5..72416923f1 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -3101,6 +3101,16 @@ int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) return OK; } +/// "clearmatches()" function +void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *win = get_optional_window(argvars, 0); + + if (win != NULL) { + clear_matches(win); + } +} + /// "getmatches()" function void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3156,6 +3166,110 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "setmatches()" function +void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *d; + list_T *s = NULL; + win_T *win = get_optional_window(argvars, 1); + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + if (win == NULL) { + return; + } + + list_T *const l = argvars[0].vval.v_list; + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int li_idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + semsg(_("E474: List item %d is either not a dictionary " + "or an empty one"), li_idx); + return; + } + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { + semsg(_("E474: List item %d is missing one of the required keys"), + li_idx); + return; + } + li_idx++; + }); + + clear_matches(win); + bool match_add_failed = false; + TV_LIST_ITER_CONST(l, li, { + int i = 0; + + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { + if (s == NULL) { + s = tv_list_alloc(9); + } + + // match from matchaddpos() + for (i = 1; i < 9; i++) { + char buf[30]; // use 30 to avoid compiler warning + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { + return; + } + + tv_list_append_tv(s, &pos_di->di_tv); + tv_list_ref(s); + } else { + break; + } + } + } + + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); + if (i == 0) { + if (match_add(win, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } + } else { + if (match_add(win, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); + s = NULL; + } + }); + if (!match_add_failed) { + rettv->vval.v_number = 0; + } +} + + /// "matchadd()" function void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -3284,3 +3398,67 @@ void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// ":[N]match {group} {pattern}" +/// Sets nextcmd to the start of the next command, if any. Also called when +/// skipping commands to find the next command. +void ex_match(exarg_T *eap) +{ + char_u *p; + char_u *g = NULL; + char_u *end; + int c; + int id; + + if (eap->line2 <= 3) { + id = (int)eap->line2; + } else { + emsg(e_invcmd); + return; + } + + // First clear any old pattern. + if (!eap->skip) { + match_delete(curwin, id, false); + } + + if (ends_excmd(*eap->arg)) { + end = eap->arg; + } else if ((STRNICMP(eap->arg, "none", 4) == 0 + && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { + end = eap->arg + 4; + } else { + p = skiptowhite(eap->arg); + if (!eap->skip) { + g = vim_strnsave(eap->arg, (size_t)(p - eap->arg)); + } + p = skipwhite(p); + if (*p == NUL) { + // There must be two arguments. + xfree(g); + semsg(_(e_invarg2), eap->arg); + return; + } + end = skip_regexp(p + 1, *p, true, NULL); + if (!eap->skip) { + if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { + xfree(g); + eap->errmsg = e_trailing; + return; + } + if (*end != *p) { + xfree(g); + semsg(_(e_invarg2), p); + return; + } + + c = *end; + *end = NUL; + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); + xfree(g); + *end = (char_u)c; + } + } + eap->nextcmd = find_nextcmd(end); +} + -- cgit From 087a9603d05b6210b0503893f16101b044125631 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 18 Mar 2022 14:36:59 +0000 Subject: vim-patch:8.1.1743: 'hlsearch' and match highlighting in the wrong place Problem: 'hlsearch' and match highlighting in the wrong place. Solution: Move highlighting from inside screen functions to highlight.c. https://github.com/vim/vim/commit/bbca7732e8a3deb6e5dcf84739579a2667a75475 --- src/nvim/highlight_group.c | 513 ++++++++++++++++++++++++++++++++++++++++++++- src/nvim/screen.c | 506 ++------------------------------------------ 2 files changed, 528 insertions(+), 491 deletions(-) (limited to 'src') diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 72416923f1..ee78a79a97 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -4,6 +4,7 @@ // highlight_group.c: code for managing highlight groups // Includes highlighting matches +#include #include "nvim/autocmd.h" #include "nvim/api/private/helpers.h" #include "nvim/charset.h" @@ -11,11 +12,13 @@ #include "nvim/eval/funcs.h" #include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" +#include "nvim/fold.h" #include "nvim/garray.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/map.h" +#include "nvim/memline.h" #include "nvim/option.h" #include "nvim/regexp.h" #include "nvim/runtime.h" @@ -29,6 +32,7 @@ /// @} #define MAX_SYN_NAME 200 +#define SEARCH_HL_PRIORITY 0 static char *e_invalwindow = N_("E957: Invalid window number"); @@ -2813,8 +2817,8 @@ int name_to_ctermcolor(const char *name) /// particular ID is desired /// @param[in] conceal_char pointer to conceal replacement char /// @return ID of added match, -1 on failure. -int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, - list_T *pos_list, const char *const conceal_char) +static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, + list_T *pos_list, const char *const conceal_char) FUNC_ATTR_NONNULL_ARG(1, 2) { matchitem_T *cur; @@ -2998,7 +3002,7 @@ fail: /// Delete match with ID 'id' in the match list of window 'wp'. /// /// @param perr print error messages if true. -int match_delete(win_T *wp, int id, bool perr) +static int match_delete(win_T *wp, int id, bool perr) { matchitem_T *cur = wp->w_match_head; matchitem_T *prev = cur; @@ -3077,7 +3081,508 @@ matchitem_T *get_match(win_T *wp, int id) return cur; } -int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) +/// Init for calling prepare_search_hl(). +void init_search_hl(win_T *wp, match_T *search_hl) + FUNC_ATTR_NONNULL_ALL +{ + // Setup for match and 'hlsearch' highlighting. Disable any previous + // match + matchitem_T *cur = wp->w_match_head; + while (cur != NULL) { + cur->hl.rm = cur->match; + if (cur->hlg_id == 0) { + cur->hl.attr = 0; + } else { + cur->hl.attr = syn_id2attr(cur->hlg_id); + } + cur->hl.buf = wp->w_buffer; + cur->hl.lnum = 0; + cur->hl.first_lnum = 0; + // Set the time limit to 'redrawtime'. + cur->hl.tm = profile_setlimit(p_rdt); + cur = cur->next; + } + search_hl->buf = wp->w_buffer; + search_hl->lnum = 0; + search_hl->first_lnum = 0; + search_hl->attr = win_hl_attr(wp, HLF_L); + + // time limit is set at the toplevel, for all windows +} + +/// @param shl points to a match. Fill on match. +/// @param posmatch match positions +/// @param mincol minimal column for a match +/// +/// @return one on match, otherwise return zero. +static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) + FUNC_ATTR_NONNULL_ALL +{ + int i; + int found = -1; + + shl->lnum = 0; + for (i = posmatch->cur; i < MAXPOSMATCH; i++) { + llpos_T *pos = &posmatch->pos[i]; + + if (pos->lnum == 0) { + break; + } + if (pos->len == 0 && pos->col < mincol) { + continue; + } + if (pos->lnum == lnum) { + if (found >= 0) { + // if this match comes before the one at "found" then swap + // them + if (pos->col < posmatch->pos[found].col) { + llpos_T tmp = *pos; + + *pos = posmatch->pos[found]; + posmatch->pos[found] = tmp; + } + } else { + found = i; + } + } + } + posmatch->cur = 0; + if (found >= 0) { + colnr_T start = posmatch->pos[found].col == 0 + ? 0: posmatch->pos[found].col - 1; + colnr_T end = posmatch->pos[found].col == 0 + ? MAXCOL : start + posmatch->pos[found].len; + + shl->lnum = lnum; + shl->rm.startpos[0].lnum = 0; + shl->rm.startpos[0].col = start; + shl->rm.endpos[0].lnum = 0; + shl->rm.endpos[0].col = end; + shl->is_addpos = true; + posmatch->cur = found + 1; + return 1; + } + return 0; +} + +/// Search for a next 'hlsearch' or match. +/// Uses shl->buf. +/// Sets shl->lnum and shl->rm contents. +/// Note: Assumes a previous match is always before "lnum", unless +/// shl->lnum is zero. +/// Careful: Any pointers for buffer lines will become invalid. +/// +/// @param shl points to search_hl or a match +/// @param mincol minimal column for a match +/// @param cur to retrieve match positions if any +static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_T lnum, + colnr_T mincol, matchitem_T *cur) + FUNC_ATTR_NONNULL_ARG(2) +{ + linenr_T l; + colnr_T matchcol; + long nmatched = 0; + int save_called_emsg = called_emsg; + + // for :{range}s/pat only highlight inside the range + if (lnum < search_first_line || lnum > search_last_line) { + shl->lnum = 0; + return; + } + + if (shl->lnum != 0) { + // Check for three situations: + // 1. If the "lnum" is below a previous match, start a new search. + // 2. If the previous match includes "mincol", use it. + // 3. Continue after the previous match. + l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; + if (lnum > l) { + shl->lnum = 0; + } else if (lnum < l || shl->rm.endpos[0].col > mincol) { + return; + } + } + + // Repeat searching for a match until one is found that includes "mincol" + // or none is found in this line. + called_emsg = false; + for (;;) { + // Stop searching after passing the time limit. + if (profile_passed_limit(shl->tm)) { + shl->lnum = 0; // no match found in time + break; + } + // Three situations: + // 1. No useful previous match: search from start of line. + // 2. Not Vi compatible or empty match: continue at next character. + // Break the loop if this is beyond the end of the line. + // 3. Vi compatible searching: continue at end of previous match. + if (shl->lnum == 0) { + matchcol = 0; + } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL + || (shl->rm.endpos[0].lnum == 0 + && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { + char_u *ml; + + matchcol = shl->rm.startpos[0].col; + ml = ml_get_buf(shl->buf, lnum, false) + matchcol; + if (*ml == NUL) { + matchcol++; + shl->lnum = 0; + break; + } + matchcol += utfc_ptr2len(ml); + } else { + matchcol = shl->rm.endpos[0].col; + } + + shl->lnum = lnum; + if (shl->rm.regprog != NULL) { + // Remember whether shl->rm is using a copy of the regprog in + // cur->match. + bool regprog_is_copy = (shl != search_hl + && cur != NULL + && shl == &cur->hl + && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = false; + + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, + &(shl->tm), &timed_out); + // Copy the regprog, in case it got freed and recompiled. + if (regprog_is_copy) { + cur->match.regprog = cur->hl.rm.regprog; + } + if (called_emsg || got_int || timed_out) { + // Error while handling regexp: stop using this regexp. + if (shl == search_hl) { + // don't free regprog in the match list, it's a copy + vim_regfree(shl->rm.regprog); + set_no_hlsearch(true); + } + shl->rm.regprog = NULL; + shl->lnum = 0; + got_int = false; // avoid the "Type :quit to exit Vim" message + break; + } + } else if (cur != NULL) { + nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); + } + if (nmatched == 0) { + shl->lnum = 0; // no match found + break; + } + if (shl->rm.startpos[0].lnum > 0 + || shl->rm.startpos[0].col >= mincol + || nmatched > 1 + || shl->rm.endpos[0].col > mincol) { + shl->lnum += shl->rm.startpos[0].lnum; + break; // useful match found + } + + // Restore called_emsg for assert_fails(). + called_emsg = save_called_emsg; + } +} + +/// Advance to the match in window "wp" line "lnum" or past it. +void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) + FUNC_ATTR_NONNULL_ALL +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag; // flag to indicate whether search_hl + // has been processed or not + + // When using a multi-line pattern, start searching at the top + // of the window or just after a closed fold. + // Do this both for search_hl and the match list. + cur = wp->w_match_head; + shl_flag = false; + while (cur != NULL || shl_flag == false) { + if (shl_flag == false) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; // -V595 + } + if (shl->rm.regprog != NULL + && shl->lnum == 0 + && re_multiline(shl->rm.regprog)) { + if (shl->first_lnum == 0) { + for (shl->first_lnum = lnum; + shl->first_lnum > wp->w_topline; + shl->first_lnum--) { + if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { + break; + } + } + } + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress + int n = 0; + while (shl->first_lnum < lnum && (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress))) { + next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, + shl == search_hl ? NULL : cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); + if (shl->lnum != 0) { + shl->first_lnum = shl->lnum + + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum; + n = shl->rm.endpos[0].col; + } else { + shl->first_lnum++; + n = 0; + } + } + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } +} + +/// Prepare for 'hlsearch' and match highlighting in one window line. +/// Return true if there is such highlighting and set "search_attr" to the +/// current highlight attribute. +bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, + match_T *search_hl, int *search_attr, bool *search_attr_from_match) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + bool area_highlighting = false; + + // Handle highlighting the last used search pattern and matches. + // Do this for both search_hl and the match list. + while (cur != NULL || !shl_flag) { + if (!shl_flag) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; // -V595 + } + shl->startcol = MAXCOL; + shl->endcol = MAXCOL; + shl->attr_cur = 0; + shl->is_addpos = false; + if (cur != NULL) { + cur->pos.cur = 0; + } + next_search_hl(wp, search_hl, shl, lnum, mincol, + shl == search_hl ? NULL : cur); + + // Need to get the line again, a multi-line regexp may have made it + // invalid. + *line = ml_get_buf(wp->w_buffer, lnum, false); + + if (shl->lnum != 0 && shl->lnum <= lnum) { + if (shl->lnum == lnum) { + shl->startcol = shl->rm.startpos[0].col; + } else { + shl->startcol = 0; + } + if (lnum == shl->lnum + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum) { + shl->endcol = shl->rm.endpos[0].col; + } else { + shl->endcol = MAXCOL; + } + // Highlight one character for an empty match. + if (shl->startcol == shl->endcol) { + if ((*line)[shl->endcol] != NUL) { + shl->endcol += utfc_ptr2len(*line + shl->endcol); + } else { + shl->endcol++; + } + } + if ((long)shl->startcol < mincol) { // match at leftcol + shl->attr_cur = shl->attr; + *search_attr = shl->attr; + *search_attr_from_match = shl != search_hl; + } + area_highlighting = true; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + return area_highlighting; +} + +/// For a position in a line: Check for start/end of 'hlsearch' and other +/// matches. +/// After end, check for start/end of next match. +/// When another match, have to check for start again. +/// Watch out for matching an empty string! +/// Return the updated search_attr. +int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl, + int *has_match_conc, int *match_conc, int lcs_eol_one, + bool *search_attr_from_match) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + int search_attr = 0; + + // Do this for 'search_hl' and the match list (ordered by priority). + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress + while (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress)) { + if (shl->startcol != MAXCOL + && col >= shl->startcol + && col < shl->endcol) { + int next_col = col + utfc_ptr2len(*line + col); + + if (shl->endcol < next_col) { + shl->endcol = next_col; + } + shl->attr_cur = shl->attr; + // Match with the "Conceal" group results in hiding + // the match. + if (cur != NULL + && shl != search_hl + && syn_name2id("Conceal") == cur->hlg_id) { + *has_match_conc = col == shl->startcol ? 2 : 1; + *match_conc = cur->conceal_char; + } else { + *has_match_conc = 0; + } + } else if (col == shl->endcol) { + shl->attr_cur = 0; + + next_search_hl(wp, search_hl, shl, lnum, col, + shl == search_hl ? NULL : cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); + + // Need to get the line again, a multi-line regexp + // may have made it invalid. + *line = ml_get_buf(wp->w_buffer, lnum, false); + + if (shl->lnum == lnum) { + shl->startcol = shl->rm.startpos[0].col; + if (shl->rm.endpos[0].lnum == 0) { + shl->endcol = shl->rm.endpos[0].col; + } else { + shl->endcol = MAXCOL; + } + + if (shl->startcol == shl->endcol) { + // highlight empty match, try again after it + shl->endcol += utfc_ptr2len(*line + shl->endcol); + } + + // Loop to check if the match starts at the + // current position + continue; + } + } + break; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + + // Use attributes from match with highest priority among + // 'search_hl' and the match list. + *search_attr_from_match = false; + search_attr = search_hl->attr_cur; + cur = wp->w_match_head; + shl_flag = false; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (shl->attr_cur != 0) { + search_attr = shl->attr_cur; + *search_attr_from_match = shl != search_hl; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + // Only highlight one character after the last column. + if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) { + search_attr = 0; + } + return search_attr; +} + +bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) +{ + long prevcol = curcol; + matchitem_T *cur; // points to the match list + + // we're not really at that column when skipping some text + if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { + prevcol++; + } + + if (!search_hl->is_addpos && prevcol == search_hl->startcol) { + return true; + } else { + cur = wp->w_match_head; + while (cur != NULL) { + if (!cur->hl.is_addpos && prevcol == cur->hl.startcol) { + return true; + } + cur = cur->next; + } + } + return false; +} + +/// Get highlighting for the char after the text in "char_attr" from 'hlsearch' +/// or match highlighting. +void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + + *char_attr = search_hl->attr; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (col - 1 == (long)shl->startcol + && (shl == search_hl || !shl->is_addpos)) { + *char_attr = shl->attr; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } +} + +static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) { dictitem_T *di; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0e1f469a4b..83d555e584 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -164,7 +164,6 @@ static bool resizing = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "screen.c.generated.h" #endif -#define SEARCH_HL_PRIORITY 0 static char *provider_err = NULL; @@ -768,7 +767,7 @@ static void win_update(win_T *wp, DecorProviders *providers) redraw_win_signcol(wp); - init_search_hl(wp); + init_search_hl(wp, &search_hl); /* Force redraw when width of 'number' or 'relativenumber' column * changes. */ @@ -1533,7 +1532,7 @@ static void win_update(win_T *wp, DecorProviders *providers) // will draw "@ " lines below. row = wp->w_grid.Rows + 1; } else { - prepare_search_hl(wp, lnum); + prepare_search_hl(wp, &search_hl, lnum); // Let the syntax stuff know we skipped a few lines. if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum && syntax_present(wp)) { @@ -2095,13 +2094,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int line_attr_save; int line_attr_lowprio = 0; // low-priority attribute for the line int line_attr_lowprio_save; - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag; // flag to indicate whether search_hl - // has been processed or not - bool prevcol_hl_flag; // flag to indicate whether prevcol - // equals startcol of search_hl or one - // of the matches int prev_c = 0; // previous Arabic character int prev_c1 = 0; // first composing char for prev_c @@ -2599,66 +2591,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } - /* - * Handle highlighting the last used search pattern and matches. - * Do this for both search_hl and the match list. - */ - cur = wp->w_match_head; - shl_flag = false; - while ((cur != NULL || !shl_flag) && !number_only - && !has_fold && !end_fill) { - if (!shl_flag) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - shl->startcol = MAXCOL; - shl->endcol = MAXCOL; - shl->attr_cur = 0; - shl->is_addpos = false; - v = (ptr - line); - if (cur != NULL) { - cur->pos.cur = 0; - } - next_search_hl(wp, shl, lnum, (colnr_T)v, - shl == &search_hl ? NULL : cur); - - // Need to get the line again, a multi-line regexp may have made it - // invalid. - line = ml_get_buf(wp->w_buffer, lnum, false); - ptr = line + v; - - if (shl->lnum != 0 && shl->lnum <= lnum) { - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - } else { - shl->startcol = 0; - } - if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - // Highlight one character for an empty match. - if (shl->startcol == shl->endcol) { - if (line[shl->endcol] != NUL) { - shl->endcol += utfc_ptr2len(line + shl->endcol); - } else { - ++shl->endcol; - } - } - if ((long)shl->startcol < v) { // match at leftcol - shl->attr_cur = shl->attr; - search_attr = shl->attr; - search_attr_from_match = shl != &search_hl; - } - area_highlighting = true; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } + if (!number_only && !has_fold && !end_fill) { + v = ptr - line; + area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v, + &line, &search_hl, &search_attr, + &search_attr_from_match); + ptr = line + v; // "line" may have been updated } unsigned off = 0; // Offset relative start of line @@ -3013,115 +2951,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } if (!n_extra) { - /* - * Check for start/end of search pattern match. - * After end, check for start/end of next match. - * When another match, have to check for start again. - * Watch out for matching an empty string! - * Do this for 'search_hl' and the match list (ordered by - * priority). - */ + // Check for start/end of 'hlsearch' and other matches. + // After end, check for start/end of next match. + // When another match, have to check for start again. v = (ptr - line); - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - while (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress)) { - if (shl->startcol != MAXCOL - && v >= (long)shl->startcol - && v < (long)shl->endcol) { - int tmp_col = v + utfc_ptr2len(ptr); - - if (shl->endcol < tmp_col) { - shl->endcol = tmp_col; - } - shl->attr_cur = shl->attr; - // Match with the "Conceal" group results in hiding - // the match. - if (cur != NULL - && shl != &search_hl - && syn_name2id("Conceal") == cur->hlg_id) { - has_match_conc = v == (long)shl->startcol ? 2 : 1; - match_conc = cur->conceal_char; - } else { - has_match_conc = 0; - } - } else if (v == (long)shl->endcol) { - shl->attr_cur = 0; - - next_search_hl(wp, shl, lnum, (colnr_T)v, - shl == &search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - - // Need to get the line again, a multi-line regexp - // may have made it invalid. - line = ml_get_buf(wp->w_buffer, lnum, false); - ptr = line + v; - - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - - if (shl->startcol == shl->endcol) { - // highlight empty match, try again after it - shl->endcol += utfc_ptr2len(line + shl->endcol); - } - - // Loop to check if the match starts at the - // current position - continue; - } - } - break; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } - - /* Use attributes from match with highest priority among - * 'search_hl' and the match list. */ - search_attr_from_match = false; - search_attr = search_hl.attr_cur; - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (shl->attr_cur != 0) { - search_attr = shl->attr_cur; - search_attr_from_match = shl != &search_hl; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } - // Only highlight one character after the last column. - if (*ptr == NUL - && (wp->w_p_list && lcs_eol_one == -1)) { - search_attr = 0; - } + search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc, + &match_conc, lcs_eol_one, &search_attr_from_match); + ptr = line + v; // "line" may have been changed // Do not allow a conceal over EOL otherwise EOL will be missed // and bad things happen. @@ -3939,30 +3775,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // At end of the text line or just after the last character. if (c == NUL && eol_hl_off == 0) { - long prevcol = (ptr - line) - 1; - - // we're not really at that column when skipping some text - if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { - prevcol++; - } + // flag to indicate whether prevcol equals startcol of search_hl or + // one of the matches + bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl, + (long)(ptr - line) - 1); // Invert at least one char, used for Visual and empty line or // highlight match at end of line. If it's beyond the last // char on the screen, just overwrite that one (tricky!) Not // needed when a '$' was displayed for 'list'. - prevcol_hl_flag = false; - if (!search_hl.is_addpos && prevcol == (long)search_hl.startcol) { - prevcol_hl_flag = true; - } else { - cur = wp->w_match_head; - while (cur != NULL) { - if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol) { - prevcol_hl_flag = true; - break; - } - cur = cur->next; - } - } if (wp->w_p_lcs_chars.eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && (VIsual_mode != Ctrl_V @@ -3993,25 +3814,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (area_attr == 0 && !has_fold) { // Use attributes from match with highest priority among // 'search_hl' and the match list. - char_attr = search_hl.attr; - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if ((ptr - line) - 1 == (long)shl->startcol - && (shl == &search_hl || !shl->is_addpos)) { - char_attr = shl->attr; - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } + get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr); } int eol_attr = char_attr; @@ -6192,277 +5995,6 @@ static void end_search_hl(void) } -/* - * Init for calling prepare_search_hl(). - */ -static void init_search_hl(win_T *wp) - FUNC_ATTR_NONNULL_ALL -{ - // Setup for match and 'hlsearch' highlighting. Disable any previous - // match - matchitem_T *cur = wp->w_match_head; - while (cur != NULL) { - cur->hl.rm = cur->match; - if (cur->hlg_id == 0) { - cur->hl.attr = 0; - } else { - cur->hl.attr = syn_id2attr(cur->hlg_id); - } - cur->hl.buf = wp->w_buffer; - cur->hl.lnum = 0; - cur->hl.first_lnum = 0; - // Set the time limit to 'redrawtime'. - cur->hl.tm = profile_setlimit(p_rdt); - cur = cur->next; - } - search_hl.buf = wp->w_buffer; - search_hl.lnum = 0; - search_hl.first_lnum = 0; - search_hl.attr = win_hl_attr(wp, HLF_L); - - // time limit is set at the toplevel, for all windows -} - -/* - * Advance to the match in window "wp" line "lnum" or past it. - */ -static void prepare_search_hl(win_T *wp, linenr_T lnum) - FUNC_ATTR_NONNULL_ALL -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag; // flag to indicate whether search_hl - // has been processed or not - - // When using a multi-line pattern, start searching at the top - // of the window or just after a closed fold. - // Do this both for search_hl and the match list. - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || shl_flag == false) { - if (shl_flag == false) { - shl = &search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - if (shl->rm.regprog != NULL - && shl->lnum == 0 - && re_multiline(shl->rm.regprog)) { - if (shl->first_lnum == 0) { - for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; - shl->first_lnum--) { - if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { - break; - } - } - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - int n = 0; - while (shl->first_lnum < lnum && (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress))) { - next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, - shl == &search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - if (shl->lnum != 0) { - shl->first_lnum = shl->lnum - + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum; - n = shl->rm.endpos[0].col; - } else { - ++shl->first_lnum; - n = 0; - } - } - } - if (shl != &search_hl && cur != NULL) { - cur = cur->next; - } - } -} - -/// Search for a next 'hlsearch' or match. -/// Uses shl->buf. -/// Sets shl->lnum and shl->rm contents. -/// Note: Assumes a previous match is always before "lnum", unless -/// shl->lnum is zero. -/// Careful: Any pointers for buffer lines will become invalid. -/// -/// @param shl points to search_hl or a match -/// @param mincol minimal column for a match -/// @param cur to retrieve match positions if any -static void next_search_hl(win_T *win, match_T *shl, linenr_T lnum, colnr_T mincol, - matchitem_T *cur) - FUNC_ATTR_NONNULL_ARG(2) -{ - linenr_T l; - colnr_T matchcol; - long nmatched = 0; - int save_called_emsg = called_emsg; - - // for :{range}s/pat only highlight inside the range - if (lnum < search_first_line || lnum > search_last_line) { - shl->lnum = 0; - return; - } - - if (shl->lnum != 0) { - // Check for three situations: - // 1. If the "lnum" is below a previous match, start a new search. - // 2. If the previous match includes "mincol", use it. - // 3. Continue after the previous match. - l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) { - shl->lnum = 0; - } else if (lnum < l || shl->rm.endpos[0].col > mincol) { - return; - } - } - - /* - * Repeat searching for a match until one is found that includes "mincol" - * or none is found in this line. - */ - called_emsg = FALSE; - for (;;) { - // Stop searching after passing the time limit. - if (profile_passed_limit(shl->tm)) { - shl->lnum = 0; // no match found in time - break; - } - // Three situations: - // 1. No useful previous match: search from start of line. - // 2. Not Vi compatible or empty match: continue at next character. - // Break the loop if this is beyond the end of the line. - // 3. Vi compatible searching: continue at end of previous match. - if (shl->lnum == 0) { - matchcol = 0; - } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { - char_u *ml; - - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, false) + matchcol; - if (*ml == NUL) { - ++matchcol; - shl->lnum = 0; - break; - } - matchcol += utfc_ptr2len(ml); - } else { - matchcol = shl->rm.endpos[0].col; - } - - shl->lnum = lnum; - if (shl->rm.regprog != NULL) { - // Remember whether shl->rm is using a copy of the regprog in - // cur->match. - bool regprog_is_copy = (shl != &search_hl - && cur != NULL - && shl == &cur->hl - && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = false; - - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, - &(shl->tm), &timed_out); - // Copy the regprog, in case it got freed and recompiled. - if (regprog_is_copy) { - cur->match.regprog = cur->hl.rm.regprog; - } - if (called_emsg || got_int || timed_out) { - // Error while handling regexp: stop using this regexp. - if (shl == &search_hl) { - // don't free regprog in the match list, it's a copy - vim_regfree(shl->rm.regprog); - set_no_hlsearch(true); - } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = FALSE; // avoid the "Type :quit to exit Vim" message - break; - } - } else if (cur != NULL) { - nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); - } - if (nmatched == 0) { - shl->lnum = 0; // no match found - break; - } - if (shl->rm.startpos[0].lnum > 0 - || shl->rm.startpos[0].col >= mincol - || nmatched > 1 - || shl->rm.endpos[0].col > mincol) { - shl->lnum += shl->rm.startpos[0].lnum; - break; // useful match found - } - - // Restore called_emsg for assert_fails(). - called_emsg = save_called_emsg; - } -} - -/// @param shl points to a match. Fill on match. -/// @param posmatch match positions -/// @param mincol minimal column for a match -/// -/// @return one on match, otherwise return zero. -static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) - FUNC_ATTR_NONNULL_ALL -{ - int i; - int found = -1; - - shl->lnum = 0; - for (i = posmatch->cur; i < MAXPOSMATCH; i++) { - llpos_T *pos = &posmatch->pos[i]; - - if (pos->lnum == 0) { - break; - } - if (pos->len == 0 && pos->col < mincol) { - continue; - } - if (pos->lnum == lnum) { - if (found >= 0) { - // if this match comes before the one at "found" then swap - // them - if (pos->col < posmatch->pos[found].col) { - llpos_T tmp = *pos; - - *pos = posmatch->pos[found]; - posmatch->pos[found] = tmp; - } - } else { - found = i; - } - } - } - posmatch->cur = 0; - if (found >= 0) { - colnr_T start = posmatch->pos[found].col == 0 - ? 0: posmatch->pos[found].col - 1; - colnr_T end = posmatch->pos[found].col == 0 - ? MAXCOL : start + posmatch->pos[found].len; - - shl->lnum = lnum; - shl->rm.startpos[0].lnum = 0; - shl->rm.startpos[0].col = start; - shl->rm.endpos[0].lnum = 0; - shl->rm.endpos[0].col = end; - shl->is_addpos = true; - posmatch->cur = found + 1; - return 1; - } - return 0; -} - - /// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' /// with character 'c1' in first column followed by 'c2' in the other columns. /// Use attributes 'attr'. -- cgit From e9b53f3fb56e91a0d4f15bb36d8068bcc6ea88cf Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Mon, 21 Mar 2022 00:25:03 +0100 Subject: fix(PVS/V583): the '?:' operator always returns one and the same value (#17790) --- src/nvim/window.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 1f9cfbf4fd..50a56056bf 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1082,7 +1082,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } else { - hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; + hsep_height = STATUS_HEIGHT; layout = FR_COL; /* @@ -1374,8 +1374,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false); } else { - win_new_height(oldwin, oldwin_height - (new_size - + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT))); + win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); } if (before) { // new window above current one @@ -1643,7 +1642,7 @@ int make_windows(int count, bool vertical) // Each window needs at least 'winminheight' lines // If statusline isn't global, each window also needs a statusline maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + (global_stl_height() > 0 ? 1 : STATUS_HEIGHT)); + - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); } if (maxcount < 2) { @@ -2166,7 +2165,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int totwincount -= wincount; } } else { // topfr->fr_layout == FR_COL - hsep_height = global_stl_height() > 0 ? 1 : STATUS_HEIGHT; + hsep_height = STATUS_HEIGHT; topfr->fr_width = width; topfr->fr_height = height; -- cgit From f63a52a0db936d04bdc65c121f7bd279709d4cf2 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 22 Mar 2022 22:31:06 +0000 Subject: vim-patch:8.1.1608: the evalfunc.c file is too big (#17807) Problem: The evalfunc.c file is too big. Solution: Move sign functionality to sign.c. https://github.com/vim/vim/commit/b60d8514b8813e2f3acefd454efcccbe04ac135a --- src/nvim/eval/funcs.c | 266 ------------------------------------ src/nvim/sign.c | 367 +++++++++++++++++++++++++++++++++++++++++++------- src/nvim/sign.h | 1 + 3 files changed, 317 insertions(+), 317 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 7ee32ec8cd..69055db9a6 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9251,272 +9251,6 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = get_sw_value(curbuf); } -/// "sign_define()" function -static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const char *name; - - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) { - // Define multiple signs - tv_list_alloc_ret(rettv, kListLenMayKnow); - - sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list); - return; - } - - // Define a single sign - rettv->vval.v_number = -1; - - name = tv_get_string_chk(&argvars[0]); - if (name == NULL) { - return; - } - - if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - - rettv->vval.v_number = sign_define_from_dict(name, - argvars[1].v_type == - VAR_DICT ? argvars[1].vval.v_dict : NULL); -} - -/// "sign_getdefined()" function -static void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const char *name = NULL; - - tv_list_alloc_ret(rettv, 0); - - if (argvars[0].v_type != VAR_UNKNOWN) { - name = tv_get_string(&argvars[0]); - } - - sign_getlist((const char_u *)name, rettv->vval.v_list); -} - -/// "sign_getplaced()" function -static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - buf_T *buf = NULL; - dict_T *dict; - dictitem_T *di; - linenr_T lnum = 0; - int sign_id = 0; - const char *group = NULL; - bool notanum = false; - - tv_list_alloc_ret(rettv, 0); - - if (argvars[0].v_type != VAR_UNKNOWN) { - // get signs placed in the specified buffer - buf = get_buf_arg(&argvars[0]); - if (buf == NULL) { - return; - } - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[1].v_type != VAR_DICT - || ((dict = argvars[1].vval.v_dict) == NULL)) { - emsg(_(e_dictreq)); - return; - } - if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) { - // get signs placed at this line - lnum = (linenr_T)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) { - return; - } - (void)lnum; - lnum = tv_get_lnum(&di->di_tv); - } - if ((di = tv_dict_find(dict, "id", -1)) != NULL) { - // get sign placed with this identifier - sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) { - return; - } - } - if ((di = tv_dict_find(dict, "group", -1)) != NULL) { - group = tv_get_string_chk(&di->di_tv); - if (group == NULL) { - return; - } - if (*group == '\0') { // empty string means global group - group = NULL; - } - } - } - } - - sign_get_placed(buf, lnum, sign_id, (const char_u *)group, - rettv->vval.v_list); -} - -/// "sign_jump()" function -static void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int sign_id; - char *sign_group = NULL; - buf_T *buf; - bool notanum = false; - - rettv->vval.v_number = -1; - - // Sign identifier - sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); - if (notanum) { - return; - } - if (sign_id <= 0) { - emsg(_(e_invarg)); - return; - } - - // Sign group - const char *sign_group_chk = tv_get_string_chk(&argvars[1]); - if (sign_group_chk == NULL) { - return; - } - if (sign_group_chk[0] == '\0') { - sign_group = NULL; // global sign group - } else { - sign_group = xstrdup(sign_group_chk); - } - - // Buffer to place the sign - buf = get_buf_arg(&argvars[2]); - if (buf == NULL) { - goto cleanup; - } - - rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf); - -cleanup: - xfree(sign_group); -} - -/// "sign_place()" function -static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_T *dict = NULL; - - rettv->vval.v_number = -1; - - if (argvars[4].v_type != VAR_UNKNOWN - && (argvars[4].v_type != VAR_DICT - || ((dict = argvars[4].vval.v_dict) == NULL))) { - emsg(_(e_dictreq)); - return; - } - - rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3], - dict); -} - -/// "sign_placelist()" function. Place multiple signs. -static void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int sign_id; - - tv_list_alloc_ret(rettv, kListLenMayKnow); - - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - - // Process the List of sign attributes - TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { - sign_id = -1; - if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { - sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); - } else { - emsg(_(e_dictreq)); - } - tv_list_append_number(rettv->vval.v_list, sign_id); - }); -} - -/// "sign_undefine()" function -static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const char *name; - - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) { - // Undefine multiple signs - tv_list_alloc_ret(rettv, kListLenMayKnow); - - sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list); - return; - } - - rettv->vval.v_number = -1; - - if (argvars[0].v_type == VAR_UNKNOWN) { - // Free all the signs - free_signs(); - rettv->vval.v_number = 0; - } else { - // Free only the specified sign - name = tv_get_string_chk(&argvars[0]); - if (name == NULL) { - return; - } - - if (sign_undefine_by_name((const char_u *)name) == OK) { - rettv->vval.v_number = 0; - } - } -} - -/// "sign_unplace()" function -static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_T *dict = NULL; - - rettv->vval.v_number = -1; - - if (argvars[0].v_type != VAR_STRING) { - emsg(_(e_invarg)); - return; - } - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[1].v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return; - } - dict = argvars[1].vval.v_dict; - } - - rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict); -} - -/// "sign_unplacelist()" function -static void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - int retval; - - tv_list_alloc_ret(rettv, kListLenMayKnow); - - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - - TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { - retval = -1; - if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { - retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); - } else { - emsg(_(e_dictreq)); - } - tv_list_append_number(rettv->vval.v_list, retval); - }); -} - /// "simplify()" function static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 50400852b8..a50b4a5a99 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -119,7 +119,7 @@ static void sign_group_unref(char_u *groupname) /// @return true if 'sign' is in 'group'. /// A sign can either be in the global group (sign->group == NULL) /// or in a named group. If 'group' is '*', then the sign is part of the group. -bool sign_in_group(sign_entry_T *sign, const char_u *group) +static bool sign_in_group(sign_entry_T *sign, const char_u *group) { return ((group != NULL && STRCMP(group, "*") == 0) || (group == NULL && sign->se_group == NULL) @@ -128,7 +128,7 @@ bool sign_in_group(sign_entry_T *sign, const char_u *group) } /// Get the next free sign identifier in the specified group -int sign_group_get_next_signid(buf_T *buf, const char_u *groupname) +static int sign_group_get_next_signid(buf_T *buf, const char_u *groupname) { int id = 1; signgroup_T *group = NULL; @@ -246,8 +246,21 @@ static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, con insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon); } +/// Lookup a sign by typenr. Returns NULL if sign is not found. +static sign_T *find_sign_by_typenr(int typenr) +{ + sign_T *sp; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { + return sp; + } + } + return NULL; +} + /// Get the name of a sign by its typenr. -char_u *sign_typenr2name(int typenr) +static char_u *sign_typenr2name(int typenr) { sign_T *sp; @@ -260,7 +273,7 @@ char_u *sign_typenr2name(int typenr) } /// Return information about a sign in a Dict -dict_T *sign_get_info(sign_entry_T *sign) +static dict_T *sign_get_info(sign_entry_T *sign) { dict_T *d = tv_dict_alloc(); tv_dict_add_nr(d, S_LEN("id"), sign->se_id); @@ -358,8 +371,8 @@ static void sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign) /// @param lnum line number which gets the mark /// @param typenr typenr of sign we are adding /// @param has_text_or_icon sign has text or icon -void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum, int typenr, - bool has_text_or_icon) +static void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum, + int typenr, bool has_text_or_icon) { sign_entry_T *sign; // a sign in the signlist sign_entry_T *prev; // the previous sign @@ -405,7 +418,8 @@ void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T /// @param group sign group /// @param typenr typenr of sign we are adding /// @param prio sign priority -linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr, int prio) +static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr, + int prio) { sign_entry_T *sign; // a sign in the signlist @@ -456,19 +470,6 @@ sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int m return NULL; } -/// Lookup a sign by typenr. Returns NULL if sign is not found. -static sign_T *find_sign_by_typenr(int typenr) -{ - sign_T *sp; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (sp->sn_typenr == typenr) { - return sp; - } - } - return NULL; -} - /// Return the attributes of all the signs placed on line 'lnum' in buffer /// 'buf'. Used when refreshing the screen. Returns the number of signs. /// @param buf Buffer in which to search @@ -536,7 +537,7 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) /// /// @return the line number of the deleted sign. If multiple signs are deleted, /// then returns the line number of the last sign deleted. -linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) +static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) { sign_entry_T **lastp; // pointer to pointer to current sign sign_entry_T *sign; // a sign in a b_signlist @@ -593,7 +594,7 @@ linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) /// @param buf buffer to store sign in /// @param id sign ID /// @param group sign group -int buf_findsign(buf_T *buf, int id, char_u *group) +static int buf_findsign(buf_T *buf, int id, char_u *group) { sign_entry_T *sign; // a sign in the signlist @@ -636,7 +637,7 @@ static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char_u *grou /// @param buf buffer whose sign we are searching for /// @param lnum line number of sign /// @param groupname sign group name -int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname) +static int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname) { sign_entry_T *sign; // a sign in the signlist @@ -681,7 +682,7 @@ void buf_delete_signs(buf_T *buf, char_u *group) } /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. -void sign_list_placed(buf_T *rbuf, char_u *sign_group) +static void sign_list_placed(buf_T *rbuf, char_u *sign_group) { buf_T *buf; sign_entry_T *sign; @@ -913,8 +914,8 @@ static int sign_define_init_text(sign_T *sp, char_u *text) } /// Define a new sign or update an existing sign -int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl, - char_u *culhl, char *numhl) +static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, + char_u *texthl, char_u *culhl, char *numhl) { sign_T *sp_prev; sign_T *sp; @@ -987,7 +988,7 @@ int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text } /// Free the sign specified by 'name'. -int sign_undefine_by_name(const char_u *name) +static int sign_undefine_by_name(const char_u *name) { sign_T *sp_prev; sign_T *sp; @@ -1002,17 +1003,6 @@ int sign_undefine_by_name(const char_u *name) return OK; } -static void may_force_numberwidth_recompute(buf_T *buf, int unplace) -{ - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf - && (wp->w_p_nu || wp->w_p_rnu) - && (unplace || wp->w_nrwidth_width < 2) - && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) { - wp->w_nrwidth_line_count = 0; - } -} - /// List the signs matching 'name' static void sign_list_by_name(char_u *name) { @@ -1026,10 +1016,20 @@ static void sign_list_by_name(char_u *name) } } +static void may_force_numberwidth_recompute(buf_T *buf, int unplace) +{ + FOR_ALL_TAB_WINDOWS(tp, wp) + if (wp->w_buffer == buf + && (wp->w_p_nu || wp->w_p_rnu) + && (unplace || wp->w_nrwidth_width < 2) + && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) { + wp->w_nrwidth_line_count = 0; + } +} /// Place a sign at the specified file location or update a sign. -int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf, - linenr_T lnum, int prio) +static int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf, + linenr_T lnum, int prio) { sign_T *sp; @@ -1081,7 +1081,7 @@ int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, } /// Unplace the specified sign -int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) +static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) { if (buf->b_signlist == NULL) { // No signs in the buffer return OK; @@ -1125,7 +1125,7 @@ static void sign_unplace_at_cursor(char_u *groupname) } /// Jump to a sign. -linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) +static linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) { linenr_T lnum; @@ -1537,7 +1537,7 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) /// If 'name' is NULL, return a list of all the defined signs. /// Otherwise, return information about the specified sign. -void sign_getlist(const char_u *name, list_T *retlist) +static void sign_getlist(const char_u *name, list_T *retlist) { sign_T *sp = first_sign; dict_T *dict; @@ -1607,8 +1607,8 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const /// Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the /// sign placed at the line number. If 'lnum' is zero, return all the signs /// placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. -void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group, - list_T *retlist) +static void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group, + list_T *retlist) { if (buf != NULL) { sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); @@ -1896,7 +1896,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on /// failure. -int sign_define_from_dict(const char *name_arg, dict_T *dict) +static int sign_define_from_dict(const char *name_arg, dict_T *dict) { char *name = NULL; char *icon = NULL; @@ -1947,7 +1947,7 @@ cleanup: /// Define multiple signs using attributes from list 'l' and store the return /// values in 'retlist'. -void sign_define_multiple(list_T *l, list_T *retlist) +static void sign_define_multiple(list_T *l, list_T *retlist) { int retval; @@ -1962,10 +1962,156 @@ void sign_define_multiple(list_T *l, list_T *retlist) }); } +/// "sign_define()" function +void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *name; + + if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) { + // Define multiple signs + tv_list_alloc_ret(rettv, kListLenMayKnow); + + sign_define_multiple(argvars[0].vval.v_list, rettv->vval.v_list); + return; + } + + // Define a single sign + rettv->vval.v_number = -1; + + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) { + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + + rettv->vval.v_number = sign_define_from_dict(name, + argvars[1].v_type == + VAR_DICT ? argvars[1].vval.v_dict : NULL); +} + +/// "sign_getdefined()" function +void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *name = NULL; + + tv_list_alloc_ret(rettv, 0); + + if (argvars[0].v_type != VAR_UNKNOWN) { + name = tv_get_string(&argvars[0]); + } + + sign_getlist((const char_u *)name, rettv->vval.v_list); +} + +/// "sign_getplaced()" function +void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int sign_id = 0; + const char *group = NULL; + bool notanum = false; + + tv_list_alloc_ret(rettv, 0); + + if (argvars[0].v_type != VAR_UNKNOWN) { + // get signs placed in the specified buffer + buf = get_buf_arg(&argvars[0]); + if (buf == NULL) { + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[1].v_type != VAR_DICT + || ((dict = argvars[1].vval.v_dict) == NULL)) { + emsg(_(e_dictreq)); + return; + } + if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) { + // get signs placed at this line + lnum = (linenr_T)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) { + return; + } + (void)lnum; + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = tv_dict_find(dict, "id", -1)) != NULL) { + // get sign placed with this identifier + sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) { + return; + } + } + if ((di = tv_dict_find(dict, "group", -1)) != NULL) { + group = tv_get_string_chk(&di->di_tv); + if (group == NULL) { + return; + } + if (*group == '\0') { // empty string means global group + group = NULL; + } + } + } + } + + sign_get_placed(buf, lnum, sign_id, (const char_u *)group, + rettv->vval.v_list); +} + +/// "sign_jump()" function +void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int sign_id; + char *sign_group = NULL; + buf_T *buf; + bool notanum = false; + + rettv->vval.v_number = -1; + + // Sign identifier + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) { + return; + } + if (sign_id <= 0) { + emsg(_(e_invarg)); + return; + } + + // Sign group + const char *sign_group_chk = tv_get_string_chk(&argvars[1]); + if (sign_group_chk == NULL) { + return; + } + if (sign_group_chk[0] == '\0') { + sign_group = NULL; // global sign group + } else { + sign_group = xstrdup(sign_group_chk); + } + + // Buffer to place the sign + buf = get_buf_arg(&argvars[2]); + if (buf == NULL) { + goto cleanup; + } + + rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf); + +cleanup: + xfree(sign_group); +} + /// Place a new sign using the values specified in dict 'dict'. Returns the sign /// identifier if successfully placed, otherwise returns 0. -int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv, typval_T *buf_tv, - dict_T *dict) +static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv, + typval_T *buf_tv, dict_T *dict) { int sign_id = 0; char_u *group = NULL; @@ -2077,8 +2223,50 @@ cleanup: return ret_sign_id; } +/// "sign_place()" function +void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *dict = NULL; + + rettv->vval.v_number = -1; + + if (argvars[4].v_type != VAR_UNKNOWN + && (argvars[4].v_type != VAR_DICT + || ((dict = argvars[4].vval.v_dict) == NULL))) { + emsg(_(e_dictreq)); + return; + } + + rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3], + dict); +} + +/// "sign_placelist()" function. Place multiple signs. +void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int sign_id; + + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + + // Process the List of sign attributes + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + sign_id = -1; + if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { + sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); + } else { + emsg(_(e_dictreq)); + } + tv_list_append_number(rettv->vval.v_list, sign_id); + }); +} + /// Undefine multiple signs -void sign_undefine_multiple(list_T *l, list_T *retlist) +static void sign_undefine_multiple(list_T *l, list_T *retlist) { char_u *name; int retval; @@ -2093,9 +2281,41 @@ void sign_undefine_multiple(list_T *l, list_T *retlist) }); } +/// "sign_undefine()" function +void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *name; + + if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) { + // Undefine multiple signs + tv_list_alloc_ret(rettv, kListLenMayKnow); + + sign_undefine_multiple(argvars[0].vval.v_list, rettv->vval.v_list); + return; + } + + rettv->vval.v_number = -1; + + if (argvars[0].v_type == VAR_UNKNOWN) { + // Free all the signs + free_signs(); + rettv->vval.v_number = 0; + } else { + // Free only the specified sign + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) { + return; + } + + if (sign_undefine_by_name((const char_u *)name) == OK) { + rettv->vval.v_number = 0; + } + } +} + /// Unplace the sign with attributes specified in 'dict'. Returns 0 on success /// and -1 on failure. -int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict) +static int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict) { dictitem_T *di; int sign_id = 0; @@ -2151,3 +2371,48 @@ cleanup: return retval; } +/// "sign_unplace()" function +void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *dict = NULL; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type != VAR_STRING) { + emsg(_(e_invarg)); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[1].v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return; + } + dict = argvars[1].vval.v_dict; + } + + rettv->vval.v_number = sign_unplace_from_dict(&argvars[0], dict); +} + +/// "sign_unplacelist()" function +void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + int retval; + + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + retval = -1; + if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { + retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); + } else { + emsg(_(e_dictreq)); + } + tv_list_append_number(rettv->vval.v_list, retval); + }); +} diff --git a/src/nvim/sign.h b/src/nvim/sign.h index 9044c2d0bb..6e75a4e62b 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -4,6 +4,7 @@ #include #include "nvim/buffer_defs.h" +#include "nvim/eval/funcs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/sign_defs.h" -- cgit From 7863e6b709976d53d69b8495f1ab4417d965f4b3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 22 Mar 2022 22:31:50 +0000 Subject: vim-patch:8.2.1078: highlight and match functionality together in one file (#17805) Problem: Highlight and match functionality together in one file. Solution: Move match functionality to a separate file. (Yegappan Lakshmanan, closes vim/vim#6352) https://github.com/vim/vim/commit/06cf97e714fd8bf9b35ff5f8a6f2302c79acdd03 --- src/nvim/eval/funcs.c | 1 + src/nvim/ex_docmd.c | 1 + src/nvim/highlight_group.c | 1172 +------------------------------------------ src/nvim/match.c | 1181 ++++++++++++++++++++++++++++++++++++++++++++ src/nvim/match.h | 12 + src/nvim/screen.c | 1 + src/nvim/window.c | 2 +- 7 files changed, 1198 insertions(+), 1172 deletions(-) create mode 100644 src/nvim/match.c create mode 100644 src/nvim/match.h (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 69055db9a6..41b419c150 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -39,6 +39,7 @@ #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/math.h" #include "nvim/memline.h" #include "nvim/mouse.h" diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a5732a006d..5ef9f6c05e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -44,6 +44,7 @@ #include "nvim/keymap.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/match.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index ee78a79a97..d448f3a646 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -2,25 +2,18 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com // highlight_group.c: code for managing highlight groups -// Includes highlighting matches #include #include "nvim/autocmd.h" #include "nvim/api/private/helpers.h" #include "nvim/charset.h" #include "nvim/cursor_shape.h" -#include "nvim/eval/funcs.h" -#include "nvim/eval/typval.h" -#include "nvim/ex_docmd.h" #include "nvim/fold.h" -#include "nvim/garray.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/lua/executor.h" -#include "nvim/map.h" -#include "nvim/memline.h" +#include "nvim/match.h" #include "nvim/option.h" -#include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/screen.h" @@ -32,9 +25,6 @@ /// @} #define MAX_SYN_NAME 200 -#define SEARCH_HL_PRIORITY 0 - -static char *e_invalwindow = N_("E957: Invalid window number"); // builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; @@ -2807,1163 +2797,3 @@ int name_to_ctermcolor(const char *name) TriState bold = kNone; return lookup_color(i, false, &bold); } - -/// Add match to the match list of window 'wp'. The pattern 'pat' will be -/// highlighted with the group 'grp' with priority 'prio'. -/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). -/// -/// @param[in] id a desired ID 'id' can be specified -/// (greater than or equal to 1). -1 must be specified if no -/// particular ID is desired -/// @param[in] conceal_char pointer to conceal replacement char -/// @return ID of added match, -1 on failure. -static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, - list_T *pos_list, const char *const conceal_char) - FUNC_ATTR_NONNULL_ARG(1, 2) -{ - matchitem_T *cur; - matchitem_T *prev; - matchitem_T *m; - int hlg_id; - regprog_T *regprog = NULL; - int rtype = SOME_VALID; - - if (*grp == NUL || (pat != NULL && *pat == NUL)) { - return -1; - } - if (id < -1 || id == 0) { - semsg(_("E799: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - return -1; - } - if (id != -1) { - cur = wp->w_match_head; - while (cur != NULL) { - if (cur->id == id) { - semsg(_("E801: ID already taken: %" PRId64), (int64_t)id); - return -1; - } - cur = cur->next; - } - } - if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) { - return -1; - } - if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { - semsg(_(e_invarg2), pat); - return -1; - } - - // Find available match ID. - while (id == -1) { - cur = wp->w_match_head; - while (cur != NULL && cur->id != wp->w_next_match_id) { - cur = cur->next; - } - if (cur == NULL) { - id = wp->w_next_match_id; - } - wp->w_next_match_id++; - } - - // Build new match. - m = xcalloc(1, sizeof(matchitem_T)); - m->id = id; - m->priority = prio; - m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); - m->hlg_id = hlg_id; - m->match.regprog = regprog; - m->match.rmm_ic = false; - m->match.rmm_maxcol = 0; - m->conceal_char = 0; - if (conceal_char != NULL) { - m->conceal_char = utf_ptr2char((const char_u *)conceal_char); - } - - // Set up position matches - if (pos_list != NULL) { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - - int i = 0; - TV_LIST_ITER(pos_list, li, { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - bool error = false; - - if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { - const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; - const listitem_T *subli = tv_list_first(subl); - if (subli == NULL) { - semsg(_("E5030: Empty list at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (lnum <= 0) { - continue; - } - m->pos.pos[i].lnum = lnum; - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (col < 0) { - continue; - } - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - len = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (len < 0) { - continue; - } - if (error) { - goto fail; - } - } - } - m->pos.pos[i].col = col; - m->pos.pos[i].len = len; - } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { - if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { - continue; - } - m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; - m->pos.pos[i].col = 0; - m->pos.pos[i].len = 0; - } else { - semsg(_("E5031: List or number required at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - if (toplnum == 0 || lnum < toplnum) { - toplnum = lnum; - } - if (botlnum == 0 || lnum >= botlnum) { - botlnum = lnum + 1; - } - i++; - if (i >= MAXPOSMATCH) { - break; - } - }); - - // Calculate top and bottom lines for redrawing area - if (toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > toplnum) { - wp->w_buffer->b_mod_top = toplnum; - } - if (wp->w_buffer->b_mod_bot < botlnum) { - wp->w_buffer->b_mod_bot = botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = toplnum; - wp->w_buffer->b_mod_bot = botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - m->pos.toplnum = toplnum; - m->pos.botlnum = botlnum; - rtype = VALID; - } - } - - // Insert new match. The match list is in ascending order with regard to - // the match priorities. - cur = wp->w_match_head; - prev = cur; - while (cur != NULL && prio >= cur->priority) { - prev = cur; - cur = cur->next; - } - if (cur == prev) { - wp->w_match_head = m; - } else { - prev->next = m; - } - m->next = cur; - - redraw_later(wp, rtype); - return id; - -fail: - xfree(m); - return -1; -} - -/// Delete match with ID 'id' in the match list of window 'wp'. -/// -/// @param perr print error messages if true. -static int match_delete(win_T *wp, int id, bool perr) -{ - matchitem_T *cur = wp->w_match_head; - matchitem_T *prev = cur; - int rtype = SOME_VALID; - - if (id < 1) { - if (perr) { - semsg(_("E802: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - } - return -1; - } - while (cur != NULL && cur->id != id) { - prev = cur; - cur = cur->next; - } - if (cur == NULL) { - if (perr) { - semsg(_("E803: ID not found: %" PRId64), (int64_t)id); - } - return -1; - } - if (cur == prev) { - wp->w_match_head = cur->next; - } else { - prev->next = cur->next; - } - vim_regfree(cur->match.regprog); - xfree(cur->pattern); - if (cur->pos.toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { - wp->w_buffer->b_mod_top = cur->pos.toplnum; - } - if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = cur->pos.toplnum; - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - rtype = VALID; - } - xfree(cur); - redraw_later(wp, rtype); - return 0; -} - -/// Delete all matches in the match list of window 'wp'. -void clear_matches(win_T *wp) -{ - matchitem_T *m; - - while (wp->w_match_head != NULL) { - m = wp->w_match_head->next; - vim_regfree(wp->w_match_head->match.regprog); - xfree(wp->w_match_head->pattern); - xfree(wp->w_match_head); - wp->w_match_head = m; - } - redraw_later(wp, SOME_VALID); -} - -/// Get match from ID 'id' in window 'wp'. -/// Return NULL if match not found. -matchitem_T *get_match(win_T *wp, int id) -{ - matchitem_T *cur = wp->w_match_head; - - while (cur != NULL && cur->id != id) { - cur = cur->next; - } - return cur; -} - -/// Init for calling prepare_search_hl(). -void init_search_hl(win_T *wp, match_T *search_hl) - FUNC_ATTR_NONNULL_ALL -{ - // Setup for match and 'hlsearch' highlighting. Disable any previous - // match - matchitem_T *cur = wp->w_match_head; - while (cur != NULL) { - cur->hl.rm = cur->match; - if (cur->hlg_id == 0) { - cur->hl.attr = 0; - } else { - cur->hl.attr = syn_id2attr(cur->hlg_id); - } - cur->hl.buf = wp->w_buffer; - cur->hl.lnum = 0; - cur->hl.first_lnum = 0; - // Set the time limit to 'redrawtime'. - cur->hl.tm = profile_setlimit(p_rdt); - cur = cur->next; - } - search_hl->buf = wp->w_buffer; - search_hl->lnum = 0; - search_hl->first_lnum = 0; - search_hl->attr = win_hl_attr(wp, HLF_L); - - // time limit is set at the toplevel, for all windows -} - -/// @param shl points to a match. Fill on match. -/// @param posmatch match positions -/// @param mincol minimal column for a match -/// -/// @return one on match, otherwise return zero. -static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) - FUNC_ATTR_NONNULL_ALL -{ - int i; - int found = -1; - - shl->lnum = 0; - for (i = posmatch->cur; i < MAXPOSMATCH; i++) { - llpos_T *pos = &posmatch->pos[i]; - - if (pos->lnum == 0) { - break; - } - if (pos->len == 0 && pos->col < mincol) { - continue; - } - if (pos->lnum == lnum) { - if (found >= 0) { - // if this match comes before the one at "found" then swap - // them - if (pos->col < posmatch->pos[found].col) { - llpos_T tmp = *pos; - - *pos = posmatch->pos[found]; - posmatch->pos[found] = tmp; - } - } else { - found = i; - } - } - } - posmatch->cur = 0; - if (found >= 0) { - colnr_T start = posmatch->pos[found].col == 0 - ? 0: posmatch->pos[found].col - 1; - colnr_T end = posmatch->pos[found].col == 0 - ? MAXCOL : start + posmatch->pos[found].len; - - shl->lnum = lnum; - shl->rm.startpos[0].lnum = 0; - shl->rm.startpos[0].col = start; - shl->rm.endpos[0].lnum = 0; - shl->rm.endpos[0].col = end; - shl->is_addpos = true; - posmatch->cur = found + 1; - return 1; - } - return 0; -} - -/// Search for a next 'hlsearch' or match. -/// Uses shl->buf. -/// Sets shl->lnum and shl->rm contents. -/// Note: Assumes a previous match is always before "lnum", unless -/// shl->lnum is zero. -/// Careful: Any pointers for buffer lines will become invalid. -/// -/// @param shl points to search_hl or a match -/// @param mincol minimal column for a match -/// @param cur to retrieve match positions if any -static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_T lnum, - colnr_T mincol, matchitem_T *cur) - FUNC_ATTR_NONNULL_ARG(2) -{ - linenr_T l; - colnr_T matchcol; - long nmatched = 0; - int save_called_emsg = called_emsg; - - // for :{range}s/pat only highlight inside the range - if (lnum < search_first_line || lnum > search_last_line) { - shl->lnum = 0; - return; - } - - if (shl->lnum != 0) { - // Check for three situations: - // 1. If the "lnum" is below a previous match, start a new search. - // 2. If the previous match includes "mincol", use it. - // 3. Continue after the previous match. - l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; - if (lnum > l) { - shl->lnum = 0; - } else if (lnum < l || shl->rm.endpos[0].col > mincol) { - return; - } - } - - // Repeat searching for a match until one is found that includes "mincol" - // or none is found in this line. - called_emsg = false; - for (;;) { - // Stop searching after passing the time limit. - if (profile_passed_limit(shl->tm)) { - shl->lnum = 0; // no match found in time - break; - } - // Three situations: - // 1. No useful previous match: search from start of line. - // 2. Not Vi compatible or empty match: continue at next character. - // Break the loop if this is beyond the end of the line. - // 3. Vi compatible searching: continue at end of previous match. - if (shl->lnum == 0) { - matchcol = 0; - } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL - || (shl->rm.endpos[0].lnum == 0 - && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { - char_u *ml; - - matchcol = shl->rm.startpos[0].col; - ml = ml_get_buf(shl->buf, lnum, false) + matchcol; - if (*ml == NUL) { - matchcol++; - shl->lnum = 0; - break; - } - matchcol += utfc_ptr2len(ml); - } else { - matchcol = shl->rm.endpos[0].col; - } - - shl->lnum = lnum; - if (shl->rm.regprog != NULL) { - // Remember whether shl->rm is using a copy of the regprog in - // cur->match. - bool regprog_is_copy = (shl != search_hl - && cur != NULL - && shl == &cur->hl - && cur->match.regprog == cur->hl.rm.regprog); - int timed_out = false; - - nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, - &(shl->tm), &timed_out); - // Copy the regprog, in case it got freed and recompiled. - if (regprog_is_copy) { - cur->match.regprog = cur->hl.rm.regprog; - } - if (called_emsg || got_int || timed_out) { - // Error while handling regexp: stop using this regexp. - if (shl == search_hl) { - // don't free regprog in the match list, it's a copy - vim_regfree(shl->rm.regprog); - set_no_hlsearch(true); - } - shl->rm.regprog = NULL; - shl->lnum = 0; - got_int = false; // avoid the "Type :quit to exit Vim" message - break; - } - } else if (cur != NULL) { - nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); - } - if (nmatched == 0) { - shl->lnum = 0; // no match found - break; - } - if (shl->rm.startpos[0].lnum > 0 - || shl->rm.startpos[0].col >= mincol - || nmatched > 1 - || shl->rm.endpos[0].col > mincol) { - shl->lnum += shl->rm.startpos[0].lnum; - break; // useful match found - } - - // Restore called_emsg for assert_fails(). - called_emsg = save_called_emsg; - } -} - -/// Advance to the match in window "wp" line "lnum" or past it. -void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) - FUNC_ATTR_NONNULL_ALL -{ - matchitem_T *cur; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag; // flag to indicate whether search_hl - // has been processed or not - - // When using a multi-line pattern, start searching at the top - // of the window or just after a closed fold. - // Do this both for search_hl and the match list. - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || shl_flag == false) { - if (shl_flag == false) { - shl = search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - if (shl->rm.regprog != NULL - && shl->lnum == 0 - && re_multiline(shl->rm.regprog)) { - if (shl->first_lnum == 0) { - for (shl->first_lnum = lnum; - shl->first_lnum > wp->w_topline; - shl->first_lnum--) { - if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { - break; - } - } - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - int n = 0; - while (shl->first_lnum < lnum && (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress))) { - next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, - shl == search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - if (shl->lnum != 0) { - shl->first_lnum = shl->lnum - + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum; - n = shl->rm.endpos[0].col; - } else { - shl->first_lnum++; - n = 0; - } - } - } - if (shl != search_hl && cur != NULL) { - cur = cur->next; - } - } -} - -/// Prepare for 'hlsearch' and match highlighting in one window line. -/// Return true if there is such highlighting and set "search_attr" to the -/// current highlight attribute. -bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, - match_T *search_hl, int *search_attr, bool *search_attr_from_match) -{ - matchitem_T *cur = wp->w_match_head; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag = false; // flag to indicate whether search_hl - // has been processed or not - bool area_highlighting = false; - - // Handle highlighting the last used search pattern and matches. - // Do this for both search_hl and the match list. - while (cur != NULL || !shl_flag) { - if (!shl_flag) { - shl = search_hl; - shl_flag = true; - } else { - shl = &cur->hl; // -V595 - } - shl->startcol = MAXCOL; - shl->endcol = MAXCOL; - shl->attr_cur = 0; - shl->is_addpos = false; - if (cur != NULL) { - cur->pos.cur = 0; - } - next_search_hl(wp, search_hl, shl, lnum, mincol, - shl == search_hl ? NULL : cur); - - // Need to get the line again, a multi-line regexp may have made it - // invalid. - *line = ml_get_buf(wp->w_buffer, lnum, false); - - if (shl->lnum != 0 && shl->lnum <= lnum) { - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - } else { - shl->startcol = 0; - } - if (lnum == shl->lnum + shl->rm.endpos[0].lnum - - shl->rm.startpos[0].lnum) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - // Highlight one character for an empty match. - if (shl->startcol == shl->endcol) { - if ((*line)[shl->endcol] != NUL) { - shl->endcol += utfc_ptr2len(*line + shl->endcol); - } else { - shl->endcol++; - } - } - if ((long)shl->startcol < mincol) { // match at leftcol - shl->attr_cur = shl->attr; - *search_attr = shl->attr; - *search_attr_from_match = shl != search_hl; - } - area_highlighting = true; - } - if (shl != search_hl && cur != NULL) { - cur = cur->next; - } - } - return area_highlighting; -} - -/// For a position in a line: Check for start/end of 'hlsearch' and other -/// matches. -/// After end, check for start/end of next match. -/// When another match, have to check for start again. -/// Watch out for matching an empty string! -/// Return the updated search_attr. -int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl, - int *has_match_conc, int *match_conc, int lcs_eol_one, - bool *search_attr_from_match) -{ - matchitem_T *cur = wp->w_match_head; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag = false; // flag to indicate whether search_hl - // has been processed or not - int search_attr = 0; - - // Do this for 'search_hl' and the match list (ordered by priority). - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (cur != NULL) { - cur->pos.cur = 0; - } - bool pos_inprogress = true; // mark that a position match search is - // in progress - while (shl->rm.regprog != NULL - || (cur != NULL && pos_inprogress)) { - if (shl->startcol != MAXCOL - && col >= shl->startcol - && col < shl->endcol) { - int next_col = col + utfc_ptr2len(*line + col); - - if (shl->endcol < next_col) { - shl->endcol = next_col; - } - shl->attr_cur = shl->attr; - // Match with the "Conceal" group results in hiding - // the match. - if (cur != NULL - && shl != search_hl - && syn_name2id("Conceal") == cur->hlg_id) { - *has_match_conc = col == shl->startcol ? 2 : 1; - *match_conc = cur->conceal_char; - } else { - *has_match_conc = 0; - } - } else if (col == shl->endcol) { - shl->attr_cur = 0; - - next_search_hl(wp, search_hl, shl, lnum, col, - shl == search_hl ? NULL : cur); - pos_inprogress = !(cur == NULL || cur->pos.cur == 0); - - // Need to get the line again, a multi-line regexp - // may have made it invalid. - *line = ml_get_buf(wp->w_buffer, lnum, false); - - if (shl->lnum == lnum) { - shl->startcol = shl->rm.startpos[0].col; - if (shl->rm.endpos[0].lnum == 0) { - shl->endcol = shl->rm.endpos[0].col; - } else { - shl->endcol = MAXCOL; - } - - if (shl->startcol == shl->endcol) { - // highlight empty match, try again after it - shl->endcol += utfc_ptr2len(*line + shl->endcol); - } - - // Loop to check if the match starts at the - // current position - continue; - } - } - break; - } - if (shl != search_hl && cur != NULL) { - cur = cur->next; - } - } - - // Use attributes from match with highest priority among - // 'search_hl' and the match list. - *search_attr_from_match = false; - search_attr = search_hl->attr_cur; - cur = wp->w_match_head; - shl_flag = false; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (shl->attr_cur != 0) { - search_attr = shl->attr_cur; - *search_attr_from_match = shl != search_hl; - } - if (shl != search_hl && cur != NULL) { - cur = cur->next; - } - } - // Only highlight one character after the last column. - if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) { - search_attr = 0; - } - return search_attr; -} - -bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) -{ - long prevcol = curcol; - matchitem_T *cur; // points to the match list - - // we're not really at that column when skipping some text - if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { - prevcol++; - } - - if (!search_hl->is_addpos && prevcol == search_hl->startcol) { - return true; - } else { - cur = wp->w_match_head; - while (cur != NULL) { - if (!cur->hl.is_addpos && prevcol == cur->hl.startcol) { - return true; - } - cur = cur->next; - } - } - return false; -} - -/// Get highlighting for the char after the text in "char_attr" from 'hlsearch' -/// or match highlighting. -void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr) -{ - matchitem_T *cur = wp->w_match_head; // points to the match list - match_T *shl; // points to search_hl or a match - bool shl_flag = false; // flag to indicate whether search_hl - // has been processed or not - - *char_attr = search_hl->attr; - while (cur != NULL || !shl_flag) { - if (!shl_flag - && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { - shl = search_hl; - shl_flag = true; - } else { - shl = &cur->hl; - } - if (col - 1 == (long)shl->startcol - && (shl == search_hl || !shl->is_addpos)) { - *char_attr = shl->attr; - } - if (shl != search_hl && cur != NULL) { - cur = cur->next; - } - } -} - -static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) -{ - dictitem_T *di; - - if (tv->v_type != VAR_DICT) { - emsg(_(e_dictreq)); - return FAIL; - } - - if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) { - *conceal_char = tv_get_string(&di->di_tv); - } - - if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) { - *win = find_win_by_nr_or_id(&di->di_tv); - if (*win == NULL) { - emsg(_(e_invalwindow)); - return FAIL; - } - } - - return OK; -} - -/// "clearmatches()" function -void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *win = get_optional_window(argvars, 0); - - if (win != NULL) { - clear_matches(win); - } -} - -/// "getmatches()" function -void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - matchitem_T *cur; - int i; - win_T *win = get_optional_window(argvars, 0); - - if (win == NULL) { - return; - } - - tv_list_alloc_ret(rettv, kListLenMayKnow); - cur = win->w_match_head; - while (cur != NULL) { - dict_T *dict = tv_dict_alloc(); - if (cur->match.regprog == NULL) { - // match added with matchaddpos() - for (i = 0; i < MAXPOSMATCH; i++) { - llpos_T *llpos; - char buf[30]; // use 30 to avoid compiler warning - - llpos = &cur->pos.pos[i]; - if (llpos->lnum == 0) { - break; - } - list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); - tv_list_append_number(l, (varnumber_T)llpos->lnum); - if (llpos->col > 0) { - tv_list_append_number(l, (varnumber_T)llpos->col); - tv_list_append_number(l, (varnumber_T)llpos->len); - } - int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); - assert((size_t)len < sizeof(buf)); - tv_dict_add_list(dict, buf, (size_t)len, l); - } - } else { - tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); - } - tv_dict_add_str(dict, S_LEN("group"), - (const char *)syn_id2name(cur->hlg_id)); - tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); - tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); - - if (cur->conceal_char) { - char buf[MB_MAXBYTES + 1]; - - buf[utf_char2bytes(cur->conceal_char, (char_u *)buf)] = NUL; - tv_dict_add_str(dict, S_LEN("conceal"), buf); - } - - tv_list_append_dict(rettv->vval.v_list, dict); - cur = cur->next; - } -} - -/// "setmatches()" function -void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - dict_T *d; - list_T *s = NULL; - win_T *win = get_optional_window(argvars, 1); - - rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_listreq)); - return; - } - if (win == NULL) { - return; - } - - list_T *const l = argvars[0].vval.v_list; - // To some extent make sure that we are dealing with a list from - // "getmatches()". - int li_idx = 0; - TV_LIST_ITER_CONST(l, li, { - if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT - || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { - semsg(_("E474: List item %d is either not a dictionary " - "or an empty one"), li_idx); - return; - } - if (!(tv_dict_find(d, S_LEN("group")) != NULL - && (tv_dict_find(d, S_LEN("pattern")) != NULL - || tv_dict_find(d, S_LEN("pos1")) != NULL) - && tv_dict_find(d, S_LEN("priority")) != NULL - && tv_dict_find(d, S_LEN("id")) != NULL)) { - semsg(_("E474: List item %d is missing one of the required keys"), - li_idx); - return; - } - li_idx++; - }); - - clear_matches(win); - bool match_add_failed = false; - TV_LIST_ITER_CONST(l, li, { - int i = 0; - - d = TV_LIST_ITEM_TV(li)->vval.v_dict; - dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); - if (di == NULL) { - if (s == NULL) { - s = tv_list_alloc(9); - } - - // match from matchaddpos() - for (i = 1; i < 9; i++) { - char buf[30]; // use 30 to avoid compiler warning - snprintf(buf, sizeof(buf), "pos%d", i); - dictitem_T *const pos_di = tv_dict_find(d, buf, -1); - if (pos_di != NULL) { - if (pos_di->di_tv.v_type != VAR_LIST) { - return; - } - - tv_list_append_tv(s, &pos_di->di_tv); - tv_list_ref(s); - } else { - break; - } - } - } - - // Note: there are three number buffers involved: - // - group_buf below. - // - numbuf in tv_dict_get_string(). - // - mybuf in tv_get_string(). - // - // If you change this code make sure that buffers will not get - // accidentally reused. - char group_buf[NUMBUFLEN]; - const char *const group = tv_dict_get_string_buf(d, "group", group_buf); - const int priority = (int)tv_dict_get_number(d, "priority"); - const int id = (int)tv_dict_get_number(d, "id"); - dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); - const char *const conceal = (conceal_di != NULL - ? tv_get_string(&conceal_di->di_tv) - : NULL); - if (i == 0) { - if (match_add(win, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal) != id) { - match_add_failed = true; - } - } else { - if (match_add(win, group, NULL, priority, id, s, conceal) != id) { - match_add_failed = true; - } - tv_list_unref(s); - s = NULL; - } - }); - if (!match_add_failed) { - rettv->vval.v_number = 0; - } -} - - -/// "matchadd()" function -void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - char grpbuf[NUMBUFLEN]; - char patbuf[NUMBUFLEN]; - // group - const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); - // pattern - const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); - // default priority - int prio = 10; - int id = -1; - bool error = false; - const char *conceal_char = NULL; - win_T *win = curwin; - - rettv->vval.v_number = -1; - - if (grp == NULL || pat == NULL) { - return; - } - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = (int)tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = (int)tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { - return; - } - } - } - if (error) { - return; - } - if (id >= 1 && id <= 3) { - semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id); - return; - } - - rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char); -} - -/// "matchaddpo()" function -void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->vval.v_number = -1; - - char buf[NUMBUFLEN]; - const char *const group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) { - return; - } - - if (argvars[1].v_type != VAR_LIST) { - semsg(_(e_listarg), "matchaddpos()"); - return; - } - - list_T *l; - l = argvars[1].vval.v_list; - if (l == NULL) { - return; - } - - bool error = false; - int prio = 10; - int id = -1; - const char *conceal_char = NULL; - win_T *win = curwin; - - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = (int)tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = (int)tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN - && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { - return; - } - } - } - if (error == true) { - return; - } - - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) { - semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id); - return; - } - - rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); -} - -/// "matcharg()" function -void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - const int id = (int)tv_get_number(&argvars[0]); - - tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 - ? 2 - : 0)); - - if (id >= 1 && id <= 3) { - matchitem_T *const m = get_match(curwin, id); - - if (m != NULL) { - tv_list_append_string(rettv->vval.v_list, - (const char *)syn_id2name(m->hlg_id), -1); - tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); - } else { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } - } -} - -/// "matchdelete()" function -void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - win_T *win = get_optional_window(argvars, 1); - if (win == NULL) { - rettv->vval.v_number = -1; - } else { - rettv->vval.v_number = match_delete(win, - (int)tv_get_number(&argvars[0]), true); - } -} - -/// ":[N]match {group} {pattern}" -/// Sets nextcmd to the start of the next command, if any. Also called when -/// skipping commands to find the next command. -void ex_match(exarg_T *eap) -{ - char_u *p; - char_u *g = NULL; - char_u *end; - int c; - int id; - - if (eap->line2 <= 3) { - id = (int)eap->line2; - } else { - emsg(e_invcmd); - return; - } - - // First clear any old pattern. - if (!eap->skip) { - match_delete(curwin, id, false); - } - - if (ends_excmd(*eap->arg)) { - end = eap->arg; - } else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { - end = eap->arg + 4; - } else { - p = skiptowhite(eap->arg); - if (!eap->skip) { - g = vim_strnsave(eap->arg, (size_t)(p - eap->arg)); - } - p = skipwhite(p); - if (*p == NUL) { - // There must be two arguments. - xfree(g); - semsg(_(e_invarg2), eap->arg); - return; - } - end = skip_regexp(p + 1, *p, true, NULL); - if (!eap->skip) { - if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { - xfree(g); - eap->errmsg = e_trailing; - return; - } - if (*end != *p) { - xfree(g); - semsg(_(e_invarg2), p); - return; - } - - c = *end; - *end = NUL; - match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, - NULL, NULL); - xfree(g); - *end = (char_u)c; - } - } - eap->nextcmd = find_nextcmd(end); -} - diff --git a/src/nvim/match.c b/src/nvim/match.c new file mode 100644 index 0000000000..af89319a09 --- /dev/null +++ b/src/nvim/match.c @@ -0,0 +1,1181 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// match.c: functions for highlighting matches + +#include +#include "nvim/charset.h" +#include "nvim/fold.h" +#include "nvim/highlight_group.h" +#include "nvim/match.h" +#include "nvim/memline.h" +#include "nvim/regexp.h" +#include "nvim/runtime.h" +#include "nvim/screen.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "match.c.generated.h" +#endif + +static char *e_invalwindow = N_("E957: Invalid window number"); + +#define SEARCH_HL_PRIORITY 0 + +/// Add match to the match list of window 'wp'. The pattern 'pat' will be +/// highlighted with the group 'grp' with priority 'prio'. +/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). +/// +/// @param[in] id a desired ID 'id' can be specified +/// (greater than or equal to 1). -1 must be specified if no +/// particular ID is desired +/// @param[in] conceal_char pointer to conceal replacement char +/// @return ID of added match, -1 on failure. +static int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, + list_T *pos_list, const char *const conceal_char) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + matchitem_T *cur; + matchitem_T *prev; + matchitem_T *m; + int hlg_id; + regprog_T *regprog = NULL; + int rtype = SOME_VALID; + + if (*grp == NUL || (pat != NULL && *pat == NUL)) { + return -1; + } + if (id < -1 || id == 0) { + semsg(_("E799: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), + (int64_t)id); + return -1; + } + if (id != -1) { + cur = wp->w_match_head; + while (cur != NULL) { + if (cur->id == id) { + semsg(_("E801: ID already taken: %" PRId64), (int64_t)id); + return -1; + } + cur = cur->next; + } + } + if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) { + return -1; + } + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { + semsg(_(e_invarg2), pat); + return -1; + } + + // Find available match ID. + while (id == -1) { + cur = wp->w_match_head; + while (cur != NULL && cur->id != wp->w_next_match_id) { + cur = cur->next; + } + if (cur == NULL) { + id = wp->w_next_match_id; + } + wp->w_next_match_id++; + } + + // Build new match. + m = xcalloc(1, sizeof(matchitem_T)); + m->id = id; + m->priority = prio; + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); + m->hlg_id = hlg_id; + m->match.regprog = regprog; + m->match.rmm_ic = false; + m->match.rmm_maxcol = 0; + m->conceal_char = 0; + if (conceal_char != NULL) { + m->conceal_char = utf_ptr2char((const char_u *)conceal_char); + } + + // Set up position matches + if (pos_list != NULL) { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + + int i = 0; + TV_LIST_ITER(pos_list, li, { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; + bool error = false; + + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { + const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; + const listitem_T *subli = tv_list_first(subl); + if (subli == NULL) { + semsg(_("E5030: Empty list at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); + goto fail; + } + lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { + goto fail; + } + if (lnum <= 0) { + continue; + } + m->pos.pos[i].lnum = lnum; + subli = TV_LIST_ITEM_NEXT(subl, subli); + if (subli != NULL) { + col = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { + goto fail; + } + if (col < 0) { + continue; + } + subli = TV_LIST_ITEM_NEXT(subl, subli); + if (subli != NULL) { + len = (colnr_T)tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (len < 0) { + continue; + } + if (error) { + goto fail; + } + } + } + m->pos.pos[i].col = col; + m->pos.pos[i].len = len; + } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { + if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { + continue; + } + m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; + m->pos.pos[i].col = 0; + m->pos.pos[i].len = 0; + } else { + semsg(_("E5031: List or number required at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); + goto fail; + } + if (toplnum == 0 || lnum < toplnum) { + toplnum = lnum; + } + if (botlnum == 0 || lnum >= botlnum) { + botlnum = lnum + 1; + } + i++; + if (i >= MAXPOSMATCH) { + break; + } + }); + + // Calculate top and bottom lines for redrawing area + if (toplnum != 0) { + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > toplnum) { + wp->w_buffer->b_mod_top = toplnum; + } + if (wp->w_buffer->b_mod_bot < botlnum) { + wp->w_buffer->b_mod_bot = botlnum; + } + } else { + wp->w_buffer->b_mod_set = true; + wp->w_buffer->b_mod_top = toplnum; + wp->w_buffer->b_mod_bot = botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + m->pos.toplnum = toplnum; + m->pos.botlnum = botlnum; + rtype = VALID; + } + } + + // Insert new match. The match list is in ascending order with regard to + // the match priorities. + cur = wp->w_match_head; + prev = cur; + while (cur != NULL && prio >= cur->priority) { + prev = cur; + cur = cur->next; + } + if (cur == prev) { + wp->w_match_head = m; + } else { + prev->next = m; + } + m->next = cur; + + redraw_later(wp, rtype); + return id; + +fail: + xfree(m); + return -1; +} + +/// Delete match with ID 'id' in the match list of window 'wp'. +/// +/// @param perr print error messages if true. +static int match_delete(win_T *wp, int id, bool perr) +{ + matchitem_T *cur = wp->w_match_head; + matchitem_T *prev = cur; + int rtype = SOME_VALID; + + if (id < 1) { + if (perr) { + semsg(_("E802: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), + (int64_t)id); + } + return -1; + } + while (cur != NULL && cur->id != id) { + prev = cur; + cur = cur->next; + } + if (cur == NULL) { + if (perr) { + semsg(_("E803: ID not found: %" PRId64), (int64_t)id); + } + return -1; + } + if (cur == prev) { + wp->w_match_head = cur->next; + } else { + prev->next = cur->next; + } + vim_regfree(cur->match.regprog); + xfree(cur->pattern); + if (cur->pos.toplnum != 0) { + if (wp->w_buffer->b_mod_set) { + if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { + wp->w_buffer->b_mod_top = cur->pos.toplnum; + } + if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + } + } else { + wp->w_buffer->b_mod_set = true; + wp->w_buffer->b_mod_top = cur->pos.toplnum; + wp->w_buffer->b_mod_bot = cur->pos.botlnum; + wp->w_buffer->b_mod_xlines = 0; + } + rtype = VALID; + } + xfree(cur); + redraw_later(wp, rtype); + return 0; +} + +/// Delete all matches in the match list of window 'wp'. +void clear_matches(win_T *wp) +{ + matchitem_T *m; + + while (wp->w_match_head != NULL) { + m = wp->w_match_head->next; + vim_regfree(wp->w_match_head->match.regprog); + xfree(wp->w_match_head->pattern); + xfree(wp->w_match_head); + wp->w_match_head = m; + } + redraw_later(wp, SOME_VALID); +} + +/// Get match from ID 'id' in window 'wp'. +/// Return NULL if match not found. +matchitem_T *get_match(win_T *wp, int id) +{ + matchitem_T *cur = wp->w_match_head; + + while (cur != NULL && cur->id != id) { + cur = cur->next; + } + return cur; +} + +/// Init for calling prepare_search_hl(). +void init_search_hl(win_T *wp, match_T *search_hl) + FUNC_ATTR_NONNULL_ALL +{ + // Setup for match and 'hlsearch' highlighting. Disable any previous + // match + matchitem_T *cur = wp->w_match_head; + while (cur != NULL) { + cur->hl.rm = cur->match; + if (cur->hlg_id == 0) { + cur->hl.attr = 0; + } else { + cur->hl.attr = syn_id2attr(cur->hlg_id); + } + cur->hl.buf = wp->w_buffer; + cur->hl.lnum = 0; + cur->hl.first_lnum = 0; + // Set the time limit to 'redrawtime'. + cur->hl.tm = profile_setlimit(p_rdt); + cur = cur->next; + } + search_hl->buf = wp->w_buffer; + search_hl->lnum = 0; + search_hl->first_lnum = 0; + search_hl->attr = win_hl_attr(wp, HLF_L); + + // time limit is set at the toplevel, for all windows +} + +/// @param shl points to a match. Fill on match. +/// @param posmatch match positions +/// @param mincol minimal column for a match +/// +/// @return one on match, otherwise return zero. +static int next_search_hl_pos(match_T *shl, linenr_T lnum, posmatch_T *posmatch, colnr_T mincol) + FUNC_ATTR_NONNULL_ALL +{ + int i; + int found = -1; + + shl->lnum = 0; + for (i = posmatch->cur; i < MAXPOSMATCH; i++) { + llpos_T *pos = &posmatch->pos[i]; + + if (pos->lnum == 0) { + break; + } + if (pos->len == 0 && pos->col < mincol) { + continue; + } + if (pos->lnum == lnum) { + if (found >= 0) { + // if this match comes before the one at "found" then swap + // them + if (pos->col < posmatch->pos[found].col) { + llpos_T tmp = *pos; + + *pos = posmatch->pos[found]; + posmatch->pos[found] = tmp; + } + } else { + found = i; + } + } + } + posmatch->cur = 0; + if (found >= 0) { + colnr_T start = posmatch->pos[found].col == 0 + ? 0: posmatch->pos[found].col - 1; + colnr_T end = posmatch->pos[found].col == 0 + ? MAXCOL : start + posmatch->pos[found].len; + + shl->lnum = lnum; + shl->rm.startpos[0].lnum = 0; + shl->rm.startpos[0].col = start; + shl->rm.endpos[0].lnum = 0; + shl->rm.endpos[0].col = end; + shl->is_addpos = true; + posmatch->cur = found + 1; + return 1; + } + return 0; +} + +/// Search for a next 'hlsearch' or match. +/// Uses shl->buf. +/// Sets shl->lnum and shl->rm contents. +/// Note: Assumes a previous match is always before "lnum", unless +/// shl->lnum is zero. +/// Careful: Any pointers for buffer lines will become invalid. +/// +/// @param shl points to search_hl or a match +/// @param mincol minimal column for a match +/// @param cur to retrieve match positions if any +static void next_search_hl(win_T *win, match_T *search_hl, match_T *shl, linenr_T lnum, + colnr_T mincol, matchitem_T *cur) + FUNC_ATTR_NONNULL_ARG(2) +{ + linenr_T l; + colnr_T matchcol; + long nmatched = 0; + int save_called_emsg = called_emsg; + + // for :{range}s/pat only highlight inside the range + if (lnum < search_first_line || lnum > search_last_line) { + shl->lnum = 0; + return; + } + + if (shl->lnum != 0) { + // Check for three situations: + // 1. If the "lnum" is below a previous match, start a new search. + // 2. If the previous match includes "mincol", use it. + // 3. Continue after the previous match. + l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum; + if (lnum > l) { + shl->lnum = 0; + } else if (lnum < l || shl->rm.endpos[0].col > mincol) { + return; + } + } + + // Repeat searching for a match until one is found that includes "mincol" + // or none is found in this line. + called_emsg = false; + for (;;) { + // Stop searching after passing the time limit. + if (profile_passed_limit(shl->tm)) { + shl->lnum = 0; // no match found in time + break; + } + // Three situations: + // 1. No useful previous match: search from start of line. + // 2. Not Vi compatible or empty match: continue at next character. + // Break the loop if this is beyond the end of the line. + // 3. Vi compatible searching: continue at end of previous match. + if (shl->lnum == 0) { + matchcol = 0; + } else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL + || (shl->rm.endpos[0].lnum == 0 + && shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) { + char_u *ml; + + matchcol = shl->rm.startpos[0].col; + ml = ml_get_buf(shl->buf, lnum, false) + matchcol; + if (*ml == NUL) { + matchcol++; + shl->lnum = 0; + break; + } + matchcol += utfc_ptr2len(ml); + } else { + matchcol = shl->rm.endpos[0].col; + } + + shl->lnum = lnum; + if (shl->rm.regprog != NULL) { + // Remember whether shl->rm is using a copy of the regprog in + // cur->match. + bool regprog_is_copy = (shl != search_hl + && cur != NULL + && shl == &cur->hl + && cur->match.regprog == cur->hl.rm.regprog); + int timed_out = false; + + nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, + &(shl->tm), &timed_out); + // Copy the regprog, in case it got freed and recompiled. + if (regprog_is_copy) { + cur->match.regprog = cur->hl.rm.regprog; + } + if (called_emsg || got_int || timed_out) { + // Error while handling regexp: stop using this regexp. + if (shl == search_hl) { + // don't free regprog in the match list, it's a copy + vim_regfree(shl->rm.regprog); + set_no_hlsearch(true); + } + shl->rm.regprog = NULL; + shl->lnum = 0; + got_int = false; // avoid the "Type :quit to exit Vim" message + break; + } + } else if (cur != NULL) { + nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol); + } + if (nmatched == 0) { + shl->lnum = 0; // no match found + break; + } + if (shl->rm.startpos[0].lnum > 0 + || shl->rm.startpos[0].col >= mincol + || nmatched > 1 + || shl->rm.endpos[0].col > mincol) { + shl->lnum += shl->rm.startpos[0].lnum; + break; // useful match found + } + + // Restore called_emsg for assert_fails(). + called_emsg = save_called_emsg; + } +} + +/// Advance to the match in window "wp" line "lnum" or past it. +void prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum) + FUNC_ATTR_NONNULL_ALL +{ + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag; // flag to indicate whether search_hl + // has been processed or not + + // When using a multi-line pattern, start searching at the top + // of the window or just after a closed fold. + // Do this both for search_hl and the match list. + cur = wp->w_match_head; + shl_flag = false; + while (cur != NULL || shl_flag == false) { + if (shl_flag == false) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; // -V595 + } + if (shl->rm.regprog != NULL + && shl->lnum == 0 + && re_multiline(shl->rm.regprog)) { + if (shl->first_lnum == 0) { + for (shl->first_lnum = lnum; + shl->first_lnum > wp->w_topline; + shl->first_lnum--) { + if (hasFoldingWin(wp, shl->first_lnum - 1, NULL, NULL, true, NULL)) { + break; + } + } + } + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress + int n = 0; + while (shl->first_lnum < lnum && (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress))) { + next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n, + shl == search_hl ? NULL : cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); + if (shl->lnum != 0) { + shl->first_lnum = shl->lnum + + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum; + n = shl->rm.endpos[0].col; + } else { + shl->first_lnum++; + n = 0; + } + } + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } +} + +/// Prepare for 'hlsearch' and match highlighting in one window line. +/// Return true if there is such highlighting and set "search_attr" to the +/// current highlight attribute. +bool prepare_search_hl_line(win_T *wp, linenr_T lnum, colnr_T mincol, char_u **line, + match_T *search_hl, int *search_attr, bool *search_attr_from_match) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + bool area_highlighting = false; + + // Handle highlighting the last used search pattern and matches. + // Do this for both search_hl and the match list. + while (cur != NULL || !shl_flag) { + if (!shl_flag) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; // -V595 + } + shl->startcol = MAXCOL; + shl->endcol = MAXCOL; + shl->attr_cur = 0; + shl->is_addpos = false; + if (cur != NULL) { + cur->pos.cur = 0; + } + next_search_hl(wp, search_hl, shl, lnum, mincol, + shl == search_hl ? NULL : cur); + + // Need to get the line again, a multi-line regexp may have made it + // invalid. + *line = ml_get_buf(wp->w_buffer, lnum, false); + + if (shl->lnum != 0 && shl->lnum <= lnum) { + if (shl->lnum == lnum) { + shl->startcol = shl->rm.startpos[0].col; + } else { + shl->startcol = 0; + } + if (lnum == shl->lnum + shl->rm.endpos[0].lnum + - shl->rm.startpos[0].lnum) { + shl->endcol = shl->rm.endpos[0].col; + } else { + shl->endcol = MAXCOL; + } + // Highlight one character for an empty match. + if (shl->startcol == shl->endcol) { + if ((*line)[shl->endcol] != NUL) { + shl->endcol += utfc_ptr2len(*line + shl->endcol); + } else { + shl->endcol++; + } + } + if ((long)shl->startcol < mincol) { // match at leftcol + shl->attr_cur = shl->attr; + *search_attr = shl->attr; + *search_attr_from_match = shl != search_hl; + } + area_highlighting = true; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + return area_highlighting; +} + +/// For a position in a line: Check for start/end of 'hlsearch' and other +/// matches. +/// After end, check for start/end of next match. +/// When another match, have to check for start again. +/// Watch out for matching an empty string! +/// Return the updated search_attr. +int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match_T *search_hl, + int *has_match_conc, int *match_conc, int lcs_eol_one, + bool *search_attr_from_match) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + int search_attr = 0; + + // Do this for 'search_hl' and the match list (ordered by priority). + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (cur != NULL) { + cur->pos.cur = 0; + } + bool pos_inprogress = true; // mark that a position match search is + // in progress + while (shl->rm.regprog != NULL + || (cur != NULL && pos_inprogress)) { + if (shl->startcol != MAXCOL + && col >= shl->startcol + && col < shl->endcol) { + int next_col = col + utfc_ptr2len(*line + col); + + if (shl->endcol < next_col) { + shl->endcol = next_col; + } + shl->attr_cur = shl->attr; + // Match with the "Conceal" group results in hiding + // the match. + if (cur != NULL + && shl != search_hl + && syn_name2id("Conceal") == cur->hlg_id) { + *has_match_conc = col == shl->startcol ? 2 : 1; + *match_conc = cur->conceal_char; + } else { + *has_match_conc = 0; + } + } else if (col == shl->endcol) { + shl->attr_cur = 0; + + next_search_hl(wp, search_hl, shl, lnum, col, + shl == search_hl ? NULL : cur); + pos_inprogress = !(cur == NULL || cur->pos.cur == 0); + + // Need to get the line again, a multi-line regexp + // may have made it invalid. + *line = ml_get_buf(wp->w_buffer, lnum, false); + + if (shl->lnum == lnum) { + shl->startcol = shl->rm.startpos[0].col; + if (shl->rm.endpos[0].lnum == 0) { + shl->endcol = shl->rm.endpos[0].col; + } else { + shl->endcol = MAXCOL; + } + + if (shl->startcol == shl->endcol) { + // highlight empty match, try again after it + shl->endcol += utfc_ptr2len(*line + shl->endcol); + } + + // Loop to check if the match starts at the + // current position + continue; + } + } + break; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + + // Use attributes from match with highest priority among + // 'search_hl' and the match list. + *search_attr_from_match = false; + search_attr = search_hl->attr_cur; + cur = wp->w_match_head; + shl_flag = false; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (shl->attr_cur != 0) { + search_attr = shl->attr_cur; + *search_attr_from_match = shl != search_hl; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } + // Only highlight one character after the last column. + if (*(*line + col) == NUL && (wp->w_p_list && lcs_eol_one == -1)) { + search_attr = 0; + } + return search_attr; +} + +bool get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol) +{ + long prevcol = curcol; + matchitem_T *cur; // points to the match list + + // we're not really at that column when skipping some text + if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol) { + prevcol++; + } + + if (!search_hl->is_addpos && prevcol == search_hl->startcol) { + return true; + } else { + cur = wp->w_match_head; + while (cur != NULL) { + if (!cur->hl.is_addpos && prevcol == cur->hl.startcol) { + return true; + } + cur = cur->next; + } + } + return false; +} + +/// Get highlighting for the char after the text in "char_attr" from 'hlsearch' +/// or match highlighting. +void get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr) +{ + matchitem_T *cur = wp->w_match_head; // points to the match list + match_T *shl; // points to search_hl or a match + bool shl_flag = false; // flag to indicate whether search_hl + // has been processed or not + + *char_attr = search_hl->attr; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { + shl = search_hl; + shl_flag = true; + } else { + shl = &cur->hl; + } + if (col - 1 == (long)shl->startcol + && (shl == search_hl || !shl->is_addpos)) { + *char_attr = shl->attr; + } + if (shl != search_hl && cur != NULL) { + cur = cur->next; + } + } +} + +static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **win) +{ + dictitem_T *di; + + if (tv->v_type != VAR_DICT) { + emsg(_(e_dictreq)); + return FAIL; + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) { + *conceal_char = tv_get_string(&di->di_tv); + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) { + *win = find_win_by_nr_or_id(&di->di_tv); + if (*win == NULL) { + emsg(_(e_invalwindow)); + return FAIL; + } + } + + return OK; +} + +/// "clearmatches()" function +void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *win = get_optional_window(argvars, 0); + + if (win != NULL) { + clear_matches(win); + } +} + +/// "getmatches()" function +void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + matchitem_T *cur; + int i; + win_T *win = get_optional_window(argvars, 0); + + if (win == NULL) { + return; + } + + tv_list_alloc_ret(rettv, kListLenMayKnow); + cur = win->w_match_head; + while (cur != NULL) { + dict_T *dict = tv_dict_alloc(); + if (cur->match.regprog == NULL) { + // match added with matchaddpos() + for (i = 0; i < MAXPOSMATCH; i++) { + llpos_T *llpos; + char buf[30]; // use 30 to avoid compiler warning + + llpos = &cur->pos.pos[i]; + if (llpos->lnum == 0) { + break; + } + list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); + tv_list_append_number(l, (varnumber_T)llpos->lnum); + if (llpos->col > 0) { + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); + } + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); + } + } else { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); + } + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); + + if (cur->conceal_char) { + char buf[MB_MAXBYTES + 1]; + + buf[utf_char2bytes(cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); + } + + tv_list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } +} + +/// "setmatches()" function +void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *d; + list_T *s = NULL; + win_T *win = get_optional_window(argvars, 1); + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_listreq)); + return; + } + if (win == NULL) { + return; + } + + list_T *const l = argvars[0].vval.v_list; + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int li_idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + semsg(_("E474: List item %d is either not a dictionary " + "or an empty one"), li_idx); + return; + } + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { + semsg(_("E474: List item %d is missing one of the required keys"), + li_idx); + return; + } + li_idx++; + }); + + clear_matches(win); + bool match_add_failed = false; + TV_LIST_ITER_CONST(l, li, { + int i = 0; + + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { + if (s == NULL) { + s = tv_list_alloc(9); + } + + // match from matchaddpos() + for (i = 1; i < 9; i++) { + char buf[30]; // use 30 to avoid compiler warning + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { + return; + } + + tv_list_append_tv(s, &pos_di->di_tv); + tv_list_ref(s); + } else { + break; + } + } + } + + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); + if (i == 0) { + if (match_add(win, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } + } else { + if (match_add(win, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); + s = NULL; + } + }); + if (!match_add_failed) { + rettv->vval.v_number = 0; + } +} + +/// "matchadd()" function +void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + // group + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + // pattern + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + // default priority + int prio = 10; + int id = -1; + bool error = false; + const char *conceal_char = NULL; + win_T *win = curwin; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; + } + } + } + if (error) { + return; + } + if (id >= 1 && id <= 3) { + semsg(_("E798: ID is reserved for \":match\": %" PRId64), (int64_t)id); + return; + } + + rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char); +} + +/// "matchaddpo()" function +void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->vval.v_number = -1; + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } + + if (argvars[1].v_type != VAR_LIST) { + semsg(_(e_listarg), "matchaddpos()"); + return; + } + + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } + + bool error = false; + int prio = 10; + int id = -1; + const char *conceal_char = NULL; + win_T *win = curwin; + + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = (int)tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = (int)tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; + } + } + } + if (error == true) { + return; + } + + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + semsg(_("E798: ID is reserved for \"match\": %" PRId64), (int64_t)id); + return; + } + + rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); +} + +/// "matcharg()" function +void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const int id = (int)tv_get_number(&argvars[0]); + + tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 + ? 2 + : 0)); + + if (id >= 1 && id <= 3) { + matchitem_T *const m = get_match(curwin, id); + + if (m != NULL) { + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); + } else { + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + } + } +} + +/// "matchdelete()" function +void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *win = get_optional_window(argvars, 1); + if (win == NULL) { + rettv->vval.v_number = -1; + } else { + rettv->vval.v_number = match_delete(win, + (int)tv_get_number(&argvars[0]), true); + } +} + +/// ":[N]match {group} {pattern}" +/// Sets nextcmd to the start of the next command, if any. Also called when +/// skipping commands to find the next command. +void ex_match(exarg_T *eap) +{ + char_u *p; + char_u *g = NULL; + char_u *end; + int c; + int id; + + if (eap->line2 <= 3) { + id = (int)eap->line2; + } else { + emsg(e_invcmd); + return; + } + + // First clear any old pattern. + if (!eap->skip) { + match_delete(curwin, id, false); + } + + if (ends_excmd(*eap->arg)) { + end = eap->arg; + } else if ((STRNICMP(eap->arg, "none", 4) == 0 + && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { + end = eap->arg + 4; + } else { + p = skiptowhite(eap->arg); + if (!eap->skip) { + g = vim_strnsave(eap->arg, (size_t)(p - eap->arg)); + } + p = skipwhite(p); + if (*p == NUL) { + // There must be two arguments. + xfree(g); + semsg(_(e_invarg2), eap->arg); + return; + } + end = skip_regexp(p + 1, *p, true, NULL); + if (!eap->skip) { + if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { + xfree(g); + eap->errmsg = e_trailing; + return; + } + if (*end != *p) { + xfree(g); + semsg(_(e_invarg2), p); + return; + } + + c = *end; + *end = NUL; + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); + xfree(g); + *end = (char_u)c; + } + } + eap->nextcmd = find_nextcmd(end); +} + diff --git a/src/nvim/match.h b/src/nvim/match.h new file mode 100644 index 0000000000..fdcec0ae05 --- /dev/null +++ b/src/nvim/match.h @@ -0,0 +1,12 @@ +#ifndef NVIM_MATCH_H +#define NVIM_MATCH_H + +#include "nvim/buffer_defs.h" +#include "nvim/eval/funcs.h" +#include "nvim/ex_cmds_defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "match.h.generated.h" +#endif + +#endif // NVIM_MATCH_H diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 83d555e584..96bc5180e2 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -95,6 +95,7 @@ #include "nvim/lua/executor.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/window.c b/src/nvim/window.c index 50a56056bf..e0d657af45 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -25,9 +25,9 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hashtab.h" -#include "nvim/highlight_group.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" -- cgit From 159111f9a59d9e366fe975bf78c223228ce9ca8a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 06:34:45 +0800 Subject: refactor(ui_refresh): only save/restore p_lz if calling screen_resize() (#17794) --- src/nvim/ui.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/ui.c b/src/nvim/ui.c index da50f068b7..4fe3e1157c 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -223,10 +223,11 @@ void ui_refresh(void) ui_default_colors_set(); - int save_p_lz = p_lz; - p_lz = false; // convince redrawing() to return true ... if (!ui_client_channel_id) { + int save_p_lz = p_lz; + p_lz = false; // convince redrawing() to return true ... screen_resize(width, height); + p_lz = save_p_lz; } else { Array args = ARRAY_DICT_INIT; Error err = ERROR_INIT; @@ -240,8 +241,6 @@ void ui_refresh(void) api_clear_error(&err); } - p_lz = save_p_lz; - if (ext_widgets[kUIMessages]) { p_ch = 0; command_height(); -- cgit From 7735163652cd9082ef89cb31707ca77df12f7b70 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 07:07:34 +0800 Subject: fix(screen): do not do syntax highlighting at filler or folded lines (#17818) --- src/nvim/screen.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 96bc5180e2..7182e69d08 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2160,7 +2160,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // To speed up the loop below, set extra_check when there is linebreak, // trailing white space and/or syntax processing to be done. extra_check = wp->w_p_lbr; - if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow) { + if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow + && !has_fold && !end_fill) { // Prepare for syntax highlighting in this line. When there is an // error, stop syntax highlighting. save_did_emsg = did_emsg; -- cgit From 89712dcbf8ecc41d6fab9608f684ce199667ed2e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Mar 2022 21:19:09 +0800 Subject: fix(aucmd_win): always make aucmd_win the last window --- src/nvim/api/win_config.c | 4 ++-- src/nvim/window.c | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c index 42e880dc19..5e37596884 100644 --- a/src/nvim/api/win_config.c +++ b/src/nvim/api/win_config.c @@ -150,7 +150,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E if (!parse_float_config(config, &fconfig, false, true, err)) { return 0; } - win_T *wp = win_new_float(NULL, fconfig, err); + win_T *wp = win_new_float(NULL, false, fconfig, err); if (!wp) { return 0; } @@ -200,7 +200,7 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) return; } if (new_float) { - if (!win_new_float(win, fconfig, err)) { + if (!win_new_float(win, false, fconfig, err)) { return; } redraw_later(win, NOT_VALID); diff --git a/src/nvim/window.c b/src/nvim/window.c index e0d657af45..97664d0870 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -560,7 +560,7 @@ wingotofile: config.height = curwin->w_height; config.external = true; Error err = ERROR_INIT; - if (!win_new_float(curwin, config, &err)) { + if (!win_new_float(curwin, false, config, &err)) { emsg(err.msg); api_clear_error(&err); beep_flush(); @@ -629,16 +629,18 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) /// Create a new float. /// -/// if wp == NULL allocate a new window, otherwise turn existing window into a -/// float. It must then already belong to the current tabpage! -/// -/// config must already have been validated! -win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) +/// @param wp if NULL, allocate a new window, otherwise turn existing window into a float. +/// It must then already belong to the current tabpage! +/// @param last make the window the last one in the window list. +/// Only used when allocating the autocommand window. +/// @param config must already have been validated! +win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err) { if (wp == NULL) { - wp = win_alloc(lastwin_nofloating(), false); + wp = win_alloc(last ? lastwin : lastwin_nofloating(), false); win_init(wp, curwin, 0); } else { + assert(!last); assert(!wp->w_floating); if (firstwin == wp && lastwin_nofloating() == wp) { // last non-float @@ -2543,7 +2545,7 @@ int win_close(win_T *win, bool free_buf, bool force) emsg(_(e_autocmd_close)); return FAIL; } - if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) { + if (lastwin == aucmd_win && one_window()) { emsg(_("E814: Cannot close window, only autocmd window would remain")); return FAIL; } @@ -3844,7 +3846,7 @@ void win_alloc_aucmd_win(void) fconfig.width = Columns; fconfig.height = 5; fconfig.focusable = false; - aucmd_win = win_new_float(NULL, fconfig, &err); + aucmd_win = win_new_float(NULL, true, fconfig, &err); aucmd_win->w_buffer->b_nwindows--; RESET_BINDING(aucmd_win); } -- cgit From 3539456f4923b4afbc7fa160aabf19c07108758f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Mar 2022 21:54:44 +0800 Subject: fix(win_close): count the window the be closed instead of curwin --- src/nvim/window.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 97664d0870..73c4acf45b 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -300,7 +300,7 @@ newwindow: // move window to new tab page case 'T': - if (one_window()) { + if (one_window(curwin)) { msg(_(m_onlyone)); } else { tabpage_T *oldtab = curtab; @@ -2401,23 +2401,24 @@ void close_windows(buf_T *buf, int keep_curwin) } } -/// Check that current window is the last one. +/// Check that the specified window is the last one. +/// @param win counted even if floating /// -/// @return true if the current window is the only window that exists, false if -/// there is another, possibly in another tab page. -static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// @return true if the specified window is the only window that exists, +/// false if there is another, possibly in another tab page. +static bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return one_window() && first_tabpage->tp_next == NULL; + return one_window(win) && first_tabpage->tp_next == NULL; } -/// Check that current tab page contains no more then one window other than -/// "aucmd_win". Only counts floating window if it is current. -bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// Check that current tab page contains no more then one window other than `aucmd_win`. +/// @param counted_float counted even if floating, but not if it is `aucmd_win` +bool one_window(win_T *counted_float) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { bool seen_one = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp != aucmd_win && (!wp->w_floating || wp == curwin)) { + if (wp != aucmd_win && (!wp->w_floating || wp == counted_float)) { if (seen_one) { return false; } @@ -2532,7 +2533,7 @@ int win_close(win_T *win, bool free_buf, bool force) frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; const bool had_diffmode = win->w_p_diff; - if (last_window() && !win->w_floating) { + if (last_window(win)) { emsg(_("E444: Cannot close last window")); return FAIL; } @@ -2545,7 +2546,7 @@ int win_close(win_T *win, bool free_buf, bool force) emsg(_(e_autocmd_close)); return FAIL; } - if (lastwin == aucmd_win && one_window()) { + if (lastwin == aucmd_win && one_window(win)) { emsg(_("E814: Cannot close window, only autocmd window would remain")); return FAIL; } @@ -2607,7 +2608,7 @@ int win_close(win_T *win, bool free_buf, bool force) return FAIL; } win->w_closing = false; - if (last_window()) { + if (last_window(win)) { return FAIL; } } @@ -2617,7 +2618,7 @@ int win_close(win_T *win, bool free_buf, bool force) return FAIL; } win->w_closing = false; - if (last_window()) { + if (last_window(win)) { return FAIL; } // autocmds may abort script processing @@ -2686,7 +2687,7 @@ int win_close(win_T *win, bool free_buf, bool force) } if (only_one_window() && win_valid(win) && win->w_buffer == NULL - && (last_window() || curtab != prev_curtab + && (last_window(win) || curtab != prev_curtab || close_last_window_tabpage(win, free_buf, prev_curtab)) && !win->w_floating) { // Autocommands have closed all windows, quit now. Restore @@ -2706,7 +2707,7 @@ int win_close(win_T *win, bool free_buf, bool force) // Autocommands may have closed the window already, or closed the only // other window or moved to another tab page. - if (!win_valid(win) || (!win->w_floating && last_window()) + if (!win_valid(win) || (!win->w_floating && last_window(win)) || close_last_window_tabpage(win, free_buf, prev_curtab)) { return FAIL; } @@ -3745,7 +3746,7 @@ void close_others(int message, int forceit) return; } - if (one_window() && !lastwin->w_floating) { + if (one_nonfloat() && !lastwin->w_floating) { if (message && !autocmd_busy) { msg(_(m_onlyone)); @@ -6500,7 +6501,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u void last_status(bool morewin) { // Don't make a difference between horizontal or vertical split. - last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window()))), + last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window(curwin)))), global_stl_height() > 0); } -- cgit From 3fdb7b528d9d066ccce8b1cb5d2225c338acfbb8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 21 Mar 2022 23:38:48 +0800 Subject: fix(float): handle buffer deletion with floating windows --- src/nvim/buffer.c | 11 ++++++++--- src/nvim/window.c | 50 ++++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 1293edb1da..1fe80dc24c 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1042,6 +1042,10 @@ static int empty_curbuf(int close_others, int forceit, int action) set_bufref(&bufref, buf); if (close_others) { + if (curwin->w_floating) { + // Last window must be non-floating. + curwin = firstwin; + } // Close any other windows on this buffer, then make it empty. close_windows(buf, true); } @@ -1224,11 +1228,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } // If the deleted buffer is the current one, close the current window - // (unless it's the only window). Repeat this so long as we end up in - // a window with this buffer. + // (unless it's the only non-floating window). + // When the autocommand window is involved win_close() may need to print an error message. + // Repeat this so long as we end up in a window with this buffer. while (buf == curbuf && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) - && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) { + && (lastwin == aucmd_win || !last_window(curwin))) { if (win_close(curwin, false, false) == FAIL) { break; } diff --git a/src/nvim/window.c b/src/nvim/window.c index 73c4acf45b..7df22c16d3 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2350,17 +2350,21 @@ void entering_window(win_T *const win) } } -/// Closes all windows for buffer `buf`. +/// Closes all windows for buffer `buf` until there is only one non-floating window. /// -/// @param keep_curwin don't close `curwin` -void close_windows(buf_T *buf, int keep_curwin) +/// @param keep_curwin don't close `curwin`, but caller must ensure `curwin` is non-floating. +void close_windows(buf_T *buf, bool keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); ++RedrawingDisabled; - for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW;) { + assert(!keep_curwin || !curwin->w_floating); + + // Start from lastwin to close floating windows with the same buffer first. + // When the autocommand window is involved win_close() may need to print an error message. + for (win_T *wp = lastwin; wp != NULL && (lastwin == aucmd_win || !one_window(wp));) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { if (win_close(wp, false, false) == FAIL) { @@ -2369,9 +2373,9 @@ void close_windows(buf_T *buf, int keep_curwin) } // Start all over, autocommands may change the window layout. - wp = firstwin; + wp = lastwin; } else { - wp = wp->w_next; + wp = wp->w_prev; } } @@ -2406,7 +2410,7 @@ void close_windows(buf_T *buf, int keep_curwin) /// /// @return true if the specified window is the only window that exists, /// false if there is another, possibly in another tab page. -static bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { return one_window(win) && first_tabpage->tp_next == NULL; } @@ -2442,12 +2446,14 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating); } -/// Check if floating windows can be closed. +/// Check if floating windows in the current tab can be closed. +/// Do not call this when the autocommand window is in use! /// /// @return true if all floating windows can be closed -static bool can_close_floating_windows(tabpage_T *tab) +static bool can_close_floating_windows(void) { - FOR_ALL_WINDOWS_IN_TAB(wp, tab) { + assert(lastwin != aucmd_win); + for (win_T *wp = lastwin; wp->w_floating; wp = wp->w_prev) { buf_T *buf = wp->w_buffer; int need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); @@ -2546,18 +2552,18 @@ int win_close(win_T *win, bool free_buf, bool force) emsg(_(e_autocmd_close)); return FAIL; } - if (lastwin == aucmd_win && one_window(win)) { - emsg(_("E814: Cannot close window, only autocmd window would remain")); - return FAIL; - } - if ((firstwin == win && lastwin_nofloating() == win) - && lastwin->w_floating) { - if (force || can_close_floating_windows(curtab)) { - win_T *nextwp; - for (win_T *wpp = firstwin; wpp != NULL; wpp = nextwp) { - nextwp = wpp->w_next; - if (wpp->w_floating) { - win_close(wpp, free_buf, force); + if (lastwin->w_floating && one_window(win)) { + if (lastwin == aucmd_win) { + emsg(_("E814: Cannot close window, only autocmd window would remain")); + return FAIL; + } + if (force || can_close_floating_windows()) { + // close the last window until the there are no floating windows + while (lastwin->w_floating) { + // `force` flag isn't actually used when closing a floating window. + if (win_close(lastwin, free_buf, true) == FAIL) { + // If closing the window fails give up, to avoid looping forever. + return FAIL; } } } else { -- cgit From a9359dca3794754ac3d4caff1bb86fe5d4dad547 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 22 Mar 2022 07:05:06 +0800 Subject: fix(float): make laststatus=1 behave consistently with floating windows --- src/nvim/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 7df22c16d3..bcc7a92b33 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6507,7 +6507,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u void last_status(bool morewin) { // Don't make a difference between horizontal or vertical split. - last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_window(curwin)))), + last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_nonfloat()))), global_stl_height() > 0); } -- cgit From f5a3edb0c0f761a82b22cd1ac193538220fdee95 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 19:52:50 +0800 Subject: refactor: remove cpo-& behavior (#17745) cpo-& has been removed, but its behavior was accidentally made the default behavior. That should be removed instead. --- src/nvim/buffer_defs.h | 1 - src/nvim/ex_docmd.c | 1 - src/nvim/memline.c | 14 ++++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f9541a55a3..29413281ad 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -90,7 +90,6 @@ typedef struct { #define BF_NEW_W 0x20 // Warned for BF_NEW and file created #define BF_READERR 0x40 // got errors while reading the file #define BF_DUMMY 0x80 // dummy buffer, only used internally -#define BF_PRESERVED 0x100 // ":preserve" was used #define BF_SYN_SET 0x200 // 'syntax' option was set // Mask to check for flags that prevent normal writing diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5ef9f6c05e..dab17fc3a4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7155,7 +7155,6 @@ void alist_slash_adjust(void) /// ":preserve". static void ex_preserve(exarg_T *eap) { - curbuf->b_flags |= BF_PRESERVED; ml_preserve(curbuf, true, true); } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 59f57aa667..5d62ddc912 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -585,16 +585,14 @@ void ml_close(buf_T *buf, int del_file) buf->b_flags &= ~BF_RECOVERED; } -/* - * Close all existing memlines and memfiles. - * Only used when exiting. - * When 'del_file' is TRUE, delete the memfiles. - * But don't delete files that were ":preserve"d when we are POSIX compatible. - */ -void ml_close_all(int del_file) +/// Close all existing memlines and memfiles. +/// Only used when exiting. +/// +/// @param del_file if true, delete the memfiles. +void ml_close_all(bool del_file) { FOR_ALL_BUFFERS(buf) { - ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0)); + ml_close(buf, del_file); } spell_delete_wordlist(); // delete the internal wordlist vim_deltempdir(); // delete created temp directory -- cgit From 52fe8eae078b027a04510715cc01b981f6879f8d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 20:23:41 +0800 Subject: fix(screen): do not update syntax_last_parsed when drawing folded line (#17826) --- src/nvim/screen.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 7182e69d08..0194149b1b 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1545,17 +1545,17 @@ static void win_update(win_T *wp, DecorProviders *providers) foldinfo.fi_lines ? srow : wp->w_grid.Rows, mod_top == 0, false, foldinfo, &line_providers); - wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0; - wp->w_lines[idx].wl_lastlnum = lnum; - did_update = DID_LINE; - - if (foldinfo.fi_lines > 0) { - did_update = DID_FOLD; + if (foldinfo.fi_lines == 0) { + wp->w_lines[idx].wl_folded = false; + wp->w_lines[idx].wl_lastlnum = lnum; + did_update = DID_LINE; + syntax_last_parsed = lnum; + } else { foldinfo.fi_lines--; + wp->w_lines[idx].wl_folded = true; wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines; + did_update = DID_FOLD; } - - syntax_last_parsed = lnum; } wp->w_lines[idx].wl_lnum = lnum; -- cgit From 06131370a4e9d18e277577edddee082755513ad3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 22:33:34 +0800 Subject: refactor(memline.c): make swapfile_unchanged() return bool (#17827) vim-patch:8.2.4613: return type of swapfile_unchanged() is wrong Problem: Return type of swapfile_unchanged() is wrong. Solution: Use "int". (closes vim/vim#10000 Yeah!) https://github.com/vim/vim/commit/3c5999e53d9f35a30abefb7224f66a75c8ffb009 --- src/nvim/memline.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5d62ddc912..9f8523eb3e 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1616,9 +1616,9 @@ static time_t swapfile_info(char_u *fname) return x; } -/// Returns TRUE if the swap file looks OK and there are no changes, thus it -/// can be safely deleted. -static time_t swapfile_unchanged(char *fname) +/// @return true if the swap file looks OK and there are no changes, thus it +/// can be safely deleted. +static bool swapfile_unchanged(char *fname) { struct block0 b0; int ret = true; -- cgit From d3af109d10b2adab23ce2e95d99f0ffb642c7c42 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 23 Mar 2022 23:25:20 +0100 Subject: fix(PVS/V560): ignore false "conditional expression is always false" (#17830) "'qi' points to the global variable 'ql_info' or the window local location list stack 'wp->w_llist'. The contents of these structures can be changed out-of-band by an autocmd." https://github.com/vim/vim/pull/9993#issuecomment-1076544168 --- src/nvim/quickfix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d868fe8284..9ee7b1cc8a 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2792,8 +2792,8 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int return NOTDONE; } - if (old_qf_curlist != qi->qf_curlist - || old_changetick != qfl->qf_changedtick + if (old_qf_curlist != qi->qf_curlist // -V560 + || old_changetick != qfl->qf_changedtick // -V560 || !is_qf_entry_present(qfl, qf_ptr)) { if (qfl_type == QFLT_QUICKFIX) { emsg(_(e_current_quickfix_list_was_changed)); @@ -2894,7 +2894,7 @@ static int qf_jump_open_window(qf_info_T *qi, qfline_T *qf_ptr, bool newwin, int } } if (old_qf_curlist != qi->qf_curlist - || old_changetick != qfl->qf_changedtick + || old_changetick != qfl->qf_changedtick // -V560 || !is_qf_entry_present(qfl, qf_ptr)) { if (qfl_type == QFLT_QUICKFIX) { emsg(_(e_current_quickfix_list_was_changed)); -- cgit From ff82b2785f161fc14ff6bd8eae497f37ecd14564 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Mar 2022 11:56:22 +0800 Subject: fix(float): don't always switch window when deleting last listed buffer (#17836) --- src/nvim/buffer.c | 16 ++++++++++++++-- src/nvim/window.c | 6 ++---- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 1fe80dc24c..7fc880fb41 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1043,8 +1043,20 @@ static int empty_curbuf(int close_others, int forceit, int action) if (close_others) { if (curwin->w_floating) { - // Last window must be non-floating. - curwin = firstwin; + bool can_close_all_others = false; + for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) { + if (wp->w_buffer != curbuf) { + // Found another non-floating window with a different (probably unlisted) buffer. + // Closing all other windows with the this buffer is fine in this case. + can_close_all_others = true; + break; + } + } + if (!can_close_all_others) { + // Closing all other windows with this buffer will close all non-floating windows. + // Move to a non-floating window then. + curwin = firstwin; + } } // Close any other windows on this buffer, then make it empty. close_windows(buf, true); diff --git a/src/nvim/window.c b/src/nvim/window.c index bcc7a92b33..fb7878c2f4 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2350,9 +2350,9 @@ void entering_window(win_T *const win) } } -/// Closes all windows for buffer `buf` until there is only one non-floating window. +/// Closes all windows for buffer `buf` unless there is only one non-floating window. /// -/// @param keep_curwin don't close `curwin`, but caller must ensure `curwin` is non-floating. +/// @param keep_curwin don't close `curwin` void close_windows(buf_T *buf, bool keep_curwin) { tabpage_T *tp, *nexttp; @@ -2360,8 +2360,6 @@ void close_windows(buf_T *buf, bool keep_curwin) ++RedrawingDisabled; - assert(!keep_curwin || !curwin->w_floating); - // Start from lastwin to close floating windows with the same buffer first. // When the autocommand window is involved win_close() may need to print an error message. for (win_T *wp = lastwin; wp != NULL && (lastwin == aucmd_win || !one_window(wp));) { -- cgit From a72f338d76c871869712518df862c85d1df25f54 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Mar 2022 14:53:20 +0800 Subject: fix(float): do not switch window before deleting last listed buffer (#17840) Just allow close_windows() to close the current window instead. This fixes wrong working directory or autocommands not being triggered. --- src/nvim/buffer.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 7fc880fb41..f200f16a5f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1042,24 +1042,24 @@ static int empty_curbuf(int close_others, int forceit, int action) set_bufref(&bufref, buf); if (close_others) { + bool can_close_all_others = true; if (curwin->w_floating) { - bool can_close_all_others = false; + // Closing all other windows with this buffer may leave only floating windows. + can_close_all_others = false; for (win_T *wp = firstwin; !wp->w_floating; wp = wp->w_next) { if (wp->w_buffer != curbuf) { // Found another non-floating window with a different (probably unlisted) buffer. - // Closing all other windows with the this buffer is fine in this case. + // Closing all other windows with this buffer is fine in this case. can_close_all_others = true; break; } } - if (!can_close_all_others) { - // Closing all other windows with this buffer will close all non-floating windows. - // Move to a non-floating window then. - curwin = firstwin; - } } - // Close any other windows on this buffer, then make it empty. - close_windows(buf, true); + // If it is fine to close all other windows with this buffer, keep the current window and + // close any other windows with this buffer, then make it empty. + // Otherwise close_windows() will refuse to close the last non-floating window, so allow it + // to close the current window instead. + close_windows(buf, can_close_all_others); } setpcmark(); -- cgit From 3e9b4e917d0783d0414192c3ad231cfcb813e73f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 11:30:02 +0800 Subject: vim-patch:8.2.4591: cursor line not updated when a callback moves the cursor Problem: Cursor line not updated when a callback moves the cursor. Solution: Check if the cursor moved. (closes vim/vim#9970) https://github.com/vim/vim/commit/e7a74d53754765f22ef8ce71c915bb669d5f7f3f redraw_after_callback() is N/A. Nvim handles timers on the main loop. --- src/nvim/normal.c | 8 +------- src/nvim/screen.c | 17 ++++++++++++++++- src/nvim/testdir/test_cursorline.vim | 26 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f402865d2d..e773351d63 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1296,13 +1296,7 @@ static void normal_redraw(NormalState *s) } // Might need to update for 'cursorline'. - // When 'cursorlineopt' is "screenline" need to redraw always. - if (curwin->w_p_cul - && (curwin->w_last_cursorline != curwin->w_cursor.lnum - || (curwin->w_p_culopt_flags & CULOPT_SCRLINE)) - && !char_avail()) { - redraw_later(curwin, VALID); - } + check_redraw_cursorline(); if (VIsual_active) { update_curbuf(INVERTED); // update inverted part diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0194149b1b..27011c6f1e 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -737,6 +737,9 @@ static void win_update(win_T *wp, DecorProviders *providers) #define DID_FOLD 3 // updated a folded line int did_update = DID_NONE; linenr_T syntax_last_parsed = 0; // last parsed text line + // remember the current w_last_cursorline, it changes when drawing the new + // cursor line + linenr_T last_cursorline = wp->w_last_cursorline; linenr_T mod_top = 0; linenr_T mod_bot = 0; int save_got_int; @@ -1368,7 +1371,7 @@ static void win_update(win_T *wp, DecorProviders *providers) || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) || (wp->w_p_cul && (lnum == wp->w_cursor.lnum - || lnum == wp->w_last_cursorline))) { + || lnum == last_cursorline))) { if (lnum == mod_top) { top_to_mod = false; } @@ -7615,3 +7618,15 @@ win_T *get_win_by_grid_handle(handle_T handle) return NULL; } +/// Check if the cursor moved and 'cursorline' is set. Mark for a VALID redraw +/// if needed. +void check_redraw_cursorline(void) +{ + // When 'cursorlineopt' is "screenline" need to redraw always. + if (curwin->w_p_cul + && (curwin->w_last_cursorline != curwin->w_cursor.lnum + || (curwin->w_p_culopt_flags & CULOPT_SCRLINE)) + && !char_avail()) { + redraw_later(curwin, VALID); + } +} diff --git a/src/nvim/testdir/test_cursorline.vim b/src/nvim/testdir/test_cursorline.vim index 39d8b901ed..bf049ec779 100644 --- a/src/nvim/testdir/test_cursorline.vim +++ b/src/nvim/testdir/test_cursorline.vim @@ -268,4 +268,30 @@ END call delete('Xtextfile') endfunc +func Test_cursorline_callback() + CheckScreendump + CheckFeature timers + + let lines =<< trim END + call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd']) + set cursorline + call cursor(4, 1) + + func Func(timer) + call cursor(2, 1) + endfunc + + call timer_start(300, 'Func') + END + call writefile(lines, 'Xcul_timer') + + let buf = RunVimInTerminal('-S Xcul_timer', #{rows: 8}) + call TermWait(buf, 310) + call VerifyScreenDump(buf, 'Test_cursorline_callback_1', {}) + + call StopVimInTerminal(buf) + call delete('Xcul_timer') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From c29a14d1fa58d5472bd14fec99c5b4228ed38b24 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Mar 2022 11:28:32 +0800 Subject: perf(screen): reduce cursorline redrawing when jumping around vim-patch:8.2.4614: redrawing too much when 'cursorline' is set Problem: Redrawing too much when 'cursorline' is set and jumping around. Solution: Rely on win_update() to redraw the current and previous cursor line, do not mark lines as modified. (closes vim/vim#9996) https://github.com/vim/vim/commit/c20e46a4e3efcd408ef132872238144ea34f7ae5 This doesn't match the patch exactly, because I missed some lines when porting patch 8.1.2029, and these lines were removed in this patch. This also makes win_update() always update for 'concealcursor' like how it always updates for 'cursorline', as 'cursorline' and 'concealcursor' redrawing logic has been unified in Nvim. As redrawing for 'cursorline' now always only requires VALID redraw type, it is no longer necessary to call redraw_for_cursorline() in nvim_win_set_cursor(). --- src/nvim/api/window.c | 1 - src/nvim/move.c | 22 ++-------------------- src/nvim/option.c | 5 +---- src/nvim/screen.c | 14 +++++++------- 4 files changed, 10 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index be43708604..fd33a82be3 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -119,7 +119,6 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) update_topline_win(win); redraw_later(win, VALID); - redraw_for_cursorline(win); win->w_redr_status = true; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 27cc2b341c..751e0046bc 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -95,11 +95,6 @@ static void comp_botline(win_T *wp) win_check_anchored_floats(wp); } -void reset_cursorline(void) -{ - curwin->w_last_cursorline = 0; -} - // Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. void redraw_for_cursorline(win_T *wp) FUNC_ATTR_NONNULL_ALL @@ -107,21 +102,8 @@ void redraw_for_cursorline(win_T *wp) if ((wp->w_p_rnu || win_cursorline_standout(wp)) && (wp->w_valid & VALID_CROW) == 0 && !pum_visible()) { - if (wp->w_p_rnu) { - // win_line() will redraw the number column only. - redraw_later(wp, VALID); - } - if (win_cursorline_standout(wp)) { - if (wp->w_redr_type <= VALID && wp->w_last_cursorline != 0) { - // "w_last_cursorline" may be outdated, worst case we redraw - // too much. This is optimized for moving the cursor around in - // the current window. - redrawWinline(wp, wp->w_last_cursorline); - redrawWinline(wp, wp->w_cursor.lnum); - } else { - redraw_later(wp, SOME_VALID); - } - } + // win_line() will redraw the number column and cursorline only. + redraw_later(wp, VALID); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index ffd009be89..9fd0e0b222 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3962,11 +3962,8 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va } else if ((int *)varp == &p_lnr) { // 'langnoremap' -> !'langremap' p_lrm = !p_lnr; - } else if ((int *)varp == &curwin->w_p_cul && !value && old_value) { - // 'cursorline' - reset_cursorline(); - // 'undofile' } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { + // 'undofile' // Only take action when the option was set. When reset we do not // delete the undo file, the option may be set again without making // any changes in between. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 27011c6f1e..5a10543559 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -737,9 +737,6 @@ static void win_update(win_T *wp, DecorProviders *providers) #define DID_FOLD 3 // updated a folded line int did_update = DID_NONE; linenr_T syntax_last_parsed = 0; // last parsed text line - // remember the current w_last_cursorline, it changes when drawing the new - // cursor line - linenr_T last_cursorline = wp->w_last_cursorline; linenr_T mod_top = 0; linenr_T mod_bot = 0; int save_got_int; @@ -1326,6 +1323,8 @@ static void win_update(win_T *wp, DecorProviders *providers) DecorProviders line_providers; decor_providers_invoke_win(wp, providers, &line_providers, &provider_err); + bool cursorline_standout = win_cursorline_standout(wp); + for (;;) { /* stop updating when reached the end of the window (check for _past_ * the end of the window is at the end of the loop) */ @@ -1370,8 +1369,8 @@ static void win_update(win_T *wp, DecorProviders *providers) // if lines were inserted or deleted || (wp->w_match_head != NULL && buf->b_mod_xlines != 0))))) - || (wp->w_p_cul && (lnum == wp->w_cursor.lnum - || lnum == last_cursorline))) { + || (cursorline_standout && lnum == wp->w_cursor.lnum) + || lnum == wp->w_last_cursorline) { if (lnum == mod_top) { top_to_mod = false; } @@ -1604,6 +1603,9 @@ static void win_update(win_T *wp, DecorProviders *providers) * End of loop over all window lines. */ + // Now that the window has been redrawn with the old and new cursor line, + // update w_last_cursorline. + wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0; if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; @@ -2383,8 +2385,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } area_highlighting = true; } - // Update w_last_cursorline even if Visual mode is active. - wp->w_last_cursorline = wp->w_cursor.lnum; } memset(sattrs, 0, sizeof(sattrs)); -- cgit From d7488bf38677b5d6b1df3a88e45b3d2f21527eb4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 30 Jan 2022 07:56:54 +0800 Subject: feat(input)!: delay some conversions to vgetc() --- src/nvim/getchar.c | 8 ++++++++ src/nvim/keymap.c | 16 ++++++++++------ src/nvim/option_defs.h | 1 + src/nvim/options.lua | 7 +++++++ src/nvim/testdir/setup.vim | 1 + 5 files changed, 27 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 3ec5d24753..c10172cc52 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1583,6 +1583,14 @@ int vgetc(void) c = utf_ptr2char(buf); } + if ((mod_mask & MOD_MASK_CTRL) && (c >= '?' && c <= '_')) { + c = Ctrl_chr(c); + mod_mask &= ~MOD_MASK_CTRL; + if (c == 0) { // is + c = K_ZERO; + } + } + // If mappings are enabled (i.e., not Ctrl-v) and the user directly typed // something with a meta- or alt- modifier that was not mapped, interpret // as x rather than as an unbound meta keypress. #8213 diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 32f2158d7b..f0536cbf15 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" +#include "nvim/option_defs.h" #include "nvim/strings.h" #include "nvim/vim.h" @@ -744,12 +745,15 @@ static int extract_modifiers(int key, int *modp) modifiers &= ~MOD_MASK_SHIFT; } } - if ((modifiers & MOD_MASK_CTRL) - && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { - key = Ctrl_chr(key); - modifiers &= ~MOD_MASK_CTRL; - if (key == 0) { // is - key = K_ZERO; + if ((modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { + key = TOUPPER_ASC(key); + int new_key = Ctrl_chr(key); + if (!p_clbg || (new_key != TAB && new_key != CAR && new_key != ESC)) { + key = new_key; + modifiers &= ~MOD_MASK_CTRL; + if (key == 0) { // is + key = K_ZERO; + } } } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index d88cd6b9b9..b6946c4c4f 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -401,6 +401,7 @@ EXTERN int p_cst; // 'cscopetag' EXTERN long p_csto; // 'cscopetagorder' EXTERN long p_cspc; // 'cscopepathcomp' EXTERN int p_csverbose; // 'cscopeverbose' +EXTERN int p_clbg; // 'ctrldisambig' EXTERN char_u *p_debug; // 'debug' EXTERN char_u *p_def; // 'define' EXTERN char_u *p_inc; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e665ffd346..d12d1d7c2c 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -541,6 +541,13 @@ return { varname='p_csverbose', defaults={if_true=1} }, + { + full_name='ctrldisambig', abbreviation='clbg', + short_desc=N_(""), + type='bool', scope={'global'}, + varname='p_clbg', + defaults={if_true=true} + }, { full_name='cursorbind', abbreviation='crb', short_desc=N_("move cursor in window as it moves in other windows"), diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 15e3b31498..589ceb0297 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -14,6 +14,7 @@ set fsync set laststatus=1 set listchars=eol:$ set joinspaces +set noctrldisambig set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd set nrformats+=octal set shortmess-=F -- cgit From ed88ca75034a48916d165e88459c791c450df550 Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 23 Mar 2022 11:58:47 +0100 Subject: feat(input): enable /, /, / pairs unconditionally --- src/nvim/keymap.c | 3 +- src/nvim/option_defs.h | 1 - src/nvim/options.lua | 7 -- src/nvim/testdir/setup.vim | 1 - src/nvim/testdir/test_eval_stuff.vim | 2 +- src/nvim/testdir/test_regex_char_classes.vim | 104 +++++++++++++-------------- src/nvim/testdir/test_substitute.vim | 56 +++++++-------- 7 files changed, 82 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index f0536cbf15..9ad9640834 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -13,7 +13,6 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" -#include "nvim/option_defs.h" #include "nvim/strings.h" #include "nvim/vim.h" @@ -748,7 +747,7 @@ static int extract_modifiers(int key, int *modp) if ((modifiers & MOD_MASK_CTRL) && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { key = TOUPPER_ASC(key); int new_key = Ctrl_chr(key); - if (!p_clbg || (new_key != TAB && new_key != CAR && new_key != ESC)) { + if (new_key != TAB && new_key != CAR && new_key != ESC) { key = new_key; modifiers &= ~MOD_MASK_CTRL; if (key == 0) { // is diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index b6946c4c4f..d88cd6b9b9 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -401,7 +401,6 @@ EXTERN int p_cst; // 'cscopetag' EXTERN long p_csto; // 'cscopetagorder' EXTERN long p_cspc; // 'cscopepathcomp' EXTERN int p_csverbose; // 'cscopeverbose' -EXTERN int p_clbg; // 'ctrldisambig' EXTERN char_u *p_debug; // 'debug' EXTERN char_u *p_def; // 'define' EXTERN char_u *p_inc; diff --git a/src/nvim/options.lua b/src/nvim/options.lua index d12d1d7c2c..e665ffd346 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -541,13 +541,6 @@ return { varname='p_csverbose', defaults={if_true=1} }, - { - full_name='ctrldisambig', abbreviation='clbg', - short_desc=N_(""), - type='bool', scope={'global'}, - varname='p_clbg', - defaults={if_true=true} - }, { full_name='cursorbind', abbreviation='crb', short_desc=N_("move cursor in window as it moves in other windows"), diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 589ceb0297..15e3b31498 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -14,7 +14,6 @@ set fsync set laststatus=1 set listchars=eol:$ set joinspaces -set noctrldisambig set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd set nrformats+=octal set shortmess-=F diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 95eccde35c..12febfeb93 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -185,7 +185,7 @@ func Test_let_register() call Assert_reg('"', 'v', "abc", "['abc']", "abc", "['abc']") let @" = "abc\n" call Assert_reg('"', 'V', "abc\n", "['abc']", "abc\n", "['abc']") - let @" = "abc\" + let @" = "abc\r" call Assert_reg('"', 'V', "abc\r\n", "['abc\r']", "abc\r\n", "['abc\r']") let @= = '"abc"' call Assert_reg('=', 'v', "abc", "['abc']", '"abc"', "['\"abc\"']") diff --git a/src/nvim/testdir/test_regex_char_classes.vim b/src/nvim/testdir/test_regex_char_classes.vim index c1a4202c2b..b0d76a15e2 100644 --- a/src/nvim/testdir/test_regex_char_classes.vim +++ b/src/nvim/testdir/test_regex_char_classes.vim @@ -66,22 +66,22 @@ func Test_regex_char_classes() let save_enc = &encoding set encoding=utf-8 - let input = "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé" + let input = "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé" " Format is [cmd_to_run, expected_output] let tests = [ \ [':s/\%#=0\d//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\d//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\d//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[0-9]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[0-9]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[0-9]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\D//g', \ "0123456789"], \ [':s/\%#=1\D//g', @@ -95,17 +95,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9]//g', \ "0123456789"], \ [':s/\%#=0\o//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\o//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\o//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[0-7]//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[0-7]//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[0-7]//g', - \ "\t\\ !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./89:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\O//g', \ "01234567"], \ [':s/\%#=1\O//g', @@ -119,17 +119,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-7]//g', \ "01234567"], \ [':s/\%#=0\x//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\x//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\x//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[0-9A-Fa-f]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[0-9A-Fa-f]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[0-9A-Fa-f]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@GHIXYZ[\]^_`ghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\X//g', \ "0123456789ABCDEFabcdef"], \ [':s/\%#=1\X//g', @@ -143,17 +143,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9A-Fa-f]//g', \ "0123456789ABCDEFabcdef"], \ [':s/\%#=0\w//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\w//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\w//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[0-9A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[0-9A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[0-9A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\W//g', \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=1\W//g', @@ -167,17 +167,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^0-9A-Za-z_]//g', \ "0123456789ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=0\h//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\h//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\h//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[A-Za-z_]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\H//g', \ "ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=1\H//g', @@ -191,17 +191,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^A-Za-z_]//g', \ "ABCDEFGHIXYZ_abcdefghiwxyz"], \ [':s/\%#=0\a//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\a//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\a//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[A-Za-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[A-Za-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[A-Za-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\A//g', \ "ABCDEFGHIXYZabcdefghiwxyz"], \ [':s/\%#=1\A//g', @@ -215,17 +215,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^A-Za-z]//g', \ "ABCDEFGHIXYZabcdefghiwxyz"], \ [':s/\%#=0\l//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\l//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\l//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[a-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[a-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[a-z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\L//g', \ "abcdefghiwxyz"], \ [':s/\%#=1\L//g', @@ -239,17 +239,17 @@ func Test_regex_char_classes() \ [':s/\%#=2[^a-z]//g', \ "abcdefghiwxyz"], \ [':s/\%#=0\u//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1\u//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2\u//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[A-Z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[A-Z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[A-Z]//g', - \ "\t\\ !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./0123456789:;<=>?@[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0\U//g', \ "ABCDEFGHIXYZ"], \ [':s/\%#=1\U//g', @@ -269,11 +269,11 @@ func Test_regex_char_classes() \ [':s/\%#=2\%' . line('.') . 'l^\t...//g', \ "!\"#$%&'()#+'-./0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[0-z]//g', - \ "\t\\ !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=1[0-z]//g', - \ "\t\\ !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=2[0-z]//g', - \ "\t\\ !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], + \ "\t\\r !\"#$%&'()#+'-./{|}~\\u0080\u0082\u0090\u009b¦±¼ÇÓé"], \ [':s/\%#=0[^0-z]//g', \ "0123456789:;<=>?@ABCDEFGHIXYZ[\]^_`abcdefghiwxyz"], \ [':s/\%#=1[^0-z]//g', diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index dbb792d2b0..9710a7ab84 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -177,9 +177,9 @@ func Test_sub_cmd_1() \ ['I', 's/I/\lII/', ['iI']], \ ['J', 's/J/\LJ\EJ/', ['jJ']], \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\\/", ["l\", 'l']], + \ ['lLl', "s/L/\\r/", ["l\", 'l']], \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\\/", ["n\", 'n']], + \ ['nNn', "s/N/\\\\r/", ["n\", 'n']], \ ['oOo', 's/O/\n/', ["o\no"]], \ ['pPp', 's/P/\b/', ["p\p"]], \ ['qQq', 's/Q/\t/', ["q\tq"]], @@ -208,9 +208,9 @@ func Test_sub_cmd_2() \ ['I', 's/I/\lII/', ['iI']], \ ['J', 's/J/\LJ\EJ/', ['jJ']], \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\\/", ["l\", 'l']], + \ ['lLl', "s/L/\\r/", ["l\", 'l']], \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\\/", ["n\", 'n']], + \ ['nNn', "s/N/\\\\r/", ["n\", 'n']], \ ['oOo', 's/O/\n/', ["o\no"]], \ ['pPp', 's/P/\b/', ["p\p"]], \ ['qQq', 's/Q/\t/', ["q\tq"]], @@ -230,9 +230,9 @@ func Test_sub_cmd_3() " List entry format: [input, cmd, output] let tests = [['aAa', "s/A/\\='\\'/", ['a\a']], \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']], - \ ['cCc', "s/C/\\='\\'/", ["c\", 'c']], - \ ['dDd', "s/D/\\='\\\\'/", ["d\\\", 'd']], - \ ['eEe', "s/E/\\='\\\\\\'/", ["e\\\\\", 'e']], + \ ['cCc', "s/C/\\='\\r'/", ["c\", 'c']], + \ ['dDd', "s/D/\\='\\\\r'/", ["d\\\", 'd']], + \ ['eEe', "s/E/\\='\\\\\\r'/", ["e\\\\\", 'e']], \ ['fFf', "s/F/\\='\r'/", ['f', 'f']], \ ['gGg', "s/G/\\='\\'/", ["g\", 'g']], \ ['hHh', "s/H/\\='\\\\'/", ["h\\\", 'h']], @@ -254,11 +254,11 @@ func Test_sub_cmd_4() \ ['a\a']], \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/", \ ['b\b']], - \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\\', '')/", + \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\\r', '')/", \ ["c\", 'c']], - \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\\', '')/", + \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\\r', '')/", \ ["d\", 'd']], - \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\\', '')/", + \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\\r', '')/", \ ["e\\\", 'e']], \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/", \ ['f', 'f']], @@ -316,7 +316,7 @@ func Test_sub_cmd_7() set cpo& " List entry format: [input, cmd, output] - let tests = [ ["A\\A", 's/A./\=submatch(0)/', ['A', 'A']], + let tests = [ ["A\\rA", 's/A./\=submatch(0)/', ['A', 'A']], \ ["B\\B", 's/B./\=submatch(0)/', ['B', 'B']], \ ["C\\C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\']C")]], \ ["D\\\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\', 'D']")]], @@ -467,11 +467,11 @@ func Test_sub_replace_1() call assert_equal('iI', substitute('I', 'I', '\lII', '')) call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', '')) call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', '')) - call assert_equal("l\\l", - \ substitute('lLl', 'L', "\\", '')) - call assert_equal("m\m", substitute('mMm', 'M', '\r', '')) - call assert_equal("n\\n", - \ substitute('nNn', 'N', "\\\\", '')) + call assert_equal("l\\rl", + \ substitute('lLl', 'L', "\\r", '')) + call assert_equal("m\rm", substitute('mMm', 'M', '\r', '')) + call assert_equal("n\\rn", + \ substitute('nNn', 'N', "\\\\r", '')) call assert_equal("o\no", substitute('oOo', 'O', '\n', '')) call assert_equal("p\p", substitute('pPp', 'P', '\b', '')) call assert_equal("q\tq", substitute('qQq', 'Q', '\t', '')) @@ -480,7 +480,7 @@ func Test_sub_replace_1() call assert_equal("u\nu", substitute('uUu', 'U', "\n", '')) call assert_equal("v\v", substitute('vVv', 'V', "\b", '')) call assert_equal("w\\w", substitute('wWw', 'W', "\\", '')) - call assert_equal("x\x", substitute('xXx', 'X', "\r", '')) + call assert_equal("x\rx", substitute('xXx', 'X', "\r", '')) call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', '')) call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', '')) endfunc @@ -500,17 +500,17 @@ func Test_sub_replace_2() call assert_equal('iI', substitute('I', 'I', '\lII', '')) call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', '')) call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', '')) - call assert_equal("l\\l", - \ substitute('lLl', 'L', "\\", '')) - call assert_equal("m\m", substitute('mMm', 'M', '\r', '')) - call assert_equal("n\\n", - \ substitute('nNn', 'N', "\\\\", '')) + call assert_equal("l\\rl", + \ substitute('lLl', 'L', "\\r", '')) + call assert_equal("m\rm", substitute('mMm', 'M', '\r', '')) + call assert_equal("n\\rn", + \ substitute('nNn', 'N', "\\\\r", '')) call assert_equal("o\no", substitute('oOo', 'O', '\n', '')) call assert_equal("p\p", substitute('pPp', 'P', '\b', '')) call assert_equal("q\tq", substitute('qQq', 'Q', '\t', '')) call assert_equal('r\r', substitute('rRr', 'R', '\\', '')) call assert_equal('scs', substitute('sSs', 'S', '\c', '')) - call assert_equal("t\t", substitute('tTt', 'T', "\r", '')) + call assert_equal("t\rt", substitute('tTt', 'T', "\r", '')) call assert_equal("u\nu", substitute('uUu', 'U', "\n", '')) call assert_equal("v\v", substitute('vVv', 'V', "\b", '')) call assert_equal('w\w', substitute('wWw', 'W', "\\", '')) @@ -528,7 +528,7 @@ func Test_sub_replace_3() call assert_equal("e\\\\\re", substitute('eEe', 'E', "\\=\"\\\\\\\\\r\"", '')) call assert_equal('f\rf', substitute('fFf', 'F', '\="\\r"', '')) call assert_equal('j\nj', substitute('jJj', 'J', '\="\\n"', '')) - call assert_equal("k\k", substitute('kKk', 'K', '\="\r"', '')) + call assert_equal("k\rk", substitute('kKk', 'K', '\="\r"', '')) call assert_equal("l\nl", substitute('lLl', 'L', '\="\n"', '')) endfunc @@ -540,10 +540,10 @@ func Test_sub_replace_4() \ '\=substitute(submatch(0), ".", "\\", "")', '')) call assert_equal('b\b', substitute('bBb', 'B', \ '\=substitute(submatch(0), ".", "\\\\", "")', '')) - call assert_equal("c\\c", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\\", "")', '')) - call assert_equal("d\\d", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\\", "")', '')) - call assert_equal("e\\\\e", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\\", "")', '')) - call assert_equal("f\f", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', '')) + call assert_equal("c\\rc", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\\r", "")', '')) + call assert_equal("d\\rd", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\\r", "")', '')) + call assert_equal("e\\\\re", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\\r", "")', '')) + call assert_equal("f\rf", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', '')) call assert_equal("j\nj", substitute('jJj', 'J', '\=substitute(submatch(0), ".", "\\n", "")', '')) call assert_equal("k\rk", substitute('kKk', 'K', '\=substitute(submatch(0), ".", "\r", "")', '')) call assert_equal("l\nl", substitute('lLl', 'L', '\=substitute(submatch(0), ".", "\n", "")', '')) -- cgit From 8b7f818ee79bb06b4fdea66b8317fbd27d4b685c Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sun, 13 Mar 2022 16:21:44 +0100 Subject: refactor(memline): convert function comments to doxygen format --- src/nvim/memline.c | 337 +++++++++++++++++++++++------------------------------ 1 file changed, 144 insertions(+), 193 deletions(-) (limited to 'src') diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 9f8523eb3e..1df32f79e1 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -242,11 +242,9 @@ typedef enum { # include "memline.c.generated.h" #endif -/* - * Open a new memline for "buf". - * - * Return FAIL for failure, OK otherwise. - */ +/// Open a new memline for "buf". +/// +/// @return FAIL for failure, OK otherwise. int ml_open(buf_T *buf) { bhdr_T *hp = NULL; @@ -379,10 +377,8 @@ error: return FAIL; } -/* - * ml_setname() is called when the file name of "buf" has been changed. - * It may rename the swap file. - */ +/// ml_setname() is called when the file name of "buf" has been changed. +/// It may rename the swap file. void ml_setname(buf_T *buf) { bool success = false; @@ -458,11 +454,9 @@ void ml_setname(buf_T *buf) } } -/* - * Open a file for the memfile for all buffers that are not readonly or have - * been modified. - * Used when 'updatecount' changes from zero to non-zero. - */ +/// Open a file for the memfile for all buffers that are not readonly or have +/// been modified. +/// Used when 'updatecount' changes from zero to non-zero. void ml_open_files(void) { FOR_ALL_BUFFERS(buf) { @@ -472,11 +466,9 @@ void ml_open_files(void) } } -/* - * Open a swap file for an existing memfile, if there is no swap file yet. - * If we are unable to find a file name, mf_fname will be NULL - * and the memfile will be in memory only (no recovery possible). - */ +/// Open a swap file for an existing memfile, if there is no swap file yet. +/// If we are unable to find a file name, mf_fname will be NULL +/// and the memfile will be in memory only (no recovery possible). void ml_open_file(buf_T *buf) { memfile_T *mfp; @@ -563,10 +555,9 @@ void check_need_swap(bool newfile) msg_silent = old_msg_silent; } -/* - * Close memline for buffer 'buf'. - * If 'del_file' is TRUE, delete the swap file - */ +/// Close memline for buffer 'buf'. +/// +/// @param del_file if TRUE, delete the swap file void ml_close(buf_T *buf, int del_file) { if (buf->b_ml.ml_mfp == NULL) { // not open @@ -598,10 +589,8 @@ void ml_close_all(bool del_file) vim_deltempdir(); // delete created temp directory } -/* - * Close all memfiles for not modified buffers. - * Only use just before exiting! - */ +/// Close all memfiles for not modified buffers. +/// Only use just before exiting! void ml_close_notmod(void) { FOR_ALL_BUFFERS(buf) { @@ -611,10 +600,8 @@ void ml_close_notmod(void) } } -/* - * Update the timestamp in the .swp file. - * Used when the file has been written. - */ +/// Update the timestamp in the .swp file. +/// Used when the file has been written. void ml_timestamp(buf_T *buf) { ml_upd_block0(buf, UB_FNAME); @@ -637,9 +624,7 @@ static bool ml_check_b0_strings(ZERO_BL *b0p) && memchr(b0p->b0_fname, NUL, B0_FNAME_SIZE_CRYPT)); // -V512 } -/* - * Update the timestamp or the B0_SAME_DIR flag of the .swp file. - */ +/// Update the timestamp or the B0_SAME_DIR flag of the .swp file. static void ml_upd_block0(buf_T *buf, upd_block0_T what) { memfile_T *mfp; @@ -663,11 +648,9 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) mf_put(mfp, hp, true, false); } -/* - * Write file name and timestamp into block 0 of a swap file. - * Also set buf->b_mtime. - * Don't use NameBuff[]!!! - */ +/// Write file name and timestamp into block 0 of a swap file. +/// Also set buf->b_mtime. +/// Don't use NameBuff[]!!! static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) { if (buf->b_ffname == NULL) { @@ -719,12 +702,10 @@ static void set_b0_fname(ZERO_BL *b0p, buf_T *buf) add_b0_fenc(b0p, curbuf); } -/* - * Update the B0_SAME_DIR flag of the swap file. It's set if the file and the - * swapfile for "buf" are in the same directory. - * This is fail safe: if we are not sure the directories are equal the flag is - * not set. - */ +/// Update the B0_SAME_DIR flag of the swap file. It's set if the file and the +/// swapfile for "buf" are in the same directory. +/// This is fail safe: if we are not sure the directories are equal the flag is +/// not set. static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) { if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname)) { @@ -734,9 +715,7 @@ static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf) } } -/* - * When there is room, add the 'fileencoding' to block zero. - */ +/// When there is room, add the 'fileencoding' to block zero. static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) { int n; @@ -755,8 +734,9 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf) /// Try to recover curbuf from the .swp file. -/// @param checkext If true, check the extension and detect whether it is a -/// swap file. +/// +/// @param checkext if true, check the extension and detect whether it is a +/// swap file. void ml_recover(bool checkext) { buf_T *buf = NULL; @@ -1463,10 +1443,8 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out) return file_count; } -/* - * Append the full path to name with path separators made into percent - * signs, to dir. An unnamed buffer is handled as "" (/"") - */ +/// Append the full path to name with path separators made into percent +/// signs, to dir. An unnamed buffer is handled as "" (/"") char *make_percent_swname(const char *dir, const char *name) FUNC_ATTR_NONNULL_ARG(1) { @@ -1488,8 +1466,9 @@ char *make_percent_swname(const char *dir, const char *name) static bool process_still_running; -/// Return information found in swapfile "fname" in dictionary "d". /// This is used by the swapinfo() function. +/// +/// @return information found in swapfile "fname" in dictionary "d". void get_b0_dict(const char *fname, dict_T *d) { int fd; @@ -1526,7 +1505,8 @@ void get_b0_dict(const char *fname, dict_T *d) } /// Give information about an existing swap file. -/// Returns timestamp (0 when unknown). +/// +/// @return timestamp (0 when unknown). static time_t swapfile_info(char_u *fname) { assert(fname != NULL); @@ -1696,13 +1676,12 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) return num_names; } -/* - * sync all memlines - * - * If 'check_file' is TRUE, check if original file exists and was not changed. - * If 'check_char' is TRUE, stop syncing when character becomes available, but - * always sync at least one block. - */ +/// sync all memlines +/// +/// @param check_file if TRUE, check if original file exists and was not changed. +/// @param check_char if TRUE, stop syncing when character becomes available, but +/// +/// always sync at least one block. void ml_sync_all(int check_file, int check_char, bool do_fsync) { FOR_ALL_BUFFERS(buf) { @@ -1738,16 +1717,14 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync) } } -/* - * sync one buffer, including negative blocks - * - * after this all the blocks are in the swap file - * - * Used for the :preserve command and when the original file has been - * changed or deleted. - * - * when message is TRUE the success of preserving is reported - */ +/// sync one buffer, including negative blocks +/// +/// after this all the blocks are in the swap file +/// +/// Used for the :preserve command and when the original file has been +/// changed or deleted. +/// +/// @param message if TRUE, the success of preserving is reported. void ml_preserve(buf_T *buf, int message, bool do_fsync) { bhdr_T *hp; @@ -1823,27 +1800,24 @@ theend: * line2 = ml_get(2); // line1 is now invalid! * Make a copy of the line if necessary. */ -/* - * Return a pointer to a (read-only copy of a) line. - * - * On failure an error message is given and IObuff is returned (to avoid - * having to check for error everywhere). - */ + +/// @return a pointer to a (read-only copy of a) line. +/// +/// On failure an error message is given and IObuff is returned (to avoid +/// having to check for error everywhere). char_u *ml_get(linenr_T lnum) { return ml_get_buf(curbuf, lnum, false); } -/* - * Return pointer to position "pos". - */ +/// @return pointer to position "pos". char_u *ml_get_pos(const pos_T *pos) FUNC_ATTR_NONNULL_ALL { return ml_get_buf(curbuf, pos->lnum, false) + pos->col; } -/// get codepoint at pos. pos must be either valid or have col set to MAXCOL! +/// @return codepoint at pos. pos must be either valid or have col set to MAXCOL! int gchar_pos(pos_T *pos) FUNC_ATTR_NONNULL_ARG(1) { @@ -1854,9 +1828,9 @@ int gchar_pos(pos_T *pos) return utf_ptr2char(ml_get_pos(pos)); } -/// Return a pointer to a line in a specific buffer -/// /// @param will_change true mark the buffer dirty (chars in the line will be changed) +/// +/// @return a pointer to a line in a specific buffer char_u *ml_get_buf(buf_T *buf, linenr_T lnum, bool will_change) FUNC_ATTR_NONNULL_ALL { @@ -1929,10 +1903,8 @@ errorret: return buf->b_ml.ml_line_ptr; } -/* - * Check if a line that was just obtained by a call to ml_get - * is in allocated memory. - */ +/// Check if a line that was just obtained by a call to ml_get +/// is in allocated memory. int ml_line_alloced(void) { return curbuf->b_ml.ml_flags & ML_LINE_DIRTY; @@ -2489,17 +2461,18 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy) return ml_replace_buf(curbuf, lnum, line, copy); } -// Replace line "lnum", with buffering, in current buffer. -// -// If "copy" is true, make a copy of the line, otherwise the line has been -// copied to allocated memory already. -// If "copy" is false the "line" may be freed to add text properties! -// Do not use it after calling ml_replace(). -// -// Check: The caller of this function should probably also call -// changed_lines(), unless update_screen(NOT_VALID) is used. -// -// return FAIL for failure, OK otherwise +/// Replace line "lnum", with buffering, in current buffer. +/// +/// @param copy if true, make a copy of the line, otherwise the line has been +/// copied to allocated memory already. +/// if false, the "line" may be freed to add text properties! +/// +/// Do not use it after calling ml_replace(). +/// +/// Check: The caller of this function should probably also call +/// changed_lines(), unless update_screen(NOT_VALID) is used. +/// +/// @return FAIL for failure, OK otherwise int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy) { if (line == NULL) { // just checking... @@ -2542,7 +2515,8 @@ int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy) /// deleted_lines() after this. /// /// @param message Show "--No lines in buffer--" message. -/// @return FAIL for failure, OK otherwise +/// +/// @return FAIL for failure, OK otherwise int ml_delete(linenr_T lnum, bool message) { ml_flush_line(curbuf); @@ -2699,9 +2673,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) return OK; } -/* - * set the B_MARKED flag for line 'lnum' - */ +/// set the B_MARKED flag for line 'lnum' void ml_setmarked(linenr_T lnum) { bhdr_T *hp; @@ -2728,9 +2700,7 @@ void ml_setmarked(linenr_T lnum) curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY; } -/* - * find the first line with its B_MARKED flag set - */ +/// find the first line with its B_MARKED flag set linenr_T ml_firstmarked(void) { bhdr_T *hp; @@ -2771,9 +2741,7 @@ linenr_T ml_firstmarked(void) return (linenr_T)0; } -/* - * clear all DB_MARKED flags - */ +/// clear all DB_MARKED flags void ml_clearmarked(void) { bhdr_T *hp; @@ -2822,9 +2790,7 @@ size_t ml_flush_deleted_bytes(buf_T *buf, size_t *codepoints, size_t *codeunits) return ret; } -/* - * flush ml_line if necessary - */ +/// flush ml_line if necessary static void ml_flush_line(buf_T *buf) { bhdr_T *hp; @@ -2921,9 +2887,7 @@ static void ml_flush_line(buf_T *buf) buf->b_ml.ml_line_offset = 0; } -/* - * create a new, empty, data block - */ +/// create a new, empty, data block static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) { assert(page_count >= 0); @@ -2937,9 +2901,7 @@ static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) return hp; } -/* - * create a new, empty, pointer block - */ +/// create a new, empty, pointer block static bhdr_T *ml_new_ptr(memfile_T *mfp) { bhdr_T *hp = mf_new(mfp, false, 1); @@ -2951,21 +2913,19 @@ static bhdr_T *ml_new_ptr(memfile_T *mfp) return hp; } -/* - * lookup line 'lnum' in a memline - * - * action: if ML_DELETE or ML_INSERT the line count is updated while searching - * if ML_FLUSH only flush a locked block - * if ML_FIND just find the line - * - * If the block was found it is locked and put in ml_locked. - * The stack is updated to lead to the locked block. The ip_high field in - * the stack is updated to reflect the last line in the block AFTER the - * insert or delete, also if the pointer block has not been updated yet. But - * if ml_locked != NULL ml_locked_lineadd must be added to ip_high. - * - * return: NULL for failure, pointer to block header otherwise - */ +/// lookup line 'lnum' in a memline +/// +/// @param action: if ML_DELETE or ML_INSERT the line count is updated while searching +/// if ML_FLUSH only flush a locked block +/// if ML_FIND just find the line +/// +/// If the block was found it is locked and put in ml_locked. +/// The stack is updated to lead to the locked block. The ip_high field in +/// the stack is updated to reflect the last line in the block AFTER the +/// insert or delete, also if the pointer block has not been updated yet. But +/// if ml_locked != NULL ml_locked_lineadd must be added to ip_high. +/// +/// @return NULL for failure, pointer to block header otherwise static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) { DATA_BL *dp; @@ -3146,11 +3106,9 @@ error_noblock: return NULL; } -/* - * add an entry to the info pointer stack - * - * return number of the new entry - */ +/// add an entry to the info pointer stack +/// +/// @return number of the new entry static int ml_add_stack(buf_T *buf) { int top = buf->b_ml.ml_stack_top; @@ -3168,16 +3126,14 @@ static int ml_add_stack(buf_T *buf) return top; } -/* - * Update the pointer blocks on the stack for inserted/deleted lines. - * The stack itself is also updated. - * - * When an insert/delete line action fails, the line is not inserted/deleted, - * but the pointer blocks have already been updated. That is fixed here by - * walking through the stack. - * - * Count is the number of lines added, negative if lines have been deleted. - */ +/// Update the pointer blocks on the stack for inserted/deleted lines. +/// The stack itself is also updated. +/// +/// When an insert/delete line action fails, the line is not inserted/deleted, +/// but the pointer blocks have already been updated. That is fixed here by +/// walking through the stack. +/// +/// Count is the number of lines added, negative if lines have been deleted. static void ml_lineadd(buf_T *buf, int count) { int idx; @@ -3204,13 +3160,13 @@ static void ml_lineadd(buf_T *buf, int count) } #if defined(HAVE_READLINK) -/* - * Resolve a symlink in the last component of a file name. - * Note that f_resolve() does it for every part of the path, we don't do that - * here. - * If it worked returns OK and the resolved link in "buf[MAXPATHL]". - * Otherwise returns FAIL. - */ + +/// Resolve a symlink in the last component of a file name. +/// Note that f_resolve() does it for every part of the path, we don't do that +/// here. +/// +/// @return OK if it worked and the resolved link in "buf[MAXPATHL]", +/// FAIL otherwise int resolve_symlink(const char_u *fname, char_u *buf) { char_u tmp[MAXPATHL]; @@ -3274,10 +3230,9 @@ int resolve_symlink(const char_u *fname, char_u *buf) } #endif -/* - * Make swap file name out of the file name and a directory name. - * Returns pointer to allocated memory or NULL. - */ +/// Make swap file name out of the file name and a directory name. +/// +/// @return pointer to allocated memory or NULL. char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name) { char_u *r, *s; @@ -3407,17 +3362,16 @@ static void attention_message(buf_T *buf, char_u *fname) } -/* - * Trigger the SwapExists autocommands. - * Returns a value for equivalent to do_dialog() (see below): - * 0: still need to ask for a choice - * 1: open read-only - * 2: edit anyway - * 3: recover - * 4: delete it - * 5: quit - * 6: abort - */ +/// Trigger the SwapExists autocommands. +/// +/// @return a value for equivalent to do_dialog() (see below): +/// 0: still need to ask for a choice +/// 1: open read-only +/// 2: edit anyway +/// 3: recover +/// 4: delete it +/// 5: quit +/// 6: abort static int do_swapexists(buf_T *buf, char_u *fname) { set_vim_var_string(VV_SWAPNAME, (char *)fname, -1); @@ -3811,10 +3765,8 @@ static bool fnamecmp_ino(char_u *fname_c, char_u *fname_s, long ino_block0) return true; } -/* - * Move a long integer into a four byte character array. - * Used for machine independency in block zero. - */ +/// Move a long integer into a four byte character array. +/// Used for machine independency in block zero. static void long_to_char(long n, char_u *s) { s[0] = (char_u)(n & 0xff); @@ -3841,12 +3793,10 @@ static long char_to_long(char_u *s) return retval; } -/* - * Set the flags in the first block of the swap file: - * - file is modified or not: buf->b_changed - * - 'fileformat' - * - 'fileencoding' - */ +/// Set the flags in the first block of the swap file: +/// - file is modified or not: buf->b_changed +/// - 'fileformat' +/// - 'fileencoding' void ml_setflags(buf_T *buf) { bhdr_T *hp; @@ -3872,13 +3822,13 @@ void ml_setflags(buf_T *buf) #define MLCS_MAXL 800 // max no of lines in chunk #define MLCS_MINL 400 // should be half of MLCS_MAXL -/* - * Keep information for finding byte offset of a line, updtype may be one of: - * ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it - * Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called. - * ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it - * ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity. - */ +/// Keep information for finding byte offset of a line +/// +/// @param updtype may be one of: +/// ML_CHNK_ADDLINE: Add len to parent chunk, possibly splitting it +/// Careful: ML_CHNK_ADDLINE may cause ml_find_line() to be called. +/// ML_CHNK_DELLINE: Subtract len from parent chunk, possibly deleting it +/// ML_CHNK_UPDLINE: Add len to parent chunk, as a signed entity. static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) { static buf_T *ml_upd_lastbuf = NULL; @@ -4081,7 +4031,7 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) /// Should be NULL when getting offset of line /// @param no_ff ignore 'fileformat' option, always use one byte for NL. /// -/// @return -1 if information is not available +/// @return -1 if information is not available long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp, bool no_ff) { linenr_T curline; @@ -4256,10 +4206,11 @@ void goto_byte(long cnt) } /// Increment the line pointer "lp" crossing line boundaries as necessary. -/// Return 1 when going to the next line. -/// Return 2 when moving forward onto a NUL at the end of the line). -/// Return -1 when at the end of file. -/// Return 0 otherwise. +/// +/// @return 1 when going to the next line. +/// 2 when moving forward onto a NUL at the end of the line). +/// -1 when at the end of file. +/// 0 otherwise. int inc(pos_T *lp) { // when searching position may be set to end of a line -- cgit From fcd57980f91ca01227b46de1659e6228115e1278 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 24 Mar 2022 11:14:04 +0000 Subject: chore: add additional compiler flags (#17815) Added: - -Wdouble-promotion - -Wmissing-noreturn - -Wmissing-format-attribute - -Wsuggest-attribute={pure,const,malloc,cold} Resolves: #343 --- src/nvim/CMakeLists.txt | 7 +++++-- src/nvim/eval.c | 19 ++++++------------- src/nvim/ex_docmd.c | 1 + src/nvim/log.c | 1 + src/nvim/main.c | 1 + src/nvim/os/input.c | 1 + src/nvim/os/signal.c | 1 + src/nvim/ui_client.c | 1 + src/nvim/window.c | 2 +- 9 files changed, 18 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8e17f94abc..b7f9292de1 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -182,12 +182,15 @@ foreach(sfile ${CONV_SOURCES}) message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() -# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 -list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES}) if(NOT MSVC) set_source_files_properties( ${CONV_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion") + + # xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 + set_source_files_properties( + ${EXTERNAL_SOURCES} PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-conversion -Wno-missing-noreturn -Wno-missing-format-attribute -Wno-double-promotion") + # gperf generates ANSI-C with incorrect linkage, ignore it. check_c_compiler_flag(-Wstatic-in-inline HAS_WSTATIC_IN_INLINE) if(HAS_WSTATIC_IN_INLINE) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7f937a3137..fbbc543893 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3976,18 +3976,11 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) f1 = f1 * f2; } else if (op == '/') { // Division by zero triggers error from AddressSanitizer - f1 = (f2 == 0 - ? ( + f1 = (f2 == 0 ? ( #ifdef NAN - f1 == 0 - ? NAN - : + f1 == 0 ? (float_T)NAN : #endif - (f1 > 0 - ? INFINITY - : -INFINITY) - ) - : f1 / f2); + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); } else { emsg(_("E804: Cannot use '%' with Float")); return FAIL; @@ -5842,15 +5835,15 @@ size_t string2float(const char *const text, float_T *const ret_value) // MS-Windows does not deal with "inf" and "nan" properly if (STRNICMP(text, "inf", 3) == 0) { - *ret_value = INFINITY; + *ret_value = (float_T)INFINITY; return 3; } if (STRNICMP(text, "-inf", 3) == 0) { - *ret_value = -INFINITY; + *ret_value = (float_T)-INFINITY; return 4; } if (STRNICMP(text, "nan", 3) == 0) { - *ret_value = NAN; + *ret_value = (float_T)NAN; return 3; } *ret_value = strtod(text, &s); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index dab17fc3a4..0dbc9d6b14 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6629,6 +6629,7 @@ static void ex_quit(exarg_T *eap) /// ":cquit". static void ex_cquit(exarg_T *eap) + FUNC_ATTR_NORETURN { // this does not always pass on the exit code to the Manx compiler. why? getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE); diff --git a/src/nvim/log.c b/src/nvim/log.c index 5539e3d6c5..7d50ecf69e 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -282,6 +282,7 @@ static bool do_log_to_file(FILE *log_file, int log_level, const char *context, static bool v_do_log_to_file(FILE *log_file, int log_level, const char *context, const char *func_name, int line_num, bool eol, const char *fmt, va_list args) + FUNC_ATTR_PRINTF(7, 0) { static const char *log_levels[] = { [DEBUG_LOG_LEVEL] = "DEBUG", diff --git a/src/nvim/main.c b/src/nvim/main.c index dec1ae93e7..afb9313cba 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -2099,6 +2099,7 @@ static bool file_owned(const char *fname) /// @param errstr string containing an error message /// @param str string to append to the primary error message, or NULL static void mainerr(const char *errstr, const char *str) + FUNC_ATTR_NORETURN { char *prgname = (char *)path_tail((char_u *)argv0); diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 54cfaee80a..c6c4990334 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -522,6 +522,7 @@ static bool input_ready(MultiQueue *events) // Exit because of an input read error. static void read_error_exit(void) + FUNC_ATTR_NORETURN { if (silent_mode) { // Normal way to exit for "nvim -es". getout(0); diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index a8bf68a1a2..327ab6bc48 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -152,6 +152,7 @@ static char *signal_name(int signum) // NOTE: Avoid unsafe functions, such as allocating memory, they can result in // a deadlock. static void deadly_signal(int signum) + FUNC_ATTR_NORETURN { // Set the v:dying variable. set_vim_var_nr(VV_DYING, 1); diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 6e45a28e89..e723a30d32 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -87,6 +87,7 @@ Object ui_client_handle_redraw(uint64_t channel_id, Array args, Error *error) /// /// This is just a stub. the full version will handle input, resizing, etc void ui_client_execute(uint64_t chan) + FUNC_ATTR_NORETURN { while (true) { loop_poll_events(&main_loop, -1); diff --git a/src/nvim/window.c b/src/nvim/window.c index fb7878c2f4..fa71a08b94 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -836,7 +836,7 @@ void ui_ext_win_position(win_T *wp) FloatConfig c = wp->w_float_config; if (!c.external) { ScreenGrid *grid = &default_grid; - float row = c.row, col = c.col; + Float row = c.row, col = c.col; if (c.relative == kFloatRelativeWindow) { Error dummy = ERROR_INIT; win_T *win = find_window_by_handle(c.window, &dummy); -- cgit From 534f5a419d2ef1c2ad78204a4de48388cc2d7fa2 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 24 Mar 2022 12:17:21 +0100 Subject: refactor: convert function comments to doxygen format (#17710) --- src/nvim/eval/userfunc.c | 183 +++++++++++++++++++---------------------------- src/nvim/event/process.c | 2 +- src/nvim/event/rstream.c | 18 ++--- src/nvim/ex_session.c | 50 +++++++------ src/nvim/extmark.c | 30 ++++---- src/nvim/file_search.c | 101 +++++++++++--------------- src/nvim/fileio.c | 156 +++++++++++++++++++--------------------- 7 files changed, 241 insertions(+), 299 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 471c4092fe..eb5c6e503a 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -201,7 +201,7 @@ static void register_closure(ufunc_T *fp) } -/// Get a name for a lambda. Returned in static memory. +/// @return a name for a lambda. Returned in static memory. char_u *get_lambda_name(void) { static char_u name[30]; @@ -544,7 +544,8 @@ static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf } /// Find a function by name, return pointer to it in ufuncs. -/// @return NULL for unknown function. +/// +/// @return NULL for unknown function. ufunc_T *find_func(const char_u *name) { hashitem_T *hi; @@ -556,11 +557,9 @@ ufunc_T *find_func(const char_u *name) return NULL; } -/* - * Copy the function name of "fp" to buffer "buf". - * "buf" must be able to hold the function name plus three bytes. - * Takes care of script-local function names. - */ +/// Copy the function name of "fp" to buffer "buf". +/// "buf" must be able to hold the function name plus three bytes. +/// Takes care of script-local function names. static void cat_func_name(char_u *buf, ufunc_T *fp) { if (fp->uf_name[0] == K_SPECIAL) { @@ -571,9 +570,7 @@ static void cat_func_name(char_u *buf, ufunc_T *fp) } } -/* - * Add a number variable "name" to dict "dp" with value "nr". - */ +/// Add a number variable "name" to dict "dp" with value "nr". static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) { #ifndef __clang_analyzer__ @@ -586,7 +583,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) v->di_tv.vval.v_number = nr; } -// Free "fc" +/// Free "fc" static void free_funccal(funccall_T *fc) { for (int i = 0; i < fc->fc_funcs.ga_len; i++) { @@ -606,9 +603,9 @@ static void free_funccal(funccall_T *fc) xfree(fc); } -// Free "fc" and what it contains. -// Can be called only when "fc" is kept beyond the period of it called, -// i.e. after cleanup_function_call(fc). +/// Free "fc" and what it contains. +/// Can be called only when "fc" is kept beyond the period of it called, +/// i.e. after cleanup_function_call(fc). static void free_funccal_contents(funccall_T *fc) { // Free all l: variables. @@ -757,7 +754,7 @@ static void func_clear_items(ufunc_T *fp) /// Free all things that a function contains. Does not free the function /// itself, use func_free() for that. /// -/// param[in] force When true, we are exiting. +/// @param[in] force When true, we are exiting. static void func_clear(ufunc_T *fp, bool force) { if (fp->uf_cleared) { @@ -773,7 +770,7 @@ static void func_clear(ufunc_T *fp, bool force) /// Free a function and remove it from the list of functions. Does not free /// what a function contains, call func_clear() first. /// -/// param[in] fp The function to free. +/// @param[in] fp The function to free. static void func_free(ufunc_T *fp) { // only remove it when not done already, otherwise we would remove a newer @@ -786,7 +783,7 @@ static void func_free(ufunc_T *fp) /// Free all things that a function contains and free the function itself. /// -/// param[in] force When true, we are exiting. +/// @param[in] force When true, we are exiting. static void func_clear_free(ufunc_T *fp, bool force) { func_clear(fp, force); @@ -795,13 +792,13 @@ static void func_clear_free(ufunc_T *fp, bool force) /// Call a user function /// -/// @param fp Function to call. -/// @param[in] argcount Number of arguments. -/// @param argvars Arguments. -/// @param[out] rettv Return value. -/// @param[in] firstline First line of range. -/// @param[in] lastline Last line of range. -/// @param selfdict Dictionary for "self" for dictionary functions. +/// @param fp Function to call. +/// @param[in] argcount Number of arguments. +/// @param argvars Arguments. +/// @param[out] rettv Return value. +/// @param[in] firstline First line of range. +/// @param[in] lastline Last line of range. +/// @param selfdict Dictionary for "self" for dictionary functions. void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, linenr_T firstline, linenr_T lastline, dict_T *selfdict) FUNC_ATTR_NONNULL_ARG(1, 3, 4) @@ -1230,8 +1227,8 @@ static bool func_name_refcount(char_u *name) static funccal_entry_T *funccal_stack = NULL; -// Save the current function call pointer, and set it to NULL. -// Used when executing autocommands and for ":source". +/// Save the current function call pointer, and set it to NULL. +/// Used when executing autocommands and for ":source". void save_funccal(funccal_entry_T *entry) { entry->top_funccal = current_funccal; @@ -1384,8 +1381,8 @@ func_call_skip_call: return r; } -// Give an error message for the result of a function. -// Nothing if "error" is FCERR_NONE. +/// Give an error message for the result of a function. +/// Nothing if "error" is FCERR_NONE. static void user_func_error(int error, const char_u *name) FUNC_ATTR_NONNULL_ALL { @@ -1902,9 +1899,7 @@ theend: return name; } -/* - * ":function" - */ +/// ":function" void ex_function(exarg_T *eap) { char_u *theline; @@ -2595,11 +2590,9 @@ ret_free: } } // NOLINT(readability/fn_size) -/* - * Return 5 if "p" starts with "" or "" (ignoring case). - * Return 2 if "p" starts with "s:". - * Return 0 otherwise. - */ +/// @return 5 if "p" starts with "" or "" (ignoring case). +/// 2 if "p" starts with "s:". +/// 0 otherwise. int eval_fname_script(const char *const p) { // Use mb_strnicmp() because in Turkish comparing the "I" may not work with @@ -2625,10 +2618,10 @@ bool translated_function_exists(const char *name) /// Check whether function with the given name exists /// -/// @param[in] name Function name. -/// @param[in] no_deref Whether to dereference a Funcref. +/// @param[in] name Function name. +/// @param[in] no_deref Whether to dereference a Funcref. /// -/// @return True if it exists, false otherwise. +/// @return true if it exists, false otherwise. bool function_exists(const char *const name, bool no_deref) { const char_u *nm = (const char_u *)name; @@ -2651,10 +2644,8 @@ bool function_exists(const char *const name, bool no_deref) return n; } -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * function names. - */ +/// Function given to ExpandGeneric() to obtain the list of user defined +/// function names. char_u *get_user_func_name(expand_T *xp, int idx) { static size_t done; @@ -2771,10 +2762,8 @@ void ex_delfunction(exarg_T *eap) } } -/* - * Unreference a Function: decrement the reference count and free it when it - * becomes zero. - */ +/// Unreference a Function: decrement the reference count and free it when it +/// becomes zero. void func_unref(char_u *name) { ufunc_T *fp = NULL; @@ -2868,9 +2857,7 @@ static int can_free_funccal(funccall_T *fc, int copyID) && fc->fc_copyID != copyID; } -/* - * ":return [expr]" - */ +/// ":return [expr]" void ex_return(exarg_T *eap) { char_u *arg = eap->arg; @@ -2921,9 +2908,7 @@ void ex_return(exarg_T *eap) // TODO(ZyX-I): move to eval/ex_cmds -/* - * ":1,25call func(arg1, arg2)" function call. - */ +/// ":1,25call func(arg1, arg2)" function call. void ex_call(exarg_T *eap) { char_u *arg = eap->arg; @@ -3050,14 +3035,16 @@ end: xfree(tofree); } -/* - * Return from a function. Possibly makes the return pending. Also called - * for a pending return at the ":endtry" or after returning from an extra - * do_cmdline(). "reanimate" is used in the latter case. "is_cmd" is set - * when called due to a ":return" command. "rettv" may point to a typval_T - * with the return rettv. Returns TRUE when the return can be carried out, - * FALSE when the return gets pending. - */ +/// Return from a function. Possibly makes the return pending. Also called +/// for a pending return at the ":endtry" or after returning from an extra +/// do_cmdline(). "reanimate" is used in the latter case. +/// +/// @param reanimate used after returning from an extra do_cmdline(). +/// @param is_cmd set when called due to a ":return" command. +/// @param rettv may point to a typval_T with the return rettv. +/// +/// @return TRUE when the return can be carried out, +/// FALSE when the return gets pending. int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) { int idx; @@ -3068,12 +3055,10 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) current_funccal->returned = false; } - // // Cleanup (and deactivate) conditionals, but stop when a try conditional // not in its finally clause (which then is to be executed next) is found. // In this case, make the ":return" pending for execution at the ":endtry". // Otherwise, return normally. - // idx = cleanup_conditionals(eap->cstack, 0, true); if (idx >= 0) { cstack->cs_pending[idx] = CSTP_RETURN; @@ -3126,10 +3111,8 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) return idx < 0; } -/* - * Generate a return command for producing the value of "rettv". The result - * is an allocated string. Used by report_pending() for verbose messages. - */ +/// Generate a return command for producing the value of "rettv". The result +/// is an allocated string. Used by report_pending() for verbose messages. char_u *get_return_cmd(void *rettv) { char_u *s = NULL; @@ -3151,11 +3134,10 @@ char_u *get_return_cmd(void *rettv) return vim_strsave(IObuff); } -/* - * Get next function line. - * Called by do_cmdline() to get the next line. - * Returns allocated string, or NULL for end of function. - */ +/// Get next function line. +/// Called by do_cmdline() to get the next line. +/// +/// @return allocated string, or NULL for end of function. char_u *get_func_line(int c, void *cookie, int indent, bool do_concat) { funccall_T *fcp = (funccall_T *)cookie; @@ -3206,10 +3188,8 @@ char_u *get_func_line(int c, void *cookie, int indent, bool do_concat) return retval; } -/* - * Return TRUE if the currently active function should be ended, because a - * return was encountered or an error occurred. Used inside a ":while". - */ +/// @return TRUE if the currently active function should be ended, because a +/// return was encountered or an error occurred. Used inside a ":while". int func_has_ended(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -3220,9 +3200,7 @@ int func_has_ended(void *cookie) || fcp->returned; } -/* - * return TRUE if cookie indicates a function which "abort"s on errors. - */ +/// @return TRUE if cookie indicates a function which "abort"s on errors. int func_has_abort(void *cookie) { return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT; @@ -3289,41 +3267,31 @@ void make_partial(dict_T *const selfdict, typval_T *const rettv) } } -/* - * Return the name of the executed function. - */ +/// @return the name of the executed function. char_u *func_name(void *cookie) { return ((funccall_T *)cookie)->func->uf_name; } -/* - * Return the address holding the next breakpoint line for a funccall cookie. - */ +/// @return the address holding the next breakpoint line for a funccall cookie. linenr_T *func_breakpoint(void *cookie) { return &((funccall_T *)cookie)->breakpoint; } -/* - * Return the address holding the debug tick for a funccall cookie. - */ +/// @return the address holding the debug tick for a funccall cookie. int *func_dbg_tick(void *cookie) { return &((funccall_T *)cookie)->dbg_tick; } -/* - * Return the nesting level for a funccall cookie. - */ +/// @return the nesting level for a funccall cookie. int func_level(void *cookie) { return ((funccall_T *)cookie)->level; } -/* - * Return TRUE when a function was ended by a ":return" command. - */ +/// @return TRUE when a function was ended by a ":return" command. int current_func_returned(void) { return current_funccal->returned; @@ -3372,8 +3340,8 @@ funccall_T *get_funccal(void) return funccal; } -/// Return the hashtable used for local variables in the current funccal. -/// Return NULL if there is no current funccal. +/// @return hashtable used for local variables in the current funccal or +/// NULL if there is no current funccal. hashtab_T *get_funccal_local_ht(void) { if (current_funccal == NULL) { @@ -3382,8 +3350,8 @@ hashtab_T *get_funccal_local_ht(void) return &get_funccal()->l_vars.dv_hashtab; } -/// Return the l: scope variable. -/// Return NULL if there is no current funccal. +/// @return the l: scope variable or +/// NULL if there is no current funccal. dictitem_T *get_funccal_local_var(void) { if (current_funccal == NULL) { @@ -3392,8 +3360,8 @@ dictitem_T *get_funccal_local_var(void) return (dictitem_T *)&get_funccal()->l_vars_var; } -/// Return the hashtable used for argument in the current funccal. -/// Return NULL if there is no current funccal. +/// @return the hashtable used for argument in the current funccal or +/// NULL if there is no current funccal. hashtab_T *get_funccal_args_ht(void) { if (current_funccal == NULL) { @@ -3402,8 +3370,8 @@ hashtab_T *get_funccal_args_ht(void) return &get_funccal()->l_avars.dv_hashtab; } -/// Return the a: scope variable. -/// Return NULL if there is no current funccal. +/// @return the a: scope variable or +/// NULL if there is no current funccal. dictitem_T *get_funccal_args_var(void) { if (current_funccal == NULL) { @@ -3412,9 +3380,7 @@ dictitem_T *get_funccal_args_var(void) return (dictitem_T *)¤t_funccal->l_avars_var; } -/* - * List function variables, if there is a function. - */ +/// List function variables, if there is a function. void list_func_vars(int *first) { if (current_funccal != NULL) { @@ -3423,9 +3389,8 @@ void list_func_vars(int *first) } } -/// If "ht" is the hashtable for local variables in the current funccal, return -/// the dict that contains it. -/// Otherwise return NULL. +/// @return if "ht" is the hashtable for local variables in the current +/// funccal, return the dict that contains it. Otherwise return NULL. dict_T *get_current_funccal_dict(hashtab_T *ht) { if (current_funccal != NULL && ht == ¤t_funccal->l_vars.dv_hashtab) { @@ -3589,7 +3554,7 @@ bool set_ref_in_func_args(int copyID) /// "list_stack" is used to add lists to be marked. Can be NULL. /// "ht_stack" is used to add hashtabs to be marked. Can be NULL. /// -/// @return true if setting references failed somehow. +/// @return true if setting references failed somehow. bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) { ufunc_T *fp = fp_in; diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index dae4dad16d..653fffae1c 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -237,7 +237,7 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL KILL_TIMEOUT_MS, 0); } -// Frees process-owned resources. +/// Frees process-owned resources. void process_free(Process *proc) FUNC_ATTR_NONNULL_ALL { if (proc->argv != NULL) { diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index e51689543f..26b5ce3b75 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -85,7 +85,7 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) // Callbacks used by libuv -// Called by libuv to allocate memory for reading. +/// Called by libuv to allocate memory for reading. static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { Stream *stream = handle->data; @@ -95,9 +95,9 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) buf->len = UV_BUF_LEN(write_count); } -// Callback invoked by libuv after it copies the data into the buffer provided -// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a -// 0-length buffer. +/// Callback invoked by libuv after it copies the data into the buffer provided +/// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a +/// 0-length buffer. static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) { Stream *stream = uvstream->data; @@ -134,11 +134,11 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) invoke_read_cb(stream, nread, false); } -// Called by the by the 'idle' handle to emulate a reading event -// -// Idle callbacks are invoked once per event loop: -// - to perform some very low priority activity. -// - to keep the loop "alive" (so there is always an event to process) +/// Called by the by the 'idle' handle to emulate a reading event +/// +/// Idle callbacks are invoked once per event loop: +/// - to perform some very low priority activity. +/// - to keep the loop "alive" (so there is always an event to process) static void fread_idle_cb(uv_idle_t *handle) { uv_fs_t req; diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 0e2b7c0ece..39ff75d8c2 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -98,10 +98,11 @@ static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin) return OK; } -// Write commands to "fd" to recursively create windows for frame "fr", -// horizontally and vertically split. -// After the commands the last window in the frame is the current window. -// Returns FAIL when writing the commands to "fd" fails. +/// Write commands to "fd" to recursively create windows for frame "fr", +/// horizontally and vertically split. +/// After the commands the last window in the frame is the current window. +/// +/// @return FAIL when writing the commands to "fd" fails. static int ses_win_rec(FILE *fd, frame_T *fr) { frame_T *frc; @@ -144,8 +145,9 @@ static int ses_win_rec(FILE *fd, frame_T *fr) return OK; } -// Skip frames that don't contain windows we want to save in the Session. -// Returns NULL when there none. +/// Skip frames that don't contain windows we want to save in the Session. +/// +/// @return NULL when there none. static frame_T *ses_skipframe(frame_T *fr) { frame_T *frc; @@ -158,8 +160,8 @@ static frame_T *ses_skipframe(frame_T *fr) return frc; } -// Return true if frame "fr" has a window somewhere that we want to save in -// the Session. +/// @return true if frame "fr" has a window somewhere that we want to save in +/// the Session. static bool ses_do_frame(const frame_T *fr) FUNC_ATTR_NONNULL_ARG(1) { @@ -176,7 +178,7 @@ static bool ses_do_frame(const frame_T *fr) return false; } -/// Return non-zero if window "wp" is to be stored in the Session. +/// @return non-zero if window "wp" is to be stored in the Session. static int ses_do_win(win_T *wp) { if (wp->w_buffer->b_fname == NULL @@ -229,7 +231,7 @@ static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigne return OK; } -/// Gets the buffer name for `buf`. +/// @return the buffer name for `buf`. static char *ses_get_fname(buf_T *buf, unsigned *flagp) { // Use the short file name if the current directory is known at the time @@ -249,7 +251,8 @@ static char *ses_get_fname(buf_T *buf, unsigned *flagp) /// Write a buffer name to the session file. /// Also ends the line, if "add_eol" is true. -/// Returns FAIL if writing fails. +/// +/// @return FAIL if writing fails. static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol) { char *name = ses_get_fname(buf, flagp); @@ -260,11 +263,11 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol) return OK; } -// Escapes a filename for session writing. -// Takes care of "slash" flag in 'sessionoptions' and escapes special -// characters. -// -// Returns allocated string or NULL. +/// Escapes a filename for session writing. +/// Takes care of "slash" flag in 'sessionoptions' and escapes special +/// characters. +/// +/// @return allocated string or NULL. static char *ses_escape_fname(char *name, unsigned *flagp) { char *p; @@ -283,10 +286,11 @@ static char *ses_escape_fname(char *name, unsigned *flagp) return p; } -// Write a file name to the session file. -// Takes care of the "slash" option in 'sessionoptions' and escapes special -// characters. -// Returns FAIL if writing fails. +/// Write a file name to the session file. +/// Takes care of the "slash" option in 'sessionoptions' and escapes special +/// characters. +/// +/// @return FAIL if writing fails. static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) { char *p = ses_escape_fname((char *)name, flagp); @@ -1052,7 +1056,7 @@ void ex_mkrc(exarg_T *eap) xfree(viewFile); } -/// Get the name of the view file for the current buffer. +/// @return the name of the view file for the current buffer. static char *get_view_file(int c) { if (curbuf->b_ffname == NULL) { @@ -1100,7 +1104,7 @@ static char *get_view_file(int c) return retval; } -// TODO(justinmk): remove this, not needed after 5ba3cecb68cd. +/// TODO(justinmk): remove this, not needed after 5ba3cecb68cd. int put_eol(FILE *fd) { if (putc('\n', fd) < 0) { @@ -1109,7 +1113,7 @@ int put_eol(FILE *fd) return OK; } -// TODO(justinmk): remove this, not needed after 5ba3cecb68cd. +/// TODO(justinmk): remove this, not needed after 5ba3cecb68cd. int put_line(FILE *fd, char *s) { if (fprintf(fd, "%s\n", s) < 0) { diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 6b879f5139..a3fcc7d784 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -175,8 +175,9 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) return true; } -// Remove an extmark -// Returns 0 on missing id +/// Remove an extmark +/// +/// @return 0 on missing id bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id) { MarkTreeIter itr[1] = { 0 }; @@ -203,8 +204,8 @@ bool extmark_del(buf_T *buf, uint32_t ns_id, uint32_t id) return true; } -// Free extmarks in a ns between lines -// if ns = 0, it means clear all namespaces +/// Free extmarks in a ns between lines +/// if ns = 0, it means clear all namespaces bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col) { if (!map_size(buf->b_extmark_ns)) { @@ -287,12 +288,13 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r return marks_cleared; } -// Returns the position of marks between a range, -// marks found at the start or end index will be included, -// if upper_lnum or upper_col are negative the buffer -// will be searched to the start, or end -// dir can be set to control the order of the array -// amount = amount of marks to find or -1 for all +/// @return the position of marks between a range, +/// marks found at the start or end index will be included. +/// +/// if upper_lnum or upper_col are negative the buffer +/// will be searched to the start, or end +/// dir can be set to control the order of the array +/// amount = amount of marks to find or -1 for all ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row, colnr_T u_col, int64_t amount, bool reverse) { @@ -334,7 +336,7 @@ next_mark: return array; } -// Lookup an extmark by id +/// Lookup an extmark by id ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) { ExtmarkInfo ret = { 0, 0, -1, -1, -1, -1, false, false, DECORATION_INIT }; @@ -359,7 +361,7 @@ ExtmarkInfo extmark_from_id(buf_T *buf, uint32_t ns_id, uint32_t id) } -// free extmarks from the buffer +/// free extmarks from the buffer void extmark_free_all(buf_T *buf) { if (!map_size(buf->b_extmark_ns)) { @@ -389,7 +391,7 @@ void extmark_free_all(buf_T *buf) } -// Save info for undo/redo of set marks +/// Save info for undo/redo of set marks static void u_extmark_set(buf_T *buf, uint64_t mark, int row, colnr_T col) { u_header_T *uhp = u_force_get_undo_header(buf); @@ -499,7 +501,7 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) } -// Adjust extmark row for inserted/deleted rows (columns stay fixed). +/// Adjust extmark row for inserted/deleted rows (columns stay fixed). void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, long amount, long amount_after, ExtmarkOp undo) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index d0f7a91d6c..15f1d3d065 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -529,9 +529,7 @@ error_return: return NULL; } -/* - * Get the stopdir string. Check that ';' is not escaped. - */ +/// @return the stopdir string. Check that ';' is not escaped. char_u *vim_findfile_stopdir(char_u *buf) { char_u *r_ptr = buf; @@ -554,9 +552,7 @@ char_u *vim_findfile_stopdir(char_u *buf) return r_ptr; } -/* - * Clean up the given search context. Can handle a NULL pointer. - */ +/// Clean up the given search context. Can handle a NULL pointer. void vim_findfile_cleanup(void *ctx) { if (ctx == NULL) { @@ -568,18 +564,19 @@ void vim_findfile_cleanup(void *ctx) xfree(ctx); } -/* - * Find a file in a search context. - * The search context was created with vim_findfile_init() above. - * Return a pointer to an allocated file name or NULL if nothing found. - * To get all matching files call this function until you get NULL. - * - * If the passed search_context is NULL, NULL is returned. - * - * The search algorithm is depth first. To change this replace the - * stack with a list (don't forget to leave partly searched directories on the - * top of the list). - */ +/// Find a file in a search context. +/// The search context was created with vim_findfile_init() above. +/// +/// To get all matching files call this function until you get NULL. +/// +/// If the passed search_context is NULL, NULL is returned. +/// +/// The search algorithm is depth first. To change this replace the +/// stack with a list (don't forget to leave partly searched directories on the +/// top of the list). +/// +/// @return a pointer to an allocated file name or, +/// NULL if nothing found. char_u *vim_findfile(void *search_ctx_arg) { char_u *file_path; @@ -999,10 +996,8 @@ fail: return NULL; } -/* - * Free the list of lists of visited files and directories - * Can handle it if the passed search_context is NULL; - */ +/// Free the list of lists of visited files and directories +/// Can handle it if the passed search_context is NULL; void vim_findfile_free_visited(void *search_ctx_arg) { ff_search_ctx_T *search_ctx; @@ -1044,10 +1039,8 @@ static void ff_free_visited_list(ff_visited_T *vl) vl = NULL; } -/* - * Returns the already visited list for the given filename. If none is found it - * allocates a new one. - */ +/// @return the already visited list for the given filename. If none is found it +/// allocates a new one. static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, ff_visited_list_hdr_T **list_headp) { @@ -1094,13 +1087,13 @@ static ff_visited_list_hdr_T *ff_get_visited_list(char_u *filename, return retptr; } -// Check if two wildcard paths are equal. -// They are equal if: -// - both paths are NULL -// - they have the same length -// - char by char comparison is OK -// - the only differences are in the counters behind a '**', so -// '**\20' is equal to '**\24' +/// Check if two wildcard paths are equal. +/// They are equal if: +/// - both paths are NULL +/// - they have the same length +/// - char by char comparison is OK +/// - the only differences are in the counters behind a '**', so +/// '**\20' is equal to '**\24' static bool ff_wc_equal(char_u *s1, char_u *s2) { int i, j; @@ -1134,11 +1127,10 @@ static bool ff_wc_equal(char_u *s1, char_u *s2) return s1[i] == s2[j]; } -/* - * maintains the list of already visited files and dirs - * returns FAIL if the given file/dir is already in the list - * returns OK if it is newly added - */ +/// maintains the list of already visited files and dirs +/// +/// @return FAIL if the given file/dir is already in the list or, +/// OK if it is newly added static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) { ff_visited_T *vp; @@ -1196,9 +1188,7 @@ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u * return OK; } -/* - * create stack element from given path pieces - */ +/// create stack element from given path pieces static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, int level, int star_star_empty) { @@ -1226,9 +1216,7 @@ static ff_stack_T *ff_create_stack_element(char_u *fix_part, char_u *wc_part, in return new; } -/* - * Push a dir on the directory stack. - */ +/// Push a dir on the directory stack. static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr) { /* check for NULL pointer, not to return an error to the user, but @@ -1239,10 +1227,9 @@ static void ff_push(ff_search_ctx_T *search_ctx, ff_stack_T *stack_ptr) } } -/* - * Pop a dir from the directory stack. - * Returns NULL if stack is empty. - */ +/// Pop a dir from the directory stack. +/// +/// @return NULL if stack is empty. static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx) { ff_stack_T *sptr; @@ -1255,9 +1242,7 @@ static ff_stack_T *ff_pop(ff_search_ctx_T *search_ctx) return sptr; } -/* - * free the given stack element - */ +/// free the given stack element static void ff_free_stack_element(ff_stack_T *const stack_ptr) { if (stack_ptr == NULL) { @@ -1275,9 +1260,7 @@ static void ff_free_stack_element(ff_stack_T *const stack_ptr) xfree(stack_ptr); } -/* - * Clear the search context, but NOT the visited list. - */ +/// Clear the search context, but NOT the visited list. static void ff_clear(ff_search_ctx_T *search_ctx) { ff_stack_T *sptr; @@ -1311,10 +1294,9 @@ static void ff_clear(ff_search_ctx_T *search_ctx) search_ctx->ffsc_level = 0; } -/* - * check if the given path is in the stopdirs - * returns TRUE if yes else FALSE - */ +/// check if the given path is in the stopdirs +/// +/// @return TRUE if yes else FALSE static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v) { int i = 0; @@ -1670,7 +1652,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause, bool pre /// Change to a file's directory. /// Caller must call shorten_fnames()! -/// @return OK or FAIL +/// +/// @return OK or FAIL int vim_chdirfile(char_u *fname, CdCause cause) { char dir[MAXPATHL]; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 41e67e5b3b..fe61a2fc90 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2033,9 +2033,7 @@ void prep_exarg(exarg_T *eap, const buf_T *buf) eap->forceit = FALSE; } -/* - * Set default or forced 'fileformat' and 'binary'. - */ +/// Set default or forced 'fileformat' and 'binary'. void set_file_options(int set_options, exarg_T *eap) { // set default 'fileformat' @@ -2056,9 +2054,7 @@ void set_file_options(int set_options, exarg_T *eap) } } -/* - * Set forced 'fileencoding'. - */ +/// Set forced 'fileencoding'. void set_forced_fenc(exarg_T *eap) { if (eap->force_enc != 0) { @@ -2068,12 +2064,12 @@ void set_forced_fenc(exarg_T *eap) } } -// Find next fileencoding to use from 'fileencodings'. -// "pp" points to fenc_next. It's advanced to the next item. -// When there are no more items, an empty string is returned and *pp is set to -// NULL. -// When *pp is not set to NULL, the result is in allocated memory and "alloced" -// is set to true. +/// Find next fileencoding to use from 'fileencodings'. +/// "pp" points to fenc_next. It's advanced to the next item. +/// When there are no more items, an empty string is returned and *pp is set to +/// NULL. +/// When *pp is not set to NULL, the result is in allocated memory and "alloced" +/// is set to true. static char_u *next_fenc(char_u **pp, bool *alloced) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET { @@ -2149,10 +2145,8 @@ static char_u *readfile_charconvert(char_u *fname, char_u *fenc, int *fdp) } -/* - * Read marks for the current buffer from the ShaDa file, when we support - * buffer marks and the buffer has a name. - */ +/// Read marks for the current buffer from the ShaDa file, when we support +/// buffer marks and the buffer has a name. static void check_marks_read(void) { if (!curbuf->b_marks_read && get_shada_parameter('\'') > 0 @@ -3761,10 +3755,8 @@ nofail: #undef SET_ERRMSG_NUM } -/* - * Set the name of the current buffer. Use when the buffer doesn't have a - * name and a ":r" or ":w" command with a file name is used. - */ +/// Set the name of the current buffer. Use when the buffer doesn't have a +/// name and a ":r" or ":w" command with a file name is used. static int set_rw_fname(char_u *fname, char_u *sfname) { buf_T *buf = curbuf; @@ -3854,9 +3846,7 @@ static bool msg_add_fileformat(int eol_type) return false; } -/* - * Append line and character count to IObuff. - */ +/// Append line and character count to IObuff. void msg_add_lines(int insert_space, long lnum, off_T nchars) { char_u *p; @@ -3880,20 +3870,16 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars) } } -/* - * Append message for missing line separator to IObuff. - */ +/// Append message for missing line separator to IObuff. static void msg_add_eol(void) { STRCAT(IObuff, shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]")); } -/* - * Check modification time of file, before writing to it. - * The size isn't checked, because using a tool like "gzip" takes care of - * using the same timestamp but can't set the size. - */ +/// Check modification time of file, before writing to it. +/// The size isn't checked, because using a tool like "gzip" takes care of +/// using the same timestamp but can't set the size. static int check_mtime(buf_T *buf, FileInfo *file_info) { if (buf->b_mtime_read != 0 @@ -3925,12 +3911,10 @@ static bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) F #endif } -/* - * Call write() to write a number of bytes to the file. - * Handles 'encoding' conversion. - * - * Return FAIL for failure, OK otherwise. - */ +/// Call write() to write a number of bytes to the file. +/// Handles 'encoding' conversion. +/// +/// @return FAIL for failure, OK otherwise. static int buf_write_bytes(struct bw_info *ip) { int wlen; @@ -4258,12 +4242,11 @@ static int get_fio_flags(const char_u *name) } -/* - * Check for a Unicode BOM (Byte Order Mark) at the start of p[size]. - * "size" must be at least 2. - * Return the name of the encoding and set "*lenp" to the length. - * Returns NULL when no BOM found. - */ +/// Check for a Unicode BOM (Byte Order Mark) at the start of p[size]. +/// "size" must be at least 2. +/// +/// @return the name of the encoding and set "*lenp" to the length or, +/// NULL when no BOM found. static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) { char *name = NULL; @@ -4304,10 +4287,9 @@ static char_u *check_for_bom(char_u *p, long size, int *lenp, int flags) return (char_u *)name; } -/* - * Generate a BOM in "buf[4]" for encoding "name". - * Return the length of the BOM (zero when no BOM). - */ +/// Generate a BOM in "buf[4]" for encoding "name". +/// +/// @return the length of the BOM (zero when no BOM). static int make_bom(char_u *buf, char_u *name) { int flags; @@ -4332,10 +4314,12 @@ static int make_bom(char_u *buf, char_u *name) } /// Shorten filename of a buffer. -/// When "force" is TRUE: Use full path from now on for files currently being -/// edited, both for file name and swap file name. Try to shorten the file -/// names a bit, if safe to do so. -/// When "force" is FALSE: Only try to shorten absolute file names. +/// +/// @param force when TRUE: Use full path from now on for files currently being +/// edited, both for file name and swap file name. Try to shorten the file +/// names a bit, if safe to do so. +/// when FALSE: Only try to shorten absolute file names. +/// /// For buffers that have buftype "nofile" or "scratch": never change the file /// name. void shorten_buf_fname(buf_T *buf, char_u *dirname, int force) @@ -4513,7 +4497,8 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL } /// Read 2 bytes from "fd" and turn them into an int, MSB first. -/// Returns -1 when encountering EOF. +/// +/// @return -1 when encountering EOF. int get2c(FILE *fd) { const int n = getc(fd); @@ -4528,7 +4513,8 @@ int get2c(FILE *fd) } /// Read 3 bytes from "fd" and turn them into an int, MSB first. -/// Returns -1 when encountering EOF. +/// +/// @return -1 when encountering EOF. int get3c(FILE *fd) { int n = getc(fd); @@ -4548,7 +4534,8 @@ int get3c(FILE *fd) } /// Read 4 bytes from "fd" and turn them into an int, MSB first. -/// Returns -1 when encountering EOF. +/// +/// @return -1 when encountering EOF. int get4c(FILE *fd) { // Use unsigned rather than int otherwise result is undefined @@ -4579,7 +4566,8 @@ int get4c(FILE *fd) } /// Read 8 bytes from `fd` and turn them into a time_t, MSB first. -/// Returns -1 when encountering EOF. +/// +/// @return -1 when encountering EOF. time_t get8ctime(FILE *fd) { time_t n = 0; @@ -4595,7 +4583,8 @@ time_t get8ctime(FILE *fd) } /// Reads a string of length "cnt" from "fd" into allocated memory. -/// @return pointer to the string or NULL when unable to read that many bytes. +/// +/// @return pointer to the string or NULL when unable to read that many bytes. char *read_string(FILE *fd, size_t cnt) { char *str = xmallocz(cnt); @@ -4611,7 +4600,8 @@ char *read_string(FILE *fd, size_t cnt) } /// Writes a number to file "fd", most significant bit first, in "len" bytes. -/// @returns false in case of an error. +/// +/// @return false in case of an error. bool put_bytes(FILE *fd, uintmax_t number, size_t len) { assert(len > 0); @@ -4624,7 +4614,8 @@ bool put_bytes(FILE *fd, uintmax_t number, size_t len) } /// Writes time_t to file "fd" in 8 bytes. -/// @returns FAIL when the write failed. +/// +/// @return FAIL when the write failed. int put_time(FILE *fd, time_t time_) { uint8_t buf[8]; @@ -4635,7 +4626,7 @@ int put_time(FILE *fd, time_t time_) /// os_rename() only works if both files are on the same file system, this /// function will (attempts to?) copy the file across if rename fails -- webb /// -/// @return -1 for failure, 0 for success +/// @return -1 for failure, 0 for success int vim_rename(const char_u *from, const char_u *to) FUNC_ATTR_NONNULL_ALL { @@ -4860,11 +4851,10 @@ int check_timestamps(int focus) return didit; } -/* - * Move all the lines from buffer "frombuf" to buffer "tobuf". - * Return OK or FAIL. When FAIL "tobuf" is incomplete and/or "frombuf" is not - * empty. - */ +/// Move all the lines from buffer "frombuf" to buffer "tobuf". +/// +/// @return OK or FAIL. +/// When FAIL "tobuf" is incomplete and/or "frombuf" is not empty. static int move_lines(buf_T *frombuf, buf_T *tobuf) { buf_T *tbuf = curbuf; @@ -4903,9 +4893,10 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) /// Check if buffer "buf" has been changed. /// Also check if the file for a new buffer unexpectedly appeared. -/// return 1 if a changed buffer was found. -/// return 2 if a message has been displayed. -/// return 0 otherwise. +/// +/// @return 1 if a changed buffer was found or, +/// 2 if a message has been displayed or, +/// 0 otherwise. int buf_check_timestamp(buf_T *buf) FUNC_ATTR_NONNULL_ALL { @@ -5280,10 +5271,8 @@ void buf_store_file_info(buf_T *buf, FileInfo *file_info) buf->b_orig_mode = (int)file_info->stat.st_mode; } -/* - * Adjust the line with missing eol, used for the next write. - * Used for do_filter(), when the input lines for the filter are deleted. - */ +/// Adjust the line with missing eol, used for the next write. +/// Used for do_filter(), when the input lines for the filter are deleted. void write_lnum_adjust(linenr_T offset) { if (curbuf->b_no_eol_lnum != 0) { // only if there is a missing eol @@ -5355,8 +5344,10 @@ static void vim_maketempdir(void) } /// Delete "name" and everything in it, recursively. +/// /// @param name The path which should be deleted. -/// @return 0 for success, -1 if some file was not deleted. +/// +/// @return 0 for success, -1 if some file was not deleted. int delete_recursive(const char *name) { int result = 0; @@ -5400,8 +5391,8 @@ void vim_deltempdir(void) } } -/// Get the name of temp directory. This directory would be created on the first -/// call to this function. +/// @return the name of temp directory. This directory would be created on the first +/// call to this function. char_u *vim_gettempdir(void) { if (vim_tempdir == NULL) { @@ -5435,8 +5426,8 @@ static bool vim_settempdir(char *tempdir) /// /// @note The temp file is NOT created. /// -/// @return pointer to the temp file name or NULL if Neovim can't create -/// temporary directory for its own temporary files. +/// @return pointer to the temp file name or NULL if Neovim can't create +/// temporary directory for its own temporary files. char_u *vim_tempname(void) { // Temp filename counter. @@ -5747,10 +5738,9 @@ char_u *file_pat_to_reg_pat(const char_u *pat, const char_u *pat_end, char *allo } #if defined(EINTR) -/* - * Version of read() that retries when interrupted by EINTR (possibly - * by a SIGWINCH). - */ + +/// Version of read() that retries when interrupted by EINTR (possibly +/// by a SIGWINCH). long read_eintr(int fd, void *buf, size_t bufsize) { long ret; @@ -5764,10 +5754,8 @@ long read_eintr(int fd, void *buf, size_t bufsize) return ret; } -/* - * Version of write() that retries when interrupted by EINTR (possibly - * by a SIGWINCH). - */ +/// Version of write() that retries when interrupted by EINTR (possibly +/// by a SIGWINCH). long write_eintr(int fd, void *buf, size_t bufsize) { long ret = 0; -- cgit From 4a11c7e56fa23c92f5ddd09007a7838dcfca9b02 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Mar 2022 19:40:00 +0800 Subject: chore(nvim_paste): assert the correct String (#17752) --- src/nvim/api/vim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index bdeac1a9f4..7c7ada55a2 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1328,7 +1328,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) if (!cancel && !(State & CMDLINE)) { // Dot-repeat. for (size_t i = 0; i < lines.size; i++) { String s = lines.items[i].data.string; - assert(data.size <= INT_MAX); + assert(s.size <= INT_MAX); AppendToRedobuffLit((char_u *)s.data, (int)s.size); // readfile()-style: "\n" is indicated by presence of N+1 item. if (i + 1 < lines.size) { -- cgit From a11ff555557ada858d74d8192badb725d77fdbb0 Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Fri, 18 Mar 2022 19:35:07 -0600 Subject: feat(tui): enable CSI u keys On TUI startup write the CSI sequence that signals to the controlling terminal that Neovim supports the CSI u scheme for encoding modifiers documented in [1]. This is similar to, but distinct from, Vim's usage of the `t_TI` and `t_TE` variables to handle Xterm's `modifyOtherKeys` setting. For a longer explanation on those differences see [2]. Since Neovim uses libtermkey for key input handling, we use the CSI u encoding rather than Xterm's modifyOtherKeys encoding. [1]: http://www.leonerd.org.uk/hacks/fixterms/ [2]: https://invisible-island.net/xterm/modified-keys.html --- src/nvim/tui/tui.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 79a6b9ed0e..bec55174c4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -131,6 +131,7 @@ typedef struct { int get_bg; int set_underline_style; int set_underline_color; + int enable_extended_keys, disable_extended_keys; } unibi_ext; char *space_buf; } TUIData; @@ -168,7 +169,7 @@ UI *tui_start(void) ui->set_title = tui_set_title; ui->set_icon = tui_set_icon; ui->screenshot = tui_screenshot; - ui->option_set= tui_option_set; + ui->option_set = tui_option_set; ui->raw_line = tui_raw_line; memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); @@ -308,6 +309,10 @@ static void terminfo_start(UI *ui) // Enable bracketed paste unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste); + // Enable extended keys (also known as 'modifyOtherKeys' or CSI u). On terminals that don't + // support this, this sequence is ignored. + unibi_out_ext(ui, data->unibi_ext.enable_extended_keys); + int ret; uv_loop_init(&data->write_loop); if (data->out_isatty) { @@ -365,6 +370,8 @@ static void terminfo_stop(UI *ui) unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste); // Disable focus reporting unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting); + // Disable extended keys + unibi_out_ext(ui, data->unibi_ext.disable_extended_keys); flush_buf(ui); uv_tty_reset_mode(); uv_close((uv_handle_t *)&data->output_handle, NULL); @@ -1378,7 +1385,6 @@ static void tui_screenshot(UI *ui, String path) fclose(f); } - static void tui_option_set(UI *ui, String name, Object value) { TUIData *data = ui->data; @@ -1387,11 +1393,9 @@ static void tui_option_set(UI *ui, String name, Object value) data->print_attr_id = -1; invalidate(ui, 0, data->grid.height, 0, data->grid.width); - } - if (strequal(name.data, "ttimeout")) { + } else if (strequal(name.data, "ttimeout")) { data->input.ttimeout = value.data.boolean; - } - if (strequal(name.data, "ttimeoutlen")) { + } else if (strequal(name.data, "ttimeoutlen")) { data->input.ttimeoutlen = (long)value.data.integer; } } @@ -1944,6 +1948,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, || terminfo_is_term_family(term, "iTerm.app") || terminfo_is_term_family(term, "iTerm2.app"); bool alacritty = terminfo_is_term_family(term, "alacritty"); + bool kitty = terminfo_is_term_family(term, "xterm-kitty"); // None of the following work over SSH; see :help TERM . bool iterm_pretending_xterm = xterm && iterm_env; @@ -2067,6 +2072,15 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, data->unibi_ext.set_underline_color = (int)unibi_add_ext_str(ut, "ext.set_underline_color", "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); } + + if (!kitty) { + // Kitty does not support these sequences; it only supports it's own CSI > 1 u which enables the + // Kitty keyboard protocol + data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", + "\x1b[>4;1m"); + data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", + "\x1b[>4;0m"); + } } static void flush_buf(UI *ui) -- cgit From 93e0d9b5560b6c37973c93785693e8e9a8a30567 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Mon, 21 Mar 2022 18:38:19 +0100 Subject: fix(PVS/V1019): compound assignment expression is used inside condition --- src/nvim/ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index fc66fb5f06..4404ee7b80 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3893,7 +3893,7 @@ void ex_display(exarg_T *eap) msg_puts_attr("^J", attr); n -= 2; } - for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { + for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 clen = utfc_ptr2len(p); msg_outtrans_len(p, clen); p += clen - 1; -- cgit From b81c310d49f77c8f92ba2fdd99c7e0e9cb49d498 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Thu, 24 Mar 2022 13:11:04 -0600 Subject: fix(tui): correct CSI sequence (#17844) Follow up to #17771. The sequence `CSI > 4 ; 1 m` does not enable distinguishing all available keys; notably, it excludes ``. Using `CSI > 4 ; 2 m` tells the terminal to disambiguate *all* keys, which is much more useful. The meaning of the final parameter is documented [here][1]. [1]: https://invisible-island.net/xterm/manpage/xterm.html#VT100-Widget-Resources:modifyOtherKeys --- src/nvim/tui/tui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index bec55174c4..d27a13c583 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -2077,7 +2077,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, // Kitty does not support these sequences; it only supports it's own CSI > 1 u which enables the // Kitty keyboard protocol data->unibi_ext.enable_extended_keys = (int)unibi_add_ext_str(ut, "ext.enable_extended_keys", - "\x1b[>4;1m"); + "\x1b[>4;2m"); data->unibi_ext.disable_extended_keys = (int)unibi_add_ext_str(ut, "ext.disable_extended_keys", "\x1b[>4;0m"); } -- cgit From 174deafcef27bc98df36c952ee93e316488bc765 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Fri, 25 Mar 2022 13:24:53 -0500 Subject: docs(api): improve autocommand docs (#17545) [skip ci] --- src/nvim/api/autocmd.c | 201 +++++++++++++++++++++++++++++++------------------ 1 file changed, 129 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 8a7dd00b2a..94a24705d0 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -36,16 +36,42 @@ // Used to delete autocmds from nvim_del_autocmd static int64_t next_autocmd_id = 1; -/// Get autocmds that match the requirements passed to {opts}. +/// Get autocommands that match the requirements passed to {opts}. /// -/// @param opts Optional Parameters: -/// - event : Name or list of name of events to match against -/// - group (string|int): Name or id of group to match against -/// - pattern: Pattern or list of patterns to match against. Cannot be used with {buffer} -/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands -/// |autocmd-buflocal|. Cannot be used with {pattern} +/// These examples will get autocommands matching ALL the given criteria: +///
+///   -- Matches all criteria
+///   autocommands = vim.api.nvim_get_autocmds({
+///     group = "MyGroup",
+///     event = {"BufEnter", "BufWinEnter"},
+///     pattern = {"*.c", "*.h"}
+///   })
+///
+///   -- All commands from one group
+///   autocommands = vim.api.nvim_get_autocmds({
+///     group = "MyGroup",
+///   })
+/// 
/// -/// @return A list of autocmds that match +/// NOTE: When multiple patterns or events are provided, it will find all the autocommands that +/// match any combination of them. +/// +/// @param opts Dictionary with at least one of the following: +/// - group (string|integer): the autocommand group name or id to match against. +/// - event (string|array): event or events to match against |autocmd-events|. +/// - pattern (string|array): pattern or patterns to match against |autocmd-pattern|. +/// @return Array of autocommands matching the criteria, with each item +/// containing the following fields: +/// - id (number): the autocommand id (only when defined with the API). +/// - group (integer): the autocommand group id. +/// - desc (string): the autocommand description. +/// - event (string): the autocommand event. +/// - command (string): the autocommand command. +/// - once (boolean): whether the autocommand is only run once. +/// - pattern (string): the autocommand pattern. +/// If the autocommand is buffer local |autocmd-buffer-local|: +/// - buflocal (boolean): true if the autocommand is buffer local. +/// - buffer (number): the buffer number. Array nvim_get_autocmds(Dict(get_autocmds) *opts, Error *err) FUNC_API_SINCE(9) { @@ -301,40 +327,68 @@ cleanup: return autocmd_list; } -/// Create an autocmd. +/// Create an |autocommand| +/// +/// The API allows for two (mutually exclusive) types of actions to be executed when the autocommand +/// triggers: a callback function (Lua or Vimscript), or a command (like regular autocommands). +/// +/// Example using callback: +///
+///     -- Lua function
+///     local myluafun = function() print("This buffer enters") end
+///
+///     -- Vimscript function name (as a string)
+///     local myvimfun = "g:MyVimFunction"
+///
+///     vim.api.nvim_create_autocmd({
+///       event = {"BufEnter", "BufWinEnter"},
+///       pattern = {"*.c", "*.h"},
+///       callback = myluafun,  -- Or myvimfun
+///     })
+/// 
/// -/// @param event The event or events to register this autocmd -/// Required keys: -/// event: string | ArrayOf(string) +/// Example using command: +///
+///     vim.api.nvim_create_autocmd({
+///       event = {"BufEnter", "BufWinEnter"},
+///       pattern = {"*.c", "*.h"},
+///       command = "echo 'Entering a C or C++ file'",
+///     })
+/// 
/// -/// Examples: -/// - event: "pat1,pat2,pat3", -/// - event: "pat1" -/// - event: { "pat1" } -/// - event: { "pat1", "pat2", "pat3" } +/// Example values for pattern: +///
+///   pattern = "*.py"
+///   pattern = { "*.py", "*.pyi" }
+/// 
/// -/// @param opts Optional Parameters: -/// - callback: (string|function) -/// - (string): The name of the viml function to execute when triggering this autocmd -/// - (function): The lua function to execute when triggering this autocmd -/// - NOTE: Cannot be used with {command} -/// - command: (string) command -/// - vimscript command -/// - NOTE: Cannot be used with {callback} -/// Eg. command = "let g:value_set = v:true" -/// - pattern: (string|table) -/// - pattern or patterns to match against -/// - defaults to "*". -/// - NOTE: Cannot be used with {buffer} -/// - buffer: (bufnr) -/// - create a |autocmd-buflocal| autocmd. -/// - NOTE: Cannot be used with {pattern} -/// - group: (string|int) The augroup name or id -/// - once: (boolean) - See |autocmd-once| -/// - nested: (boolean) - See |autocmd-nested| -/// - desc: (string) - Description of the autocmd +/// Examples values for event: +///
+///   event = "BufPreWrite"
+///   event = {"CursorHold", "BufPreWrite", "BufPostWrite"}
+/// 
+/// +/// @param event (String|Array) The event or events to register this autocommand +/// @param opts Dictionary of autocommand options: +/// - group (string|integer) optional: the autocommand group name or +/// id to match against. +/// - pattern (string|array) optional: pattern or patterns to match +/// against |autocmd-pattern|. +/// - buffer (integer) optional: buffer number for buffer local autocommands +/// |autocmd-buflocal|. Cannot be used with {pattern}. +/// - desc (string) optional: description of the autocommand. +/// - callback (function|string) optional: Lua function or Vim function (as string) to +/// execute on event. Cannot be used with {command} +/// - command (string) optional: Vim command to execute on event. Cannot be used with +/// {callback} +/// - once (boolean) optional: defaults to false. Run the autocommand +/// only once |autocmd-once|. +/// - nested (boolean) optional: defaults to false. Run nested +/// autocommands |autocmd-nested|. /// -/// @returns opaque value to use with nvim_del_autocmd +/// @return Integer id of the created autocommand. +/// @see |autocommand| +/// @see |nvim_del_autocmd()| Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autocmd) *opts, Error *err) FUNC_API_SINCE(9) @@ -552,33 +606,32 @@ cleanup: return autocmd_id; } -/// Delete an autocmd by {id}. Autocmds only return IDs when created -/// via the API. Will not error if called and no autocmds match -/// the {id}. +/// Delete an autocommand by id. /// -/// @param id Integer The ID returned by nvim_create_autocmd +/// NOTE: Only autocommands created via the API have an id. +/// @param id Integer The id returned by nvim_create_autocmd +/// @see |nvim_create_autocmd()| void nvim_del_autocmd(Integer id) FUNC_API_SINCE(9) { autocmd_delete_id(id); } -/// Create or get an augroup. +/// Create or get an autocommand group |autocmd-groups|. /// -/// To get an existing augroup ID, do: +/// To get an existing group id, do: ///
-///     local id = vim.api.nvim_create_augroup(name, {
+///     local id = vim.api.nvim_create_augroup("MyGroup", {
 ///         clear = false
 ///     })
 /// 
/// -/// @param name String: The name of the augroup to create -/// @param opts Parameters -/// - clear (bool): Whether to clear existing commands or not. -/// Defaults to true. -/// See |autocmd-groups| -/// -/// @returns opaque value to use with nvim_del_augroup_by_id +/// @param name String: The name of the group +/// @param opts Dictionary Parameters +/// - clear (bool) optional: defaults to true. Clear existing +/// commands if the group already exists |autocmd-groups|. +/// @return Integer id of the created group. +/// @see |autocmd-groups| Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augroup) *opts, Error *err) FUNC_API_SINCE(9) @@ -604,13 +657,15 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou return augroup; } -/// Delete an augroup by {id}. {id} can only be returned when augroup was -/// created with |nvim_create_augroup|. +/// Delete an autocommand group by id. /// -/// NOTE: behavior differs from augroup-delete. +/// To get a group id one can use |nvim_get_autocmds()|. /// -/// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. -/// This augroup will no longer exist +/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in +/// this group will also be deleted and cleared. This group will no longer exist. +/// @param id Integer The id of the group. +/// @see |nvim_del_augroup_by_name()| +/// @see |nvim_create_augroup()| void nvim_del_augroup_by_id(Integer id) FUNC_API_SINCE(9) { @@ -618,28 +673,30 @@ void nvim_del_augroup_by_id(Integer id) augroup_del(name, false); } -/// Delete an augroup by {name}. +/// Delete an autocommand group by name. /// -/// NOTE: behavior differs from augroup-delete. -/// -/// When deleting an augroup, autocmds contained by this augroup will also be deleted and cleared. -/// This augroup will no longer exist +/// NOTE: behavior differs from |augroup-delete|. When deleting a group, autocommands contained in +/// this group will also be deleted and cleared. This group will no longer exist. +/// @param name String The name of the group. +/// @see |autocommand-groups| void nvim_del_augroup_by_name(String name) FUNC_API_SINCE(9) { augroup_del(name.data, false); } -/// Do one autocmd. -/// -/// @param event The event or events to execute -/// @param opts Optional Parameters: -/// - buffer (number) - buffer number -/// - NOTE: Cannot be used with {pattern} -/// - pattern (string|table) - optional, defaults to "*". -/// - NOTE: Cannot be used with {buffer} -/// - group (string|int) - autocmd group name or id -/// - modeline (boolean) - Default true, see || +/// Execute an autocommand |autocmd-execute|. +/// @param event (String|Array) The event or events to execute +/// @param opts Dictionary of autocommand options: +/// - group (string|integer) optional: the autocommand group name or +/// id to match against. |autocmd-groups|. +/// - pattern (string|array) optional: defaults to "*" |autocmd-pattern|. Cannot be used +/// with {buffer}. +/// - buffer (integer) optional: buffer number |autocmd-buflocal|. Cannot be used with +/// {pattern}. +/// - modeline (bool) optional: defaults to true. Process the +/// modeline after the autocommands ||. +/// @see |:doautocmd| void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) FUNC_API_SINCE(9) { -- cgit From 61205c1defb64ac5466496b5451e4a7f3171e21e Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 25 Mar 2022 19:57:59 +0100 Subject: chore: fix typos (#17755) Co-authored-by: Jordan Haine --- src/nvim/ui_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index e723a30d32..49971ac501 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -152,7 +152,7 @@ void ui_client_event_grid_line(Array args) Array cells = args.items[3].data.array; Integer endcol, clearcol; - // TODO(hlpr98): Accomodate other LineFlags when included in grid_line + // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; endcol = startcol; -- cgit From 876d22fca9a102141f3b1bc6776f4bdd855099fb Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 26 Mar 2022 01:02:42 +0100 Subject: fix(clang/'Dead store'): do not assign endcol (#17788) --- src/nvim/ui_client.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 49971ac501..6a7695bf72 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -151,10 +151,8 @@ void ui_client_event_grid_line(Array args) Integer startcol = args.items[2].data.integer; Array cells = args.items[3].data.array; - Integer endcol, clearcol; // TODO(hlpr98): Accommodate other LineFlags when included in grid_line LineFlags lineflags = 0; - endcol = startcol; size_t j = 0; int cur_attr = 0; @@ -203,8 +201,8 @@ void ui_client_event_grid_line(Array args) } } - endcol = startcol + (int)j; - clearcol = endcol + clear_width; + Integer endcol = startcol + (int)j; + Integer clearcol = endcol + clear_width; clear_attr = cur_attr; ui_call_raw_line(grid, row, startcol, endcol, clearcol, clear_attr, lineflags, -- cgit From c2378be3dd5cd7d97f7a22a2472fcee9992b1c31 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Mar 2022 08:27:29 +0800 Subject: vim-patch:8.2.3453: autocmd not executed when editing a directory (#17846) Problem: Autocmd not executed when editing a directory ending in a path separator inside try block. Solution: Return NOTDONE instead of FAIL. (closes vim/vim#8885) https://github.com/vim/vim/commit/40fa12aea352474d229f2f750e954a4318aead4e --- src/nvim/fileio.c | 2 +- src/nvim/testdir/test_autocmd.vim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index fe61a2fc90..7905b29876 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -361,7 +361,7 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski filemess(curbuf, fname, (char_u *)_(msg_is_a_directory), 0); msg_end(); msg_scroll = msg_save; - return FAIL; + return NOTDONE; } } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index c39546b9ea..d4005e41e8 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2343,6 +2343,19 @@ func Test_throw_in_BufWritePre() au! throwing endfunc +func Test_autocmd_in_try_block() + call mkdir('Xdir') + au BufEnter * let g:fname = expand('%') + try + edit Xdir/ + endtry + call assert_match('Xdir', g:fname) + + unlet g:fname + au! BufEnter + call delete('Xdir', 'rf') +endfunc + func Test_autocmd_CmdWinEnter() CheckRunVimInTerminal " There is not cmdwin switch, so -- cgit From 9530c2d6d82dbae1303e15608c56f4fefad312a9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Mar 2022 08:52:54 +0800 Subject: vim-patch:8.2.4626: Visual area not updated when removing sign in Visual mode (#17864) Problem: Visual area not fully updated when removing sign in Visual mode while scrolling. Solution: Adjust check for topline. (closes vim/vim#10017) https://github.com/vim/vim/commit/abb6fbd14d985b9b36a4e336d6edaf9853888ac1 --- src/nvim/screen.c | 2 +- src/nvim/testdir/test_display.vim | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5a10543559..41a129dba9 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -938,7 +938,7 @@ static void win_update(win_T *wp, DecorProviders *providers) if (mod_top != 0 && wp->w_topline == mod_top && (!wp->w_lines[0].wl_valid - || wp->w_topline <= wp->w_lines[0].wl_lnum)) { + || wp->w_topline == wp->w_lines[0].wl_lnum)) { // w_topline is the first changed line and window is not scrolled, // the scrolling from changed lines will be done further down. } else if (wp->w_lines[0].wl_valid diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index 9f74d0a38a..094283a3a3 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -263,6 +263,27 @@ func Test_display_scroll_at_topline() call StopVimInTerminal(buf) endfunc +func Test_display_scroll_update_visual() + CheckScreendump + + let lines =<< trim END + set scrolloff=0 + call setline(1, repeat(['foo'], 10)) + call sign_define('foo', { 'text': '>' }) + call sign_place(1, 'bar', 'foo', bufnr(), { 'lnum': 2 }) + call sign_place(2, 'bar', 'foo', bufnr(), { 'lnum': 1 }) + autocmd CursorMoved * if getcurpos()[1] == 2 | call sign_unplace('bar', { 'id': 1 }) | endif + END + call writefile(lines, 'XupdateVisual.vim') + + let buf = RunVimInTerminal('-S XupdateVisual.vim', #{rows: 8, cols: 60}) + call term_sendkeys(buf, "VG7kk") + call VerifyScreenDump(buf, 'Test_display_scroll_update_visual', {}) + + call StopVimInTerminal(buf) + call delete('XupdateVisual.vim') +endfunc + " Test for 'eob' (EndOfBuffer) item in 'fillchars' func Test_eob_fillchars() " default value (skipped) -- cgit From 19bbc43947a75b0279f9697f5830a238af337c5b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Mar 2022 12:29:26 +0800 Subject: vim-patch:8.2.4281: using freed memory with :lopen and :bwipe Problem: Using freed memory with :lopen and :bwipe. Solution: Do not use a wiped out buffer. https://github.com/vim/vim/commit/9b4a80a66544f2782040b641498754bcb5b8d461 Cherry-pick some indent changes from patch 8.2.1432. --- src/nvim/buffer.c | 11 ++- src/nvim/testdir/test_quickfix.vim | 177 ++++++++++++++++++++----------------- 2 files changed, 106 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f200f16a5f..8d042525d3 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1486,8 +1486,15 @@ void set_curbuf(buf_T *buf, int action) // An autocommand may have deleted "buf", already entered it (e.g., when // it did ":bunload") or aborted the script processing! // If curwin->w_buffer is null, enter_buffer() will make it valid again - if ((buf_valid(buf) && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) { - enter_buffer(buf); + bool valid = buf_valid(buf); + if ((valid && buf != curbuf && !aborting()) || curwin->w_buffer == NULL) { + // If the buffer is not valid but curwin->w_buffer is NULL we must + // enter some buffer. Using the last one is hopefully OK. + if (!valid) { + enter_buffer(lastbuf); + } else { + enter_buffer(buf); + } if (old_tw != curbuf->b_p_tw) { check_colorcolumn(curwin); } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 14d13049d9..a00231c5e9 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -796,101 +796,102 @@ func ReadTestProtocol(name) endfunc func Test_locationlist() - enew + enew - augroup testgroup - au! - autocmd BufReadCmd test://* call ReadTestProtocol(expand("")) - augroup END + augroup testgroup + au! + autocmd BufReadCmd test://* call ReadTestProtocol(expand("")) + augroup END - let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ] + let words = [ "foo", "bar", "baz", "quux", "shmoo", "spam", "eggs" ] - let qflist = [] - for word in words - call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', }) - " NOTE: problem 1: - " intentionally not setting 'lnum' so that the quickfix entries are not - " valid - eval qflist->setloclist(0, ' ') - endfor + let qflist = [] + for word in words + call add(qflist, {'filename': 'test://' . word . '.txt', 'text': 'file ' . word . '.txt', }) + " NOTE: problem 1: + " intentionally not setting 'lnum' so that the quickfix entries are not + " valid + eval qflist->setloclist(0, ' ') + endfor - " Test A - lrewind - enew - lopen - 4lnext - vert split - wincmd L - lopen - wincmd p - lnext - let fileName = expand("%") - wincmd p - let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '') - let fileName = substitute(fileName, '\\', '/', 'g') - let locationListFileName = substitute(locationListFileName, '\\', '/', 'g') - call assert_equal("test://bar.txt", fileName) - call assert_equal("test://bar.txt", locationListFileName) + " Test A + lrewind + enew + lopen + 4lnext + vert split + wincmd L + lopen + wincmd p + lnext + let fileName = expand("%") + wincmd p + let locationListFileName = substitute(getline(line('.')), '\([^|]*\)|.*', '\1', '') + let fileName = substitute(fileName, '\\', '/', 'g') + let locationListFileName = substitute(locationListFileName, '\\', '/', 'g') + call assert_equal("test://bar.txt", fileName) + call assert_equal("test://bar.txt", locationListFileName) - wincmd n | only + wincmd n | only - " Test B: - lrewind - lopen - 2 - exe "normal \" - wincmd p - 3 - exe "normal \" - wincmd p - 4 - exe "normal \" - call assert_equal(2, winnr('$')) - wincmd n | only + " Test B: + lrewind + lopen + 2 + exe "normal \" + wincmd p + 3 + exe "normal \" + wincmd p + 4 + exe "normal \" + call assert_equal(2, winnr('$')) + wincmd n | only - " Test C: - lrewind - lopen - " Let's move the location list window to the top to check whether it (the - " first window found) will be reused when we try to open new windows: - wincmd K - 2 - exe "normal \" - wincmd p - 3 - exe "normal \" - wincmd p - 4 - exe "normal \" - 1wincmd w - call assert_equal('quickfix', &buftype) - 2wincmd w - let bufferName = expand("%") - let bufferName = substitute(bufferName, '\\', '/', 'g') - call assert_equal('test://quux.txt', bufferName) + " Test C: + lrewind + lopen + " Let's move the location list window to the top to check whether it (the + " first window found) will be reused when we try to open new windows: + wincmd K + 2 + exe "normal \" + wincmd p + 3 + exe "normal \" + wincmd p + 4 + exe "normal \" + 1wincmd w + call assert_equal('quickfix', &buftype) + 2wincmd w + let bufferName = expand("%") + let bufferName = substitute(bufferName, '\\', '/', 'g') + call assert_equal('test://quux.txt', bufferName) - wincmd n | only + wincmd n | only - augroup! testgroup + augroup! testgroup endfunc func Test_locationlist_curwin_was_closed() - augroup testgroup - au! - autocmd BufReadCmd test_curwin.txt call R(expand("")) - augroup END + augroup testgroup + au! + autocmd BufReadCmd test_curwin.txt call R(expand("")) + augroup END - func! R(n) - quit - endfunc + func! R(n) + quit + endfunc - new - let q = [] - call add(q, {'filename': 'test_curwin.txt' }) - call setloclist(0, q) - call assert_fails('lrewind', 'E924:') + new + let q = [] + call add(q, {'filename': 'test_curwin.txt' }) + call setloclist(0, q) + call assert_fails('lrewind', 'E924:') - augroup! testgroup + augroup! testgroup + delfunc R endfunc func Test_locationlist_cross_tab_jump() @@ -5489,4 +5490,20 @@ func Test_two_qf_windows() %bw! endfunc +" Weird sequence of commands that caused entering a wiped-out buffer +func Test_lopen_bwipe() + func R() + silent! tab lopen + e x + silent! lfile + endfunc + + cal R() + cal R() + cal R() + bw! + delfunc R +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From d8b4f3e3b83d5a0fd5d844da34da23c88dc9c4c5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Mar 2022 12:39:31 +0800 Subject: vim-patch:8.2.4327: may end up with no current buffer Problem: May end up with no current buffer. Solution: When deleting the current buffer to not pick a quickfix buffer as the new current buffer. https://github.com/vim/vim/commit/e3537aec2f8d6470010547af28dcbd83d41461b8 The test cannot be ported as-is because Nvim doesn't support "-Z" command line argument. Just use only "--clean" instead. --- src/nvim/buffer.c | 13 +++++++++---- src/nvim/testdir/test_quickfix.vim | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8d042525d3..4ca752e747 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1284,8 +1284,10 @@ int do_buffer(int action, int start, int dir, int count, int forceit) while (jumpidx != curwin->w_jumplistidx) { buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); if (buf != NULL) { - if (buf == curbuf || !buf->b_p_bl) { - buf = NULL; // skip current and unlisted bufs + // Skip current and unlisted bufs. Also skip a quickfix + // buffer, it might be deleted soon. + if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf)) { + buf = NULL; } else if (buf->b_ml.ml_mfp == NULL) { // skip unloaded buf, but may keep it for later if (bp == NULL) { @@ -1323,7 +1325,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) continue; } // in non-help buffer, try to skip help buffers, and vv - if (buf->b_help == curbuf->b_help && buf->b_p_bl) { + if (buf->b_help == curbuf->b_help && buf->b_p_bl && !bt_quickfix(buf)) { if (buf->b_ml.ml_mfp != NULL) { // found loaded buffer break; } @@ -1343,7 +1345,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } if (buf == NULL) { // No loaded buffer, find listed one FOR_ALL_BUFFERS(buf2) { - if (buf2->b_p_bl && buf2 != curbuf) { + if (buf2->b_p_bl && buf2 != curbuf && !bt_quickfix(buf2)) { buf = buf2; break; } @@ -1355,6 +1357,9 @@ int do_buffer(int action, int start, int dir, int count, int forceit) } else { buf = curbuf->b_prev; } + if (bt_quickfix(buf)) { + buf = NULL; + } } } diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index a00231c5e9..5457223677 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5505,5 +5505,30 @@ func Test_lopen_bwipe() delfunc R endfunc +" Another sequence of commands that caused all buffers to be wiped out +func Test_lopen_bwipe_all() + let lines =<< trim END + func R() + silent! tab lopen + e foo + silent! lfile + endfunc + cal R() + exe "norm \\0" + cal R() + bwipe + + call writefile(['done'], 'Xresult') + qall! + END + call writefile(lines, 'Xscript') + if RunVim([], [], '--clean -n -S Xscript') + call assert_equal(['done'], readfile('Xresult')) + endif + + call delete('Xscript') + call delete('Xresult') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From a70b39965b942c24d17bb44e731c9f9087417e50 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Mar 2022 10:48:31 +0800 Subject: fix(tui): avoid using uninitialized memory in kitty (#17866) --- src/nvim/tui/tui.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index d27a13c583..a67bcf98dc 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -226,6 +226,8 @@ static void terminfo_start(UI *ui) data->unibi_ext.reset_cursor_style = -1; data->unibi_ext.get_bg = -1; data->unibi_ext.set_underline_color = -1; + data->unibi_ext.enable_extended_keys = -1; + data->unibi_ext.disable_extended_keys = -1; data->out_fd = STDOUT_FILENO; data->out_isatty = os_isatty(data->out_fd); -- cgit From 82304038a239bb833e88c9c342aae19ab5dda489 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Sat, 26 Mar 2022 11:54:31 +0100 Subject: vim-patch:8.2.3943: compiler warning from gcc for uninitialized variable Problem: Compiler warning from gcc for uninitialized variable. Solution: Initialize variable. (closes vim/vim#9429) https://github.com/vim/vim/commit/491669701c72578f273db53e579d8a03a9deac0c --- src/nvim/diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1b8a9f41e9..a6bbe40999 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1536,7 +1536,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio) long off; int i; int notset = true; // block "*dp" not set yet - diffhunk_T *hunk; + diffhunk_T *hunk = NULL; // init to avoid gcc warning enum { DIFF_ED, DIFF_UNIFIED, -- cgit From 85821d8b6fa366f7361f84e07e62dc8fc951f26d Mon Sep 17 00:00:00 2001 From: ã‹ã‚ãˆã‚‚ã‚“ Date: Sat, 26 Mar 2022 21:21:32 +0900 Subject: docs(api): fix wrong documentation of `nvim_create_autocmd` (#17870) also add doc changes from typofix PR --- src/nvim/api/autocmd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 94a24705d0..9aaa0418dc 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -340,8 +340,7 @@ cleanup: /// -- Vimscript function name (as a string) /// local myvimfun = "g:MyVimFunction" /// -/// vim.api.nvim_create_autocmd({ -/// event = {"BufEnter", "BufWinEnter"}, +/// vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, { /// pattern = {"*.c", "*.h"}, /// callback = myluafun, -- Or myvimfun /// }) @@ -349,8 +348,7 @@ cleanup: /// /// Example using command: ///
-///     vim.api.nvim_create_autocmd({
-///       event = {"BufEnter", "BufWinEnter"},
+///     vim.api.nvim_create_autocmd({"BufEnter", "BufWinEnter"}, {
 ///       pattern = {"*.c", "*.h"},
 ///       command = "echo 'Entering a C or C++ file'",
 ///     })
@@ -364,8 +362,8 @@ cleanup:
 ///
 /// Examples values for event:
 /// 
-///   event = "BufPreWrite"
-///   event = {"CursorHold", "BufPreWrite", "BufPostWrite"}
+///   "BufPreWrite"
+///   {"CursorHold", "BufPreWrite", "BufPostWrite"}
 /// 
/// /// @param event (String|Array) The event or events to register this autocommand -- cgit From a490db5ba819218e9188cbb51d885dbf3a194000 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sat, 26 Mar 2022 09:34:56 -0500 Subject: refactor!: rename nvim_do_autocmd to nvim_exec_autocmd (#17854) according to established code standards (`:h dev-api`) --- src/nvim/api/autocmd.c | 2 +- src/nvim/api/keysets.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 9aaa0418dc..49f6f98ba4 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -695,7 +695,7 @@ void nvim_del_augroup_by_name(String name) /// - modeline (bool) optional: defaults to true. Process the /// modeline after the autocommands ||. /// @see |:doautocmd| -void nvim_do_autocmd(Object event, Dict(do_autocmd) *opts, Error *err) +void nvim_exec_autocmd(Object event, Dict(exec_autocmd) *opts, Error *err) FUNC_API_SINCE(9) { int au_group = AUGROUP_ALL; diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index b6264cdfab..6a29b0fb59 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -133,7 +133,7 @@ return { "nested"; "pattern"; }; - do_autocmd = { + exec_autocmd = { "buffer"; "group"; "modeline"; -- cgit From f4f18a983305d3cf8a6028333e9b99e86283032b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 27 Mar 2022 08:57:57 +0800 Subject: vim-patch:8.2.4631: crash when switching window in BufWipeout autocommand Problem: Crash when switching window in BufWipeout autocommand. Solution: Put any buffer in the window to avoid it being NULL. (closes vim/vim#10024) https://github.com/vim/vim/commit/347538fad0c503249ebdedd5884c2081257c9f61 win_init_empty() cannot be made static because it is used in autocmd.c --- src/nvim/buffer.c | 4 +++ src/nvim/testdir/test_autocmd.vim | 17 ++++++++++++ src/nvim/window.c | 58 +++++++++++++++++++++------------------ 3 files changed, 52 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4ca752e747..bf592a626d 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -590,6 +590,10 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) // Remove the buffer from the list. if (wipe_buf) { + // Do not wipe out the buffer if it is used in a window. + if (buf->b_nwindows > 0) { + return false; + } if (buf->b_sfname != buf->b_ffname) { XFREE_CLEAR(buf->b_sfname); } else { diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index d4005e41e8..76c69ad10b 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2611,4 +2611,21 @@ func Test_autocmd_closing_cmdwin() only endfunc +func Test_bufwipeout_changes_window() + " This should not crash, but we don't have any expectations about what + " happens, changing window in BufWipeout has unpredictable results. + tabedit + let g:window_id = win_getid() + topleft new + setlocal bufhidden=wipe + autocmd BufWipeout call win_gotoid(g:window_id) + tabprevious + +tabclose + + unlet g:window_id + au! BufWipeout + %bwipe! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index fa71a08b94..02b297bcef 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2350,6 +2350,30 @@ void entering_window(win_T *const win) } } +void win_init_empty(win_T *wp) +{ + redraw_later(wp, NOT_VALID); + wp->w_lines_valid = 0; + wp->w_cursor.lnum = 1; + wp->w_curswant = wp->w_cursor.col = 0; + wp->w_cursor.coladd = 0; + wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1 + wp->w_pcmark.col = 0; + wp->w_prev_pcmark.lnum = 0; + wp->w_prev_pcmark.col = 0; + wp->w_topline = 1; + wp->w_topfill = 0; + wp->w_botline = 2; + wp->w_s = &wp->w_buffer->b_s; +} + +/// Init the current window "curwin". +/// Called when a new file is being edited. +void curwin_init(void) +{ + win_init_empty(curwin); +} + /// Closes all windows for buffer `buf` unless there is only one non-floating window. /// /// @param keep_curwin don't close `curwin` @@ -2867,6 +2891,13 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) { } if (ptp == NULL || tp == curtab) { + // If the buffer was removed from the window we have to give it any + // buffer. + if (win_valid_any_tab(win) && win->w_buffer == NULL) { + win->w_buffer = firstbuf; + firstbuf->b_nwindows++; + win_init_empty(win); + } return; } @@ -3793,33 +3824,6 @@ void close_others(int message, int forceit) } } - -/* - * Init the current window "curwin". - * Called when a new file is being edited. - */ -void curwin_init(void) -{ - win_init_empty(curwin); -} - -void win_init_empty(win_T *wp) -{ - redraw_later(wp, NOT_VALID); - wp->w_lines_valid = 0; - wp->w_cursor.lnum = 1; - wp->w_curswant = wp->w_cursor.col = 0; - wp->w_cursor.coladd = 0; - wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1 - wp->w_pcmark.col = 0; - wp->w_prev_pcmark.lnum = 0; - wp->w_prev_pcmark.col = 0; - wp->w_topline = 1; - wp->w_topfill = 0; - wp->w_botline = 2; - wp->w_s = &wp->w_buffer->b_s; -} - /* * Allocate the first window and put an empty buffer in it. * Called from main(). -- cgit From ae0a43ec23d9d902d2d9c446dda592873988ea50 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 27 Mar 2022 09:19:56 +0800 Subject: fix(tabpage): correct check for failure to close window Avoid closing window 999 times. --- src/nvim/ex_docmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 0dbc9d6b14..20325509c4 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6839,7 +6839,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) // Autocommands may delete the tab page under our fingers and we may // fail to close a window with a modified buffer. - if (!valid_tabpage(tp) || tp->tp_firstwin == wp) { + if (!valid_tabpage(tp) || tp->tp_lastwin == wp) { break; } } -- cgit From 05edab85d7edad63c3b449689b57f9f658782c11 Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Sun, 27 Mar 2022 21:33:54 +0600 Subject: refactor: assume `STATUS_HEIGHT` to be 1 (#17804) Since https://github.com/neovim/neovim/pull/17790 being merged means we can assume the value of `STATUS_HEIGHT` to always be 1, this commit removes code that's unnecessary if `STATUS_HEIGHT` is 1. --- src/nvim/window.c | 68 ++++++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 02b297bcef..6bd2ef3ef6 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -971,7 +971,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int before; int minheight; int wmh1; - int hsep_height; bool did_set_fraction = false; // aucmd_win should always remain floating @@ -1084,7 +1083,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } } } else { - hsep_height = STATUS_HEIGHT; layout = FR_COL; /* @@ -1093,7 +1091,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) */ // Current window requires at least 1 space. wmh1 = p_wmh == 0 ? 1 : p_wmh; - needed = wmh1 + hsep_height; + needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { needed += p_wh - wmh1; } @@ -1135,15 +1133,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) new_size = oldwin_height / 2; } - if (new_size > available - minheight - hsep_height) { - new_size = available - minheight - hsep_height; + if (new_size > available - minheight - STATUS_HEIGHT) { + new_size = available - minheight - STATUS_HEIGHT; } if (new_size < wmh1) { new_size = wmh1; } // if it doesn't fit in the current window, need win_equal() - if (oldwin_height - new_size - hsep_height < p_wmh) { + if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh) { do_equal = true; } @@ -1156,7 +1154,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) set_fraction(oldwin); did_set_fraction = true; - win_setheight_win(oldwin->w_height + new_size + hsep_height, + win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, oldwin); oldwin_height = oldwin->w_height; if (need_status) { @@ -1173,7 +1171,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_height > new_size - || frp->fr_win->w_height > oldwin_height - new_size - hsep_height)) { + || frp->fr_win->w_height > oldwin_height - new_size - STATUS_HEIGHT)) { do_equal = true; break; } @@ -2021,7 +2019,6 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int int room = 0; int new_size; int has_next_curwin = 0; - int hsep_height; bool hnc; if (topfr->fr_layout == FR_LEAF) { @@ -2167,7 +2164,6 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int totwincount -= wincount; } } else { // topfr->fr_layout == FR_COL - hsep_height = STATUS_HEIGHT; topfr->fr_width = width; topfr->fr_height = height; @@ -2182,7 +2178,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmh + hsep_height); + totwincount = (n + extra_sep) / (p_wmh + STATUS_HEIGHT); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2217,7 +2213,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // These windows don't use up room. totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmh + hsep_height); + ? extra_sep : 0)) / (p_wmh + STATUS_HEIGHT); } room -= new_size - n; if (room < 0) { @@ -2263,7 +2259,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmh + hsep_height); + / (p_wmh + STATUS_HEIGHT); m = frame_minheight(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -6514,38 +6510,32 @@ void last_status(bool morewin) } // Look for resizable frames and take lines from them to make room for the statusline -static void resize_frame_for_status(frame_T *fr, int resize_amount) +static void resize_frame_for_status(frame_T *fr) { // Find a frame to take a line from. frame_T *fp = fr; win_T *wp = fr->fr_win; - int n; - while (resize_amount > 0) { - while (fp->fr_height <= frame_minheight(fp, NULL)) { - if (fp == topframe) { - emsg(_(e_noroom)); - return; - } - // In a column of frames: go to frame above. If already at - // the top or in a row of frames: go to parent. - if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { - fp = fp->fr_prev; - } else { - fp = fp->fr_parent; - } + while (fp->fr_height <= frame_minheight(fp, NULL)) { + if (fp == topframe) { + emsg(_(e_noroom)); + return; } - n = MIN(fp->fr_height - frame_minheight(fp, NULL), resize_amount); - resize_amount -= n; - - if (fp != fr) { - frame_new_height(fp, fp->fr_height - n, false, false); - frame_fix_height(wp); - (void)win_comp_pos(); + // In a column of frames: go to frame above. If already at + // the top or in a row of frames: go to parent. + if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { + fp = fp->fr_prev; } else { - win_new_height(wp, wp->w_height - n); + fp = fp->fr_parent; } } + if (fp != fr) { + frame_new_height(fp, fp->fr_height - 1, false, false); + frame_fix_height(wp); + (void)win_comp_pos(); + } else { + win_new_height(wp, wp->w_height - 1); + } } static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) @@ -6566,15 +6556,12 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) } else if (wp->w_status_height == 0 && !is_stl_global && statusline) { // Add statusline to window if needed wp->w_status_height = STATUS_HEIGHT; - resize_frame_for_status(fr, STATUS_HEIGHT); + resize_frame_for_status(fr); comp_col(); } } else if (wp->w_status_height != 0 && is_stl_global) { // If statusline is global and the window has a statusline, replace it with a horizontal // separator - if (STATUS_HEIGHT - 1 != 0) { - win_new_height(wp, wp->w_height + STATUS_HEIGHT - 1); - } wp->w_status_height = 0; wp->w_hsep_height = 1; comp_col(); @@ -6582,7 +6569,6 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) // If statusline isn't global and the window doesn't have a statusline, re-add it wp->w_status_height = STATUS_HEIGHT; wp->w_hsep_height = 0; - resize_frame_for_status(fr, STATUS_HEIGHT - 1); comp_col(); } redraw_all_later(SOME_VALID); -- cgit From 72652cbc46f568128bfc296ba63fb2d26941da8e Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 27 Mar 2022 10:25:55 -0700 Subject: feat(test): use nvim_exec in helpers.source() #16064 helpers.source() was a hack to work around the lack of anonymous :source. Its "create tempfile" behavior is not a required part of most tests that use it. Some tests still need the old "create tempfile" behavior either because they test SID behavior, or because of missing nvim_exec features: #16071 --- src/nvim/eval.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fbbc543893..6c72c2866e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9217,6 +9217,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va /// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. /// +/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994 +/// /// @param[in] name Variable name, possibly with scope prefix. /// @param[in] name_len Variable name length. /// @param[out] varname Will be set to the start of the name without scope @@ -9304,6 +9306,7 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons } } if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { + // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994 new_script_item(NULL, ¤t_sctx.sc_sid); } *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; -- cgit From 4baeb96c1bb66c845b4bc58482df4e601485eec3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Mar 2022 04:43:51 +0800 Subject: vim-patch:8.2.4638: superfluous check if a redraw is needed for 'cursorline' Problem: Superfluous check if a redraw is needed for 'cursorline'. Solution: Remove check_redraw_cursorline(). (closes vim/vim#10030, closes vim/vim#10029) https://github.com/vim/vim/commit/3e559cd88486ffab6b6fb4e0921b4600d137a617 redraw_after_callback() is N/A. Omits changes that just revert code from patch 8.2.4630. --- src/nvim/move.c | 29 +++++++++++++++++++---------- src/nvim/normal.c | 3 --- src/nvim/screen.c | 13 ------------- src/nvim/testdir/test_highlight.vim | 25 +++++++++++++++++++++++++ src/nvim/testdir/test_number.vim | 25 +++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/nvim/move.c b/src/nvim/move.c index 751e0046bc..9b35a7da0a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -95,7 +95,7 @@ static void comp_botline(win_T *wp) win_check_anchored_floats(wp); } -// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. +/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. void redraw_for_cursorline(win_T *wp) FUNC_ATTR_NONNULL_ALL { @@ -107,6 +107,22 @@ void redraw_for_cursorline(win_T *wp) } } +/// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' +/// contains "screenline". +static void redraw_for_cursorcolumn(win_T *wp) + FUNC_ATTR_NONNULL_ALL +{ + if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { + if (wp->w_p_cuc) { + // When 'cursorcolumn' is set need to redraw with SOME_VALID. + redraw_later(wp, SOME_VALID); + } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) { + // When 'cursorlineopt' contains "screenline" need to redraw with VALID. + redraw_later(wp, VALID); + } + } +} + /* * Update curwin->w_topline and redraw if necessary. * Used to update the screen before printing a message. @@ -623,11 +639,8 @@ void validate_virtcol_win(win_T *wp) check_cursor_moved(wp); if (!(wp->w_valid & VALID_VIRTCOL)) { getvvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL); + redraw_for_cursorcolumn(wp); wp->w_valid |= VALID_VIRTCOL; - if (wp->w_p_cuc - && !pum_visible()) { - redraw_later(wp, SOME_VALID); - } } } @@ -930,11 +943,7 @@ void curs_columns(win_T *wp, int may_scroll) redraw_later(wp, NOT_VALID); } - // Redraw when w_virtcol changes and 'cursorcolumn' is set - if (wp->w_p_cuc && (wp->w_valid & VALID_VIRTCOL) == 0 - && !pum_visible()) { - redraw_later(wp, SOME_VALID); - } + redraw_for_cursorcolumn(curwin); // now w_leftcol is valid, avoid check_cursor_moved() thinking otherwise wp->w_valid_leftcol = wp->w_leftcol; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index e773351d63..6c7595d1a4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1295,9 +1295,6 @@ static void normal_redraw(NormalState *s) redrawWinline(curwin, curwin->w_cursor.lnum); } - // Might need to update for 'cursorline'. - check_redraw_cursorline(); - if (VIsual_active) { update_curbuf(INVERTED); // update inverted part } else if (must_redraw) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 41a129dba9..67eee55c51 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -7617,16 +7617,3 @@ win_T *get_win_by_grid_handle(handle_T handle) } return NULL; } - -/// Check if the cursor moved and 'cursorline' is set. Mark for a VALID redraw -/// if needed. -void check_redraw_cursorline(void) -{ - // When 'cursorlineopt' is "screenline" need to redraw always. - if (curwin->w_p_cul - && (curwin->w_last_cursorline != curwin->w_cursor.lnum - || (curwin->w_p_culopt_flags & CULOPT_SCRLINE)) - && !char_avail()) { - redraw_later(curwin, VALID); - } -} diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim index aa7b3a225b..6387ec62af 100644 --- a/src/nvim/testdir/test_highlight.vim +++ b/src/nvim/testdir/test_highlight.vim @@ -597,6 +597,31 @@ func Test_cursorline_with_visualmode() call delete('Xtest_cursorline_with_visualmode') endfunc +func Test_cursorcolumn_callback() + CheckScreendump + CheckFeature timers + + let lines =<< trim END + call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd']) + set cursorcolumn + call cursor(4, 5) + + func Func(timer) + call cursor(1, 1) + endfunc + + call timer_start(300, 'Func') + END + call writefile(lines, 'Xcuc_timer') + + let buf = RunVimInTerminal('-S Xcuc_timer', #{rows: 8}) + call TermWait(buf, 310) + call VerifyScreenDump(buf, 'Test_cursorcolumn_callback_1', {}) + + call StopVimInTerminal(buf) + call delete('Xcuc_timer') +endfunc + func Test_colorcolumn() CheckScreendump diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim index dfbdc0bffd..521b0cf706 100644 --- a/src/nvim/testdir/test_number.vim +++ b/src/nvim/testdir/test_number.vim @@ -298,6 +298,31 @@ func Test_relativenumber_colors() call delete('XTest_relnr') endfunc +func Test_relativenumber_callback() + CheckScreendump + CheckFeature timers + + let lines =<< trim END + call setline(1, ['aaaaa', 'bbbbb', 'ccccc', 'ddddd']) + set relativenumber + call cursor(4, 1) + + func Func(timer) + call cursor(1, 1) + endfunc + + call timer_start(300, 'Func') + END + call writefile(lines, 'Xrnu_timer') + + let buf = RunVimInTerminal('-S Xrnu_timer', #{rows: 8}) + call TermWait(buf, 310) + call VerifyScreenDump(buf, 'Test_relativenumber_callback_1', {}) + + call StopVimInTerminal(buf) + call delete('Xrnu_timer') +endfunc + " Test for displaying line numbers with 'rightleft' func Test_number_rightleft() CheckFeature rightleft -- cgit From ab02b28b4e14ecd22facab9377ea71aff2ffdfdb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Mar 2022 05:10:15 +0800 Subject: vim-patch:8.2.4630: 'cursorline' not always updated with 'culopt' is "screenline" Problem: 'cursorline' not always updated with 'cursorlineopt' is "screenline". Solution: Call check_redraw_cursorline() more often. (closes vim/vim#10013) https://github.com/vim/vim/commit/bf269ed0b0bd8414eea7bea17465b2738a9a2b55 Code was reverted in patch 8.2.4638, so this just ports the test. --- src/nvim/testdir/test_cursorline.vim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_cursorline.vim b/src/nvim/testdir/test_cursorline.vim index bf049ec779..7e97df6027 100644 --- a/src/nvim/testdir/test_cursorline.vim +++ b/src/nvim/testdir/test_cursorline.vim @@ -293,5 +293,26 @@ func Test_cursorline_callback() call delete('Xcul_timer') endfunc +func Test_cursorline_screenline_update() + CheckScreendump + + let lines =<< trim END + call setline(1, repeat('xyz ', 30)) + set cursorline cursorlineopt=screenline + inoremap call cursor(1, 1) + END + call writefile(lines, 'Xcul_screenline') + + let buf = RunVimInTerminal('-S Xcul_screenline', #{rows: 8}) + call term_sendkeys(buf, "A") + call VerifyScreenDump(buf, 'Test_cursorline_screenline_1', {}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_cursorline_screenline_2', {}) + call term_sendkeys(buf, "\") + + call StopVimInTerminal(buf) + call delete('Xcul_screenline') +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 9ce2c73180b741a7969ede940c3c439d0cc9010d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Mar 2022 09:36:26 +0800 Subject: revert: "Fix redraw regression with w_p_cole in visual mode" Revert the code change from b7d6caaa036c3d1be716bb6e4b0f56c08fb8dcf5. The test is kept. The glitch was fixed by #17864, so this workaround is no longer needed. --- src/nvim/screen.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 67eee55c51..38ea861cbd 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -667,15 +667,11 @@ void conceal_check_cursor_line(void) /// Whether cursorline is drawn in a special way /// -/// If true, both old and new cursorline will need -/// to be redrawn when moving cursor within windows. -/// TODO(bfredl): VIsual_active shouldn't be needed, but is used to fix a glitch -/// caused by scrolling. +/// If true, both old and new cursorline will need to be redrawn when moving cursor within windows. bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { - return wp->w_p_cul - || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); + return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); } /* -- cgit From 595c1a724af9fe93d4ff1df7d5c47e4c9c31a7a6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 28 Mar 2022 06:45:15 +0800 Subject: perf: only redraw concealed line if cursor has moved horizontally Building upon #17889, this moves conceal redrawing logic into move.c, so that concealed line is only redrawn if cursor has moved horizontally. --- src/nvim/edit.c | 8 -------- src/nvim/move.c | 13 ++++++++++--- src/nvim/normal.c | 7 ------- 3 files changed, 10 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index c087948810..815d57121b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1480,8 +1480,6 @@ bool edit(int cmdchar, bool startln, long count) /// @param ready not busy with something static void ins_redraw(bool ready) { - bool conceal_cursor_moved = false; - if (char_avail()) { return; } @@ -1504,7 +1502,6 @@ static void ins_redraw(bool ready) update_curswant(); ins_apply_autocmds(EVENT_CURSORMOVEDI); } - conceal_cursor_moved = true; curwin->w_last_cursormoved = curwin->w_cursor; } @@ -1560,11 +1557,6 @@ static void ins_redraw(bool ready) curbuf->b_changed_invalid = false; } - if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin) - && conceal_cursor_moved) { - redrawWinline(curwin, curwin->w_cursor.lnum); - } - pum_check_clear(); if (must_redraw) { update_screen(0); diff --git a/src/nvim/move.c b/src/nvim/move.c index 9b35a7da0a..5e02e355bf 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -96,12 +96,12 @@ static void comp_botline(win_T *wp) } /// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. +/// Also when concealing is on and 'concealcursor' is not active. void redraw_for_cursorline(win_T *wp) FUNC_ATTR_NONNULL_ALL { - if ((wp->w_p_rnu || win_cursorline_standout(wp)) - && (wp->w_valid & VALID_CROW) == 0 - && !pum_visible()) { + if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible() + && (wp->w_p_rnu || win_cursorline_standout(wp))) { // win_line() will redraw the number column and cursorline only. redraw_later(wp, VALID); } @@ -109,6 +109,7 @@ void redraw_for_cursorline(win_T *wp) /// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' /// contains "screenline". +/// Also when concealing is on and 'concealcursor' is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { @@ -121,6 +122,12 @@ static void redraw_for_cursorcolumn(win_T *wp) redraw_later(wp, VALID); } } + // If the cursor moves horizontally when 'concealcursor' is active, then the + // current line needs to be redrawn in order to calculate the correct + // cursor position. + if ((wp->w_valid & VALID_VIRTCOL) == 0 && wp->w_p_cole > 0 && conceal_cursor_line(wp)) { + redrawWinline(wp, wp->w_cursor.lnum); + } } /* diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6c7595d1a4..72e80952ff 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1288,13 +1288,6 @@ static void normal_redraw(NormalState *s) update_topline(curwin); validate_cursor(); - // If the cursor moves horizontally when 'concealcursor' is active, then the - // current line needs to be redrawn in order to calculate the correct - // cursor position. - if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) { - redrawWinline(curwin, curwin->w_cursor.lnum); - } - if (VIsual_active) { update_curbuf(INVERTED); // update inverted part } else if (must_redraw) { -- cgit From 3cc6cfecf39a67bfcc2547b81f3dd4b186c306be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 29 Mar 2022 11:57:04 +0800 Subject: vim-patch:8.2.4640: some boolean options use "long" instead of "int" (#17896) Problem: Some boolean options use "long" instead of "int". Solution: Adjust the type. (James McCoy, closes vim/vim#10033) https://github.com/vim/vim/commit/8be423b7ac3b5742deb20a7eba8f5c9680c04500 N/A patches for version.c: vim-patch:8.2.4641: may mark the wrong window for redrawing Problem: May mark the wrong window for redrawing. Solution: Use redraw_win_later(). (closes vim/vim#10032) https://github.com/vim/vim/commit/471b3aed3e9c43d4dd53444ceb74f9a4f8a3874a --- src/nvim/option_defs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index d88cd6b9b9..98c2b84770 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -523,11 +523,11 @@ EXTERN long p_mmd; // 'maxmapdepth' EXTERN long p_mmp; // 'maxmempattern' EXTERN long p_mis; // 'menuitems' EXTERN char_u *p_msm; // 'mkspellmem' -EXTERN long p_mle; // 'modelineexpr' +EXTERN int p_mle; // 'modelineexpr' EXTERN long p_mls; // 'modelines' EXTERN char_u *p_mouse; // 'mouse' EXTERN char_u *p_mousem; // 'mousemodel' -EXTERN long p_mousef; // 'mousefocus' +EXTERN int p_mousef; // 'mousefocus' EXTERN long p_mouset; // 'mousetime' EXTERN int p_more; // 'more' EXTERN char_u *p_opfunc; // 'operatorfunc' -- cgit From 81d7628c3fadaa72b879b5e934d30c609d7c89f8 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 29 Mar 2022 12:37:42 +0100 Subject: vim-patch:8.2.4644: redrawing too often when 'relativenumber' is set (#17756) Problem: Redrawing too often when 'relativenumber' is set. Solution: Only redraw when the cursor line changed. (Lewis Russell, closes vim/vim#10040) https://github.com/vim/vim/commit/1624639ec8a6c3c99e417a2990f2f02f0d0b6e10 --- src/nvim/buffer_defs.h | 2 ++ src/nvim/change.c | 2 +- src/nvim/screen.c | 8 +++++--- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 29413281ad..08ca1a6247 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1211,6 +1211,8 @@ struct window_S { colnr_T w_old_visual_col; ///< last known start of visual part colnr_T w_old_curswant; ///< last known value of Curswant + linenr_T w_last_cursor_lnum_rnu; ///< cursor lnum when 'rnu' was last redrawn + // 'listchars' characters. Defaults set in set_chars_option(). struct { int eol; diff --git a/src/nvim/change.c b/src/nvim/change.c index 6c3dbf72e4..0644b1d601 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -287,7 +287,7 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra } // Relative numbering may require updating more. - if (wp->w_p_rnu) { + if (wp->w_p_rnu && xtra != 0) { redraw_later(wp, SOME_VALID); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 67eee55c51..48f00e1a6a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1577,9 +1577,9 @@ static void win_update(win_T *wp, DecorProviders *providers) idx++; lnum += foldinfo.fi_lines + 1; } else { - if (wp->w_p_rnu) { - // 'relativenumber' set: The text doesn't need to be drawn, but - // the number column nearly always does. + if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) { + // 'relativenumber' set and cursor moved vertically: The + // text doesn't need to be drawn, but the number column does. foldinfo_T info = fold_info(wp, lnum); (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, info, &line_providers); @@ -1607,6 +1607,8 @@ static void win_update(win_T *wp, DecorProviders *providers) // update w_last_cursorline. wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0; + wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0; + if (idx > wp->w_lines_valid) { wp->w_lines_valid = idx; } -- cgit From ba257d74b187e38cd591b2f4eb2a4f0f6483b358 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 29 Mar 2022 19:56:45 +0800 Subject: fix(decorations): do not put empty virt_text (#17872) --- src/nvim/screen.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 48f00e1a6a..262f17cd21 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4335,6 +4335,9 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, break; } } + if (!*s.p) { + continue; + } int attr; bool through = false; if (hl_mode == kHlModeCombine) { -- cgit From 524f6294a3a9d93241d3b7f3034033f96dd32d7f Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Mar 2022 16:27:57 +0200 Subject: build(clint): link to wiki when accessing list_T internals directly (#17876) --- src/clint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/clint.py b/src/clint.py index 10f33d22e2..75aaba176d 100755 --- a/src/clint.py +++ b/src/clint.py @@ -3187,8 +3187,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, r'|li_(?:next|prev|tv))\b', line) if match: error(filename, linenum, 'runtime/deprecated', 4, - 'Accessing list_T internals directly is prohibited ' - '(hint: see commit d46e37cb4c71)') + 'Accessing list_T internals directly is prohibited; ' + 'see https://github.com/neovim/neovim/wiki/List-management-in-Neovim') # Check for suspicious usage of "if" like # } if (a == b) { -- cgit From 7a6978ba29572ca4346a795fcbcb67e047d0b776 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Mar 2022 20:23:58 +0200 Subject: vim-patch:8.2.3551: checking first character of url twice (#17910) Problem: Checking first character of url twice. Solution: Only check once. (closes vim/vim#9026) https://github.com/vim/vim/commit/94e7d345c156a722bb161b73238c4ba1d27ec586 --- src/nvim/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/path.c b/src/nvim/path.c index d3aa5e5bf2..75624778e3 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1777,7 +1777,7 @@ int path_with_url(const char *fname) } // check body: alpha or dash - for (p = fname; (isalpha(*p) || (*p == '-')); p++) {} + for (p = fname + 1; (isalpha(*p) || (*p == '-')); p++) {} // check last char is not a dash if (p[-1] == '-') { -- cgit From 29aa08a09da088178bccc5ea29ac1c872bc4ab32 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Mar 2022 22:14:37 +0200 Subject: vim-patch:8.2.3449: sort fails if the sort compare function returns 999 (#17909) Problem: Sort fails if the sort compare function returns 999. Solution: Adjust value to -1 / 0 / 1. (Yasuhiro Matsumoto, closes vim/vim#8884) https://github.com/vim/vim/commit/c04f62346bfd6b92151908239a3c5ab1a7d18f2a --- src/nvim/eval/funcs.c | 5 +++++ src/nvim/testdir/test_sort.vim | 1 + 2 files changed, 6 insertions(+) (limited to 'src') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 41b419c150..ae9cb3b1e8 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9466,6 +9466,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) res = ITEM_COMPARE_FAIL; } else { res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (res > 0) { + res = 1; + } else if (res < 0) { + res = -1; + } } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index f72368fc59..5d7dd7bfff 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -59,6 +59,7 @@ endfunc func Test_sort_numbers() call assert_equal([3, 13, 28], sort([13, 28, 3], 'N')) call assert_equal(['3', '13', '28'], sort(['13', '28', '3'], 'N')) + call assert_equal([3997, 4996], sort([4996, 3997], 'Compare1')) endfunc func Test_sort_float() -- cgit From a9665bb12cd8cbacbc6ef6df66c1989b0c6f9fcc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 05:25:00 +0800 Subject: fix(event-loop): duplicate display updating logic in vgetorpeek() (#17913) --- src/nvim/state.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/nvim/state.c b/src/nvim/state.c index f9a3aaab7f..3a7636085b 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -15,6 +15,7 @@ #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/os/input.h" +#include "nvim/screen.h" #include "nvim/state.h" #include "nvim/ui.h" #include "nvim/vim.h" @@ -54,6 +55,12 @@ getkey: // Event was made available after the last multiqueue_process_events call key = K_EVENT; } else { + // Duplicate display updating logic in vgetorpeek() + if (((State & INSERT) != 0 || p_lz) && (State & CMDLINE) == 0 + && must_redraw != 0 && !need_wait_return) { + update_screen(0); + setcursor(); // put cursor back where it belongs + } // Flush screen updates before blocking ui_flush(); // Call `os_inchar` directly to block for events or user input without -- cgit From e7ac16425c2bcbe26167d89134c51bf412c7b15e Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 29 Mar 2022 23:54:02 +0200 Subject: vim-patch:8.2.4473: Coverity warns for not checking return value of ftell() (#17914) Problem: Coverity warns for not checking return value of ftell(). Solution: Bail out if ftell() returns a negative value. https://github.com/vim/vim/commit/416b5f4894196947ea87eea2ed4fda3504674f72 --- src/nvim/spellfile.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index d7b220b3f6..07058b34fd 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5580,6 +5580,9 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo while (!vim_fgets(line, MAXWLEN * 2, fd)) { fpos = fpos_next; fpos_next = ftell(fd); + if (fpos_next < 0) { + break; // should never happen + } if (STRNCMP(word, line, len) == 0 && (line[len] == '/' || line[len] < ' ')) { // Found duplicate word. Remove it by writing a '#' at -- cgit From 2f378237037150f3c7405fec1d8762e73067d223 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 07:44:12 +0800 Subject: vim-patch:8.2.4646: using buffer line after it has been freed (#17907) Problem: Using buffer line after it has been freed in old regexp engine. Solution: After getting mark get the line again. https://github.com/vim/vim/commit/b55986c52d4cd88a22d0b0b0e8a79547ba13e1d5 --- src/nvim/regexp_bt.c | 10 +++++++++- src/nvim/testdir/test_regexp_latin.vim | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 7340957903..3835a2bbae 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -3130,9 +3130,17 @@ static bool regmatch( { int mark = OPERAND(scan)[0]; int cmp = OPERAND(scan)[1]; - pos_T *pos; + pos_T *pos; + size_t col = REG_MULTI ? rex.input - rex.line : 0; pos = getmark_buf(rex.reg_buf, mark, false); + + // Line may have been freed, get it again. + if (REG_MULTI) { + rex.line = reg_getline(rex.lnum); + rex.input = rex.line + col; + } + if (pos == NULL // mark doesn't exist || pos->lnum <= 0) { // mark isn't set in reg_buf status = RA_NOMATCH; diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index a0f5ebfb9f..cbd45696a9 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -789,10 +789,17 @@ endfunc func Test_using_mark_position() " this was using freed memory + " new engine new norm O0 call assert_fails("s/\\%')", 'E486:') bwipe! + + " old engine + new + norm O0 + call assert_fails("s/\\%#=1\\%')", 'E486:') + bwipe! endfunc func Test_using_visual_position() -- cgit From e5428d10b5a49e9395190482ff35bbac0c1117ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 08:32:12 +0800 Subject: vim-patch:8.1.0439: recursive use of getcmdline() still not protected (#17726) Problem: Recursive use of getcmdline() still not protected. Solution: Instead of saving the command buffer when making a call which may cause recursiveness, save the buffer when actually being called recursively. https://github.com/vim/vim/commit/438d176e35c16d56ff3bb7a80300197ce5a30c4f Co-authored-by: zeertzjq Clear ccline earlier in save_cmdline() if ccline is in use so that ccline.prev_ccline can be assigned. --- src/nvim/ex_getln.c | 144 +++++++++++++++++++++------------------------------- src/nvim/getchar.c | 5 -- src/nvim/main.c | 2 + 3 files changed, 59 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 84fca137d2..1d02c74c41 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -192,13 +192,12 @@ typedef struct command_line_state { typedef struct cmdline_info CmdlineInfo; -/* The current cmdline_info. It is initialized in getcmdline() and after that - * used by other functions. When invoking getcmdline() recursively it needs - * to be saved with save_cmdline() and restored with restore_cmdline(). - * TODO: make it local to getcmdline() and pass it around. */ +/// The current cmdline_info. It is initialized in getcmdline() and after that +/// used by other functions. When invoking getcmdline() recursively it needs +/// to be saved with save_cmdline() and restored with restore_cmdline(). static struct cmdline_info ccline; -static int cmd_showtail; // Only show path tail in lists ? +static int cmd_showtail; // Only show path tail in lists ? static int new_cmdpos; // position set by set_cmdline_pos() @@ -732,9 +731,10 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool /// Internal entry point for cmdline mode. /// -/// caller must use save_cmdline and restore_cmdline. Best is to use -/// getcmdline or getcmdline_prompt, instead of calling this directly. -static uint8_t *command_line_enter(int firstc, long count, int indent) +/// @param count only used for incremental search +/// @param indent indent for inside conditionals +/// @param init_ccline clear ccline first +static uint8_t *command_line_enter(int firstc, long count, int indent, bool init_ccline) { // can be invoked recursively, identify each level static int cmdline_level = 0; @@ -751,6 +751,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) CommandLineState *s = &state; s->save_p_icm = vim_strsave(p_icm); init_incsearch_state(&s->is_state); + CmdlineInfo save_ccline; + bool did_save_ccline = false; + + if (ccline.cmdbuff != NULL) { + // Currently ccline can never be in use if init_ccline is false. + // Some changes will be needed if this is no longer the case. + assert(init_ccline); + // Being called recursively. Since ccline is global, we need to save + // the current buffer and restore it when returning. + save_cmdline(&save_ccline); + did_save_ccline = true; + } else if (init_ccline) { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } if (s->firstc == -1) { s->firstc = NUL; @@ -997,6 +1011,13 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } cmdline_level--; + + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } else { + ccline.cmdbuff = NULL; + } + return p; } @@ -1340,15 +1361,9 @@ static int command_line_execute(VimState *state, int key) s->c = get_expr_register(); if (s->c == '=') { - // Need to save and restore ccline. And set "textlock" - // to avoid nasty things like going to another buffer when - // evaluating an expression. - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); textlock++; p = get_expr_line(); textlock--; - restore_cmdline(&save_ccline); if (p != NULL) { len = (int)STRLEN(p); @@ -1889,10 +1904,7 @@ static int command_line_handle_key(CommandLineState *s) beep_flush(); s->c = ESC; } else { - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); s->c = get_expr_register(); - restore_cmdline(&save_ccline); } } @@ -2120,7 +2132,7 @@ static int command_line_handle_key(CommandLineState *s) int len = 0; int old_firstc; - xfree(ccline.cmdbuff); + XFREE_CLEAR(ccline.cmdbuff); s->xpc.xp_context = EXPAND_NOTHING; if (s->hiscnt == hislen) { p = s->lookfor; // back to the old one @@ -2403,14 +2415,7 @@ static void abandon_cmdline(void) /// @param indent indent for inside conditionals char_u *getcmdline(int firstc, long count, int indent, bool do_concat FUNC_ATTR_UNUSED) { - // Be prepared for situations where cmdline can be invoked recursively. - // That includes cmd mappings, event handlers, as well as update_screen() - // (custom status line eval), which all may invoke ":normal :". - CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - char_u *retval = command_line_enter(firstc, count, indent); - restore_cmdline(&save_ccline); - return retval; + return command_line_enter(firstc, count, indent, true); } /// Get a command line with a prompt @@ -2434,8 +2439,14 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a const int msg_col_save = msg_col; CmdlineInfo save_ccline; - save_cmdline(&save_ccline); - + bool did_save_ccline = false; + if (ccline.cmdbuff != NULL) { + // Save the values of the current cmdline and restore them below. + save_cmdline(&save_ccline); + did_save_ccline = true; + } else { + memset(&ccline, 0, sizeof(struct cmdline_info)); + } ccline.prompt_id = last_prompt_id++; ccline.cmdprompt = (char_u *)prompt; ccline.cmdattr = attr; @@ -2447,9 +2458,11 @@ char *getcmdline_prompt(const char firstc, const char *const prompt, const int a int msg_silent_saved = msg_silent; msg_silent = 0; - char *const ret = (char *)command_line_enter(firstc, 1L, 0); + char *const ret = (char *)command_line_enter(firstc, 1L, 0, false); - restore_cmdline(&save_ccline); + if (did_save_ccline) { + restore_cmdline(&save_ccline); + } msg_silent = msg_silent_saved; // Restore msg_col, the prompt from input() may have changed it. // But only if called recursively and the commandline is therefore being @@ -2601,7 +2614,6 @@ bool cmdline_at_end(void) /* * Allocate a new command line buffer. * Assigns the new buffer to ccline.cmdbuff and ccline.cmdbufflen. - * Returns the new value of ccline.cmdbuff and ccline.cmdbufflen. */ static void alloc_cmdbuff(int len) { @@ -3367,53 +3379,23 @@ void put_on_cmdline(char_u *str, int len, int redraw) } } -/* - * Save ccline, because obtaining the "=" register may execute "normal :cmd" - * and overwrite it. But get_cmdline_str() may need it, thus make it - * available globally in prev_ccline. - */ +/// Save ccline, because obtaining the "=" register may execute "normal :cmd" +/// and overwrite it. static void save_cmdline(struct cmdline_info *ccp) { *ccp = ccline; + memset(&ccline, 0, sizeof(struct cmdline_info)); ccline.prev_ccline = ccp; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; - ccline.xpc = NULL; - ccline.special_char = NUL; - ccline.level = 0; + ccline.cmdbuff = NULL; // signal that ccline is not in use } -/* - * Restore ccline after it has been saved with save_cmdline(). - */ +/// Restore ccline after it has been saved with save_cmdline(). static void restore_cmdline(struct cmdline_info *ccp) FUNC_ATTR_NONNULL_ALL { ccline = *ccp; } -/* - * Save the command line into allocated memory. Returns a pointer to be - * passed to restore_cmdline_alloc() later. - */ -char_u *save_cmdline_alloc(void) - FUNC_ATTR_NONNULL_RET -{ - struct cmdline_info *p = xmalloc(sizeof(struct cmdline_info)); - save_cmdline(p); - return (char_u *)p; -} - -/* - * Restore the command line from the return value of save_cmdline_alloc(). - */ -void restore_cmdline_alloc(char_u *p) - FUNC_ATTR_NONNULL_ALL -{ - restore_cmdline((struct cmdline_info *)p); - xfree(p); -} - /// Paste a yank register into the command line. /// Used by CTRL-R command in command-line mode. /// insert_reg() can't be used here, because special characters from the @@ -3429,7 +3411,6 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) char_u *arg; char_u *p; bool allocated; - struct cmdline_info save_ccline; // check for valid regname; also accept special characters for CTRL-R in // the command line @@ -3447,13 +3428,11 @@ static bool cmdline_paste(int regname, bool literally, bool remcr) } - // Need to save and restore ccline. And set "textlock" to avoid nasty - // things like going to another buffer when evaluating an expression. - save_cmdline(&save_ccline); + // Need to set "textlock" to avoid nasty things like going to another + // buffer when evaluating an expression. textlock++; const bool i = get_spec_reg(regname, &arg, &allocated, true); textlock--; - restore_cmdline(&save_ccline); if (i) { // Got the value of a special register in "arg". @@ -5307,7 +5286,6 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T typval_T args[4]; char_u *pat = NULL; const sctx_T save_current_sctx = current_sctx; - struct cmdline_info save_ccline; if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) { return NULL; @@ -5329,15 +5307,10 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T args[1].vval.v_string = xp->xp_line; args[2].vval.v_number = xp->xp_col; - // Save the cmdline, we don't know what the function may do. - save_ccline = ccline; - ccline.cmdbuff = NULL; - ccline.cmdprompt = NULL; current_sctx = xp->xp_script_ctx; void *const ret = user_expand_func(xp->xp_arg, 3, args); - ccline = save_ccline; current_sctx = save_current_sctx; if (ccline.cmdbuff != NULL) { ccline.cmdbuff[ccline.cmdlen] = keep; @@ -5947,10 +5920,8 @@ int get_history_idx(int histype) } -/* - * Get pointer to the command line info to use. cmdline_paste() may clear - * ccline and put the previous value in prev_ccline. - */ +/// Get pointer to the command line info to use. save_cmdline() may clear +/// ccline and put the previous value in ccline.prev_ccline. static struct cmdline_info *get_ccline_ptr(void) { if ((State & CMDLINE) == 0) { @@ -6350,6 +6321,11 @@ int hist_type2char(int type) return NUL; } +void cmdline_init(void) +{ + memset(&ccline, 0, sizeof(struct cmdline_info)); +} + /// Open a window on the current command line and history. Allow editing in /// the window. Returns when the window is closed. /// Returns: @@ -6358,7 +6334,6 @@ int hist_type2char(int type) /// K_IGNORE if editing continues static int open_cmdwin(void) { - struct cmdline_info save_ccline; bufref_T old_curbuf; bufref_T bufref; win_T *old_curwin = curwin; @@ -6459,9 +6434,6 @@ static int open_cmdwin(void) } redraw_later(curwin, SOME_VALID); - // Save the command line info, can be used recursively. - save_cmdline(&save_ccline); - // No Ex mode here! exmode_active = false; @@ -6499,8 +6471,6 @@ static int open_cmdwin(void) // Restore KeyTyped in case it is modified by autocommands KeyTyped = save_KeyTyped; - // Restore the command line info. - restore_cmdline(&save_ccline); cmdwin_type = 0; cmdwin_level = 0; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index c10172cc52..b12b99b7ee 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -4001,7 +4001,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) char_u *res; char_u *p = NULL; char_u *expr = NULL; - char_u *save_cmd; pos_T save_cursor; int save_msg_col; int save_msg_row; @@ -4013,8 +4012,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) vim_unescape_ks(expr); } - save_cmd = save_cmdline_alloc(); - // Forbid changing text or using ":normal" to avoid most of the bad side // effects. Also restore the cursor position. textlock++; @@ -4045,8 +4042,6 @@ static char_u *eval_map_expr(mapblock_T *mp, int c) msg_col = save_msg_col; msg_row = save_msg_row; - restore_cmdline_alloc(save_cmd); - if (p == NULL) { return NULL; } diff --git a/src/nvim/main.c b/src/nvim/main.c index afb9313cba..6ea1cb0875 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -20,6 +20,7 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" +#include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" @@ -156,6 +157,7 @@ bool event_teardown(void) void early_init(mparm_T *paramp) { env_init(); + cmdline_init(); eval_init(); // init global variables init_path(argv0 ? argv0 : "nvim"); init_normal_cmds(); // Init the table of Normal mode commands. -- cgit From 1bbe8ec2820497b8a61adcd138da350445b0ad92 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 16:42:24 +0800 Subject: vim-patch:8.2.3110: a pattern that matches the cursor position is complicated Problem: A pattern that matches the cursor position is bit complicated. Solution: Use a dot to indicate the cursor line and column. (Christian Brabandt, closes vim/vim#8497, closes vim/vim#8179) https://github.com/vim/vim/commit/04db26b36000a4677b95403ec94bd11f6cc73975 Also use `n = ++vcol` in regexp_bt.c as `++vcol` alone fails lint. --- src/nvim/regexp.c | 6 ++- src/nvim/regexp_bt.c | 28 +++++++++-- src/nvim/regexp_nfa.c | 23 ++++++++- src/nvim/testdir/test_regexp_latin.vim | 90 ++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 009a26d4e0..d3c43e530a 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -107,8 +107,10 @@ static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)"); static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here"); static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here"); static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%["); -static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); -static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); +static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); +static char_u e_recursive[] = N_("E956: Cannot use pattern recursively"); +static char_u e_regexp_number_after_dot_pos_search[] + = N_("E1204: No Number allowed after .: '\\%%%c'"); #define NOT_MULTI 0 #define MULTI_ONE 1 diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c index 3835a2bbae..7d0b9a7a77 100644 --- a/src/nvim/regexp_bt.c +++ b/src/nvim/regexp_bt.c @@ -1578,14 +1578,19 @@ static char_u *regatom(int *flagp) } default: - if (ascii_isdigit(c) || c == '<' || c == '>' - || c == '\'') { + if (ascii_isdigit(c) || c == '<' || c == '>' || c == '\'' || c == '.') { uint32_t n = 0; int cmp; + bool cur = false; cmp = c; - if (cmp == '<' || cmp == '>') + if (cmp == '<' || cmp == '>') { c = getchr(); + } + if (no_Magic(c) == '.') { + cur = true; + c = getchr(); + } while (ascii_isdigit(c)) { n = n * 10 + (uint32_t)(c - '0'); c = getchr(); @@ -1602,14 +1607,31 @@ static char_u *regatom(int *flagp) } break; } else if (c == 'l' || c == 'c' || c == 'v') { + if (cur && n) { + semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c)); + rc_did_emsg = true; + return NULL; + } if (c == 'l') { + if (cur) { + n = curwin->w_cursor.lnum; + } ret = regnode(RE_LNUM); if (save_prev_at_start) { at_start = true; } } else if (c == 'c') { + if (cur) { + n = curwin->w_cursor.col; + n++; + } ret = regnode(RE_COL); } else { + if (cur) { + colnr_T vcol = 0; + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); + n = ++vcol; + } ret = regnode(RE_VCOL); } if (ret == JUST_CALC_SIZE) { diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 7e316624f8..67c11451f6 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1639,10 +1639,19 @@ static int nfa_regatom(void) { int64_t n = 0; const int cmp = c; + bool cur = false; - if (c == '<' || c == '>') + if (c == '<' || c == '>') { c = getchr(); + } + if (no_Magic(c) == '.') { + cur = true; + c = getchr(); + } while (ascii_isdigit(c)) { + if (cur) { + semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c)); + } if (n > (INT32_MAX - (c - '0')) / 10) { // overflow. emsg(_(e_value_too_large)); @@ -1655,6 +1664,9 @@ static int nfa_regatom(void) int32_t limit = INT32_MAX; if (c == 'l') { + if (cur) { + n = curwin->w_cursor.lnum; + } // \%{n}l \%{n}l EMIT(cmp == '<' ? NFA_LNUM_LT : cmp == '>' ? NFA_LNUM_GT : NFA_LNUM); @@ -1662,10 +1674,19 @@ static int nfa_regatom(void) at_start = true; } } else if (c == 'c') { + if (cur) { + n = curwin->w_cursor.col; + n++; + } // \%{n}c \%{n}c EMIT(cmp == '<' ? NFA_COL_LT : cmp == '>' ? NFA_COL_GT : NFA_COL); } else { + if (cur) { + colnr_T vcol = 0; + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); + n = ++vcol; + } // \%{n}v \%{n}v EMIT(cmp == '<' ? NFA_VCOL_LT : cmp == '>' ? NFA_VCOL_GT : NFA_VCOL); diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index cbd45696a9..b733e334de 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -787,6 +787,96 @@ func Test_regexp_error() set re& endfunc +" Check patterns matching cursor position. +func s:curpos_test2() + new + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 2, 10, 0]) + s/\%.c.*//g + call setpos('.', [0, 3, 15, 0]) + s/\%.l.*//g + call setpos('.', [0, 5, 3, 0]) + s/\%.v.*/_/g + call assert_equal(['1', + \ '2 foobar ', + \ '', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 _', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse'], + \ getline(1, '$')) + call assert_fails('call search("\\%.1l")', 'E1204:') + call assert_fails('call search("\\%.1c")', 'E1204:') + call assert_fails('call search("\\%.1v")', 'E1204:') + bwipe! +endfunc + +" Check patterns matching before or after cursor position. +func s:curpos_test3() + new + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 2, 10, 0]) + " Note: This removes all columns, except for the column directly in front of + " the cursor. Bug???? + :s/^.*\%<.c// + call setpos('.', [0, 3, 10, 0]) + :s/\%>.c.*$// + call setpos('.', [0, 5, 4, 0]) + " Note: This removes all columns, except for the column directly in front of + " the cursor. Bug???? + :s/^.*\%<.v/_/ + call setpos('.', [0, 6, 4, 0]) + :s/\%>.v.*$/_/ + call assert_equal(['1', + \ ' eins zwei drei vier fünf sechse', + \ '3 foobar e', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '_foobar eins zwei drei vier fünf sechse', + \ '6 fo_', + \ '7 foobar eins zwei drei vier fünf sechse'], + \ getline(1, '$')) + sil %d + call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse', + \ '3 foobar eins zwei drei vier fünf sechse', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '6 foobar eins zwei drei vier fünf sechse', + \ '7 foobar eins zwei drei vier fünf sechse']) + call setpos('.', [0, 4, 4, 0]) + %s/\%<.l.*// + call setpos('.', [0, 5, 4, 0]) + %s/\%>.l.*// + call assert_equal(['', '', '', + \ '4 foobar eins zwei drei vier fünf sechse', + \ '5 foobar eins zwei drei vier fünf sechse', + \ '', ''], + \ getline(1, '$')) + bwipe! +endfunc + +" Test that matching below, at or after the +" cursor position work +func Test_matching_pos() + for val in range(3) + exe "set re=" .. val + " Match at cursor position + call s:curpos_test2() + " Match before or after cursor position + call s:curpos_test3() + endfor + set re& +endfunc + func Test_using_mark_position() " this was using freed memory " new engine -- cgit From 465f8ff8cbd3f9b73c1de8823505ee6a804b2bf1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 19 Mar 2022 19:37:08 +0800 Subject: vim-patch:8.2.4592: search continues after giving E1204 Problem: Search continues after giving E1204. Solution: Return failure after giving E1204. (closes vim/vim#9972) https://github.com/vim/vim/commit/b10ff5c1b3581ed4990d196bed51b4a8f961e8a2 --- src/nvim/regexp_nfa.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 67c11451f6..8a14a2864c 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1651,6 +1651,7 @@ static int nfa_regatom(void) while (ascii_isdigit(c)) { if (cur) { semsg(_(e_regexp_number_after_dot_pos_search), no_Magic(c)); + return FAIL; } if (n > (INT32_MAX - (c - '0')) / 10) { // overflow. -- cgit From 4ca14b310cc385bd7ebb6e478ee5cc249f26556e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 18:34:35 +0800 Subject: vim-patch:8.2.4349: FileChangedShell test fails on MS-Windows Problem: FileChangedShell test fails on MS-Windows. Solution: Skip the test on MS-Windows. https://github.com/vim/vim/commit/c9e3187d053dcef03d11915b06be0c78ab45bc75 --- src/nvim/testdir/test_filechanged.vim | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index 06ccd6e85f..e9ba71c12d 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -142,6 +142,8 @@ endfunc func Test_FileChangedShell_edit_dialog() throw 'Skipped: requires a UI to be active' CheckNotGui + " FIXME: why does this not work on MS-Windows? + CheckUnix new Xchanged_r call setline(1, 'reload this') -- cgit From 06cf205aced6b0d5b7ae919665f2ab3fd81ab960 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 18:30:36 +0800 Subject: vim-patch:8.2.4649: various formatting problems Problem: Various formatting problems. Solution: Improve the code formatting. https://github.com/vim/vim/commit/b4ad3b0deac12674a7773311890b48fd39c6807c --- src/nvim/ops.c | 2 +- src/nvim/quickfix.c | 1 + src/nvim/regexp_nfa.c | 6 ++++-- src/nvim/testdir/test_filechanged.vim | 3 +-- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4404ee7b80..dab6d237bf 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3893,7 +3893,7 @@ void ex_display(exarg_T *eap) msg_puts_attr("^J", attr); n -= 2; } - for (p = yb->y_array[j]; *p && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 + for (p = yb->y_array[j]; *p != NUL && (n -= ptr2cells(p)) >= 0; p++) { // -V1019 clen = utfc_ptr2len(p); msg_outtrans_len(p, clen); p += clen - 1; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 9ee7b1cc8a..c8d9e91fb5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2780,6 +2780,7 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int // present. if (qfl_type == QFLT_LOCATION) { win_T *wp = win_id2wp(prev_winid); + if (wp == NULL && curwin->w_llist != qi) { emsg(_("E924: Current window was closed")); *opened_window = false; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 8a14a2864c..a8d6e9c40b 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -6249,8 +6249,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MARK_GT: case NFA_MARK_LT: { - size_t col = rex.input - rex.line; - pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false); + pos_T *pos; + size_t col = REG_MULTI ? rex.input - rex.line : 0; + + pos = getmark_buf(rex.reg_buf, t->state->val, false); // Line may have been freed, get it again. if (REG_MULTI) { diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim index e9ba71c12d..8e1cb2c3ee 100644 --- a/src/nvim/testdir/test_filechanged.vim +++ b/src/nvim/testdir/test_filechanged.vim @@ -142,8 +142,7 @@ endfunc func Test_FileChangedShell_edit_dialog() throw 'Skipped: requires a UI to be active' CheckNotGui - " FIXME: why does this not work on MS-Windows? - CheckUnix + CheckUnix " Using low level feedkeys() does not work on MS-Windows. new Xchanged_r call setline(1, 'reload this') -- cgit From 7fb2310edb8f2fae1ddd175ef4bd6508ca3ce7e3 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 30 Mar 2022 11:59:36 -0400 Subject: fix: set nested before executing callback (#17801) --- src/nvim/autocmd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index a36f2c97b5..d4af05b961 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2022,6 +2022,11 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) verbose_leave_scroll(); } + // Make sure to set autocmd_nested before executing + // lua code, so that it works properly + autocmd_nested = ac->nested; + current_sctx = ac->script_ctx; + if (ac->exec.type == CALLABLE_CB) { typval_T argsin = TV_INITIAL_VALUE; typval_T rettv = TV_INITIAL_VALUE; @@ -2052,8 +2057,6 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) if (oneshot) { aucmd_del(ac); } - autocmd_nested = ac->nested; - current_sctx = ac->script_ctx; if (ac->last) { acp->nextcmd = NULL; } else { -- cgit From 0d4bd420c19e3a81b494ec1f58cffde53d9b84ea Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Mar 2022 08:40:17 +0800 Subject: fix: correct vertical dragging room calculation with global statusline (#17928) This fixes the bug that win_move_statusline() or mouse dragging cannot reduce 'cmdheight' to 1 when global statusline is used. --- src/nvim/window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/window.c b/src/nvim/window.c index 6bd2ef3ef6..632e9b3f44 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5857,11 +5857,11 @@ void win_drag_status_line(win_T *dragwin, int offset) } else { // drag down up = false; // Only dragging the last status line can reduce p_ch. - room = Rows - cmdline_row - global_stl_height(); + room = Rows - cmdline_row; if (curfr->fr_next == NULL) { room -= 1; } else { - room -= p_ch; + room -= p_ch + global_stl_height(); } if (room < 0) { room = 0; -- cgit From b6e3a2dbbb4c408c21dc58723d8dd3d68053f0cb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Mar 2022 10:04:12 +0800 Subject: vim-patch:8.2.4645: 'shortmess' changed when session does not store options (#17908) Problem: 'shortmess' changed when session does not store options. Solution: Save and restore 'shortmess' if needed. (James Charti, closes vim/vim#10037) https://github.com/vim/vim/commit/fd01280d01c2270a320d8c962d24140a8176a400 --- src/nvim/ex_session.c | 32 ++++++++++++++++++--------- src/nvim/testdir/test_mksession.vim | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 39ff75d8c2..1235087500 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -589,12 +589,18 @@ static int makeopens(FILE *fd, char_u *dirnow) "if expand('%') == '' && !&modified && line('$') <= 1" " && getline(1) == ''\n" " let s:wipebuf = bufnr('%')\n" - "endif\n" - // Now save the current files, current buffer first. - "set shortmess=aoO\n") < 0) { + "endif\n") < 0) { return FAIL; } + // save 'shortmess' if not storing options + if ((ssop_flags & SSOP_OPTIONS) == 0) { + PUTLINE_FAIL("let s:shortmess_save = &shortmess"); + } + + // Now save the current files, current buffer first. + PUTLINE_FAIL("set shortmess=aoO"); + // Put all buffers into the buffer list. // Do it very early to preserve buffer order after loading session (which // can be disrupted by prior `edit` or `tabedit` calls). @@ -842,15 +848,21 @@ static int makeopens(FILE *fd, char_u *dirnow) return FAIL; } - // Re-apply 'winheight', 'winwidth' and 'shortmess'. - if (fprintf(fd, - "set winheight=%" PRId64 " winwidth=%" PRId64 - " shortmess=%s\n", - (int64_t)p_wh, - (int64_t)p_wiw, - p_shm) < 0) { + // Re-apply 'winheight' and 'winwidth'. + if (fprintf(fd, "set winheight=%" PRId64 " winwidth=%" PRId64 "\n", + (int64_t)p_wh, (int64_t)p_wiw) < 0) { return FAIL; } + + // Restore 'shortmess'. + if (ssop_flags & SSOP_OPTIONS) { + if (fprintf(fd, "set shortmess=%s\n", p_shm) < 0) { + return FAIL; + } + } else { + PUTLINE_FAIL("let &shortmess = s:shortmess_save"); + } + if (tab_firstwin != NULL && tab_firstwin->w_next != NULL) { // Restore 'winminheight' and 'winminwidth'. PUTLINE_FAIL("let &winminheight = s:save_winminheight"); diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index 798cb9e54f..5dbe2cd366 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -795,6 +795,49 @@ func Test_mksession_winminheight() set sessionoptions& endfunc +" Test for mksession with and without options restores shortmess +func Test_mksession_shortmess() + " Without options + set sessionoptions-=options + split + mksession! Xtest_mks.out + let found_save = 0 + let found_restore = 0 + let lines = readfile('Xtest_mks.out') + for line in lines + let line = trim(line) + + if line ==# 'let s:shortmess_save = &shortmess' + let found_save += 1 + endif + + if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save' + let found_restore += 1 + endif + endfor + call assert_equal(1, found_save) + call assert_equal(1, found_restore) + call delete('Xtest_mks.out') + close + set sessionoptions& + + " With options + set sessionoptions+=options + split + mksession! Xtest_mks.out + let found_restore = 0 + let lines = readfile('Xtest_mks.out') + for line in lines + if line =~# 's:shortmess_save' + let found_restore += 1 + endif + endfor + call assert_equal(0, found_restore) + call delete('Xtest_mks.out') + close + set sessionoptions& +endfunc + " Test for mksession with 'compatible' option func Test_mksession_compatible() throw 'skipped: Nvim does not support "compatible" option' -- cgit From 47630743fc67c56f724cd99660d86d8c4ea7782f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 21:11:53 +0800 Subject: vim-patch:8.2.1844: using "q" at the more prompt doesn't stop a long message Problem: Using "q" at the more prompt doesn't stop a long message. Solution: Check for "got_int". (closes vim/vim#7122) https://github.com/vim/vim/commit/3d30af8783bf43fbfece641ec81ad8d2f01b3735 Cherry-pick file name change from patch 8.2.2112. --- src/nvim/message.c | 10 ++++++++-- src/nvim/testdir/test_messages.vim | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index b3fefbc0f4..7b90f882ab 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1486,6 +1486,10 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) char_u *s; int mb_l; int c; + int save_got_int = got_int; + + // Only quit when got_int was set in here. + got_int = false; // if MSG_HIST flag set, add message to history if (attr & MSG_HIST) { @@ -1503,7 +1507,7 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) * Go over the string. Special characters are translated and printed. * Normal characters are printed several at a time. */ - while (--len >= 0) { + while (--len >= 0 && !got_int) { // Don't include composing chars after the end. mb_l = utfc_ptr2len_len((char_u *)str, len + 1); if (mb_l > 1) { @@ -1542,11 +1546,13 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr) } } - if (str > plain_start) { + if (str > plain_start && !got_int) { // Print the printable chars at the end. msg_puts_attr_len(plain_start, str - plain_start, attr); } + got_int |= save_got_int; + return retval; } diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 9c84d77dd2..65de0bb169 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -2,6 +2,9 @@ source check.vim source shared.vim +source term_util.vim +source view_util.vim +source screendump.vim func Test_messages() let oldmore = &more @@ -109,6 +112,22 @@ func Test_echospace() set ruler& showcmd& endfunc +func Test_quit_long_message() + CheckScreendump + + let content =<< trim END + echom range(9999)->join("\x01") + END + call writefile(content, 'Xtest_quit_message') + let buf = RunVimInTerminal('-S Xtest_quit_message', #{rows: 6}) + call term_sendkeys(buf, "q") + call VerifyScreenDump(buf, 'Test_quit_long_message', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_quit_message') +endfunc + " this was missing a terminating NUL func Test_echo_string_partial() function CountSpaces() -- cgit From e2247c0baa78570f7cb81695c28394ce0be73825 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 21:37:51 +0800 Subject: vim-patch:8.2.2515: memory access error when truncating an empty message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Memory access error when truncating an empty message. Solution: Check for an empty string. (Dominique Pellé, closes vim/vim#7841) https://github.com/vim/vim/commit/6281815eccc3ded54960f7798833ceb39561b9a0 --- src/nvim/message.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 7b90f882ab..3ad38f7b77 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -382,6 +382,13 @@ void trunc_string(char_u *s, char_u *buf, int room_in, int buflen) int i; int n; + if (*s == NUL) { + if (buflen > 0) { + *buf = NUL; + } + return; + } + if (room_in < 3) { room = 0; } -- cgit From d5dee83552033506a308cac999a8c9f08e98e6b1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 21:41:03 +0800 Subject: vim-patch:8.2.4156: fileinfo message overwrites echo'ed message Problem: Fileinfo message overwrites echo'ed message. Solution: Reset need_fileinfo when displaying a message. (Rob Pilling, closes vim/vim#9569) https://github.com/vim/vim/commit/726f7f91fd17e3e7eb39614a20d10ea83c134df0 --- src/nvim/message.c | 8 ++++++-- src/nvim/testdir/test_messages.vim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/message.c b/src/nvim/message.c index 3ad38f7b77..6708001495 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -327,11 +327,12 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline) } retval = msg_end(); - if (keep && retval && vim_strsize((char_u *)s) < (Rows - cmdline_row - 1) - * Columns + sc_col) { + if (keep && retval && vim_strsize((char_u *)s) < (Rows - cmdline_row - 1) * Columns + sc_col) { set_keep_msg((char *)s, 0); } + need_fileinfo = false; + xfree(buf); --entered; return retval; @@ -1355,6 +1356,7 @@ void msg_start(void) if (!msg_silent) { XFREE_CLEAR(keep_msg); // don't display old message now + need_fileinfo = false; } if (need_clr_eos) { @@ -2026,6 +2028,8 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) if (!msg_use_printf() || (headless_mode && default_grid.chars)) { msg_puts_display((const char_u *)str, len, attr, false); } + + need_fileinfo = false; } /// Print a formatted message diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 65de0bb169..f8c6ee78e6 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -135,3 +135,35 @@ func Test_echo_string_partial() call assert_equal("function('CountSpaces', [{'ccccccccccc': ['ab', 'cd'], 'aaaaaaaaaaa': v:false, 'bbbbbbbbbbbb': ''}])", string(function('CountSpaces', [#{aaaaaaaaaaa: v:false, bbbbbbbbbbbb: '', ccccccccccc: ['ab', 'cd']}]))) endfunc +" Message output was previously overwritten by the fileinfo display, shown +" when switching buffers. If a buffer is switched to, then a message if +" echoed, we should show the message, rather than overwriting it with +" fileinfo. +func Test_fileinfo_after_echo() + CheckScreendump + + let content =<< trim END + file a.txt + + hide edit b.txt + call setline(1, "hi") + setlocal modified + + hide buffer a.txt + + set updatetime=1 + autocmd CursorHold * b b.txt | w | echo "'b' written" + END + + call writefile(content, 'Xtest_fileinfo_after_echo') + let buf = RunVimInTerminal('-S Xtest_fileinfo_after_echo', #{rows: 6}) + call VerifyScreenDump(buf, 'Test_fileinfo_after_echo', {}) + + call term_sendkeys(buf, ":q\") + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_fileinfo_after_echo') +endfunc + +" vim: shiftwidth=2 sts=2 expandtab -- cgit From 5a3a1304e1e8b1fe8a2f8d0ccd70390881777bfe Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 21:54:23 +0800 Subject: vim-patch:8.2.4200: some tests do not clean up properly Problem: Some tests do not clean up properly. Solution: Delete created files. (Yegappan Lakshmanan, closes vim/vim#9611) https://github.com/vim/vim/commit/7e765a39b795d5331bf2d4927b41df7b78915af9 Omit test_filetype.vim: already ported Omit test_vim9_import.vim: N/A --- src/nvim/testdir/test_messages.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index f8c6ee78e6..7c6af32e82 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -164,6 +164,7 @@ func Test_fileinfo_after_echo() " clean up call StopVimInTerminal(buf) call delete('Xtest_fileinfo_after_echo') + call delete('b.txt') endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 86f81c471a2acd87854eb11e20ddbebe244ff7c0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 30 Mar 2022 21:59:55 +0800 Subject: vim-patch:8.2.4577: message test is flaky Problem: Message test is flaky. (Elimar Riesebieter) Solution: Trigger the autocommand event only after startup is finished. https://github.com/vim/vim/commit/9323ca51c2b1522f26907a7b8879067245ebd1be --- src/nvim/testdir/test_messages.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 7c6af32e82..82c4cc128b 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -151,12 +151,13 @@ func Test_fileinfo_after_echo() hide buffer a.txt - set updatetime=1 - autocmd CursorHold * b b.txt | w | echo "'b' written" + autocmd CursorHold * buf b.txt | w | echo "'b' written" END call writefile(content, 'Xtest_fileinfo_after_echo') let buf = RunVimInTerminal('-S Xtest_fileinfo_after_echo', #{rows: 6}) + call term_sendkeys(buf, ":set updatetime=50\") + call term_sendkeys(buf, "0$") call VerifyScreenDump(buf, 'Test_fileinfo_after_echo', {}) call term_sendkeys(buf, ":q\") -- cgit From b80651eda9c50d4e438f02af9311b18c5c202656 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Sat, 12 Mar 2022 15:12:02 -0500 Subject: feat(api): nvim_clear_autocmd Co-authored-by: Christian Clason --- src/nvim/api/autocmd.c | 291 +++++++++++++++++++++++++++++------------ src/nvim/api/keysets.lua | 8 +- src/nvim/api/private/helpers.h | 5 +- 3 files changed, 217 insertions(+), 87 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 49f6f98ba4..482aa80f1e 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -392,9 +392,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc FUNC_API_SINCE(9) { int64_t autocmd_id = -1; - - const char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; - int au_group = AUGROUP_DEFAULT; char *desc = NULL; Array patterns = ARRAY_DICT_INIT; @@ -404,7 +401,7 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc Callback cb = CALLBACK_NONE; - if (!unpack_string_or_array(&event_array, &event, "event", err)) { + if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { goto cleanup; } @@ -466,84 +463,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc bool is_once = api_object_to_bool(opts->once, "once", false, err); bool is_nested = api_object_to_bool(opts->nested, "nested", false, err); - switch (opts->group.type) { - case kObjectTypeNil: - break; - case kObjectTypeString: - au_group = augroup_find(opts->group.data.string.data); - if (au_group == AUGROUP_ERROR) { - api_set_error(err, - kErrorTypeValidation, - "invalid augroup: %s", opts->group.data.string.data); - goto cleanup; - } - break; - case kObjectTypeInteger: - au_group = (int)opts->group.data.integer; - char *name = augroup_name(au_group); - if (!augroup_exists(name)) { - api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); - goto cleanup; - } - break; - default: - api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + int au_group = get_augroup_from_object(opts->group, err); + if (au_group == AUGROUP_ERROR) { goto cleanup; } - if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { - api_set_error(err, kErrorTypeValidation, - "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { goto cleanup; - } else if (opts->pattern.type != kObjectTypeNil) { - Object *v = &opts->pattern; - - if (v->type == kObjectTypeString) { - char_u *pat = (char_u *)v->data.string.data; - size_t patlen = aucmd_pattern_length(pat); - while (patlen) { - ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); - - pat = aucmd_next_pattern(pat, patlen); - patlen = aucmd_pattern_length(pat); - } - } else if (v->type == kObjectTypeArray) { - if (!check_autocmd_string_array(patterns, "pattern", err)) { - goto cleanup; - } - - Array array = v->data.array; - for (size_t i = 0; i < array.size; i++) { - char_u *pat = (char_u *)array.items[i].data.string.data; - size_t patlen = aucmd_pattern_length(pat); - while (patlen) { - ADD(patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); - - pat = aucmd_next_pattern(pat, patlen); - patlen = aucmd_pattern_length(pat); - } - } - } else { - api_set_error(err, - kErrorTypeValidation, - "'pattern' must be a string"); - goto cleanup; - } - } else if (opts->buffer.type != kObjectTypeNil) { - if (opts->buffer.type != kObjectTypeInteger) { - api_set_error(err, - kErrorTypeValidation, - "'buffer' must be an integer"); - goto cleanup; - } - - buf_T *buf = find_buffer_by_handle((Buffer)opts->buffer.data.integer, err); - if (ERROR_SET(err)) { - goto cleanup; - } - - snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); - ADD(patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); } if (opts->desc.type != kObjectTypeNil) { @@ -615,6 +541,94 @@ void nvim_del_autocmd(Integer id) autocmd_delete_id(id); } +/// Clear all autocommands that match the corresponding {opts}. To delete +/// a particular autocmd, see |nvim_del_autocmd|. +/// @param opts Parameters +/// - event: (string|table) +/// Examples: +/// - event: "pat1" +/// - event: { "pat1" } +/// - event: { "pat1", "pat2", "pat3" } +/// - pattern: (string|table) +/// - pattern or patterns to match exactly. +/// - For example, if you have `*.py` as that pattern for the autocmd, +/// you must pass `*.py` exactly to clear it. `test.py` will not +/// match the pattern. +/// - defaults to clearing all patterns. +/// - NOTE: Cannot be used with {buffer} +/// - buffer: (bufnr) +/// - clear only |autocmd-buflocal| autocommands. +/// - NOTE: Cannot be used with {pattern} +/// - group: (string|int) The augroup name or id. +/// - NOTE: If not passed, will only delete autocmds *not* in any group. +/// +void nvim_clear_autocmd(Dict(clear_autocmd) *opts, Error *err) + FUNC_API_SINCE(9) +{ + // TODO(tjdevries): Future improvements: + // - once: (boolean) - Only clear autocmds with once. See |autocmd-once| + // - nested: (boolean) - Only clear autocmds with nested. See |autocmd-nested| + // - group: Allow passing "*" or true or something like that to force doing all + // autocmds, regardless of their group. + + Array patterns = ARRAY_DICT_INIT; + Array event_array = ARRAY_DICT_INIT; + + if (!unpack_string_or_array(&event_array, &opts->event, "event", false, err)) { + goto cleanup; + } + + if (opts->pattern.type != kObjectTypeNil && opts->buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "Cannot use both 'pattern' and 'buffer'"); + goto cleanup; + } + + int au_group = get_augroup_from_object(opts->group, err); + if (au_group == AUGROUP_ERROR) { + goto cleanup; + } + + if (!get_patterns_from_pattern_or_buf(&patterns, opts->pattern, opts->buffer, err)) { + goto cleanup; + } + + // When we create the autocmds, we want to say that they are all matched, so that's * + // but when we clear them, we want to say that we didn't pass a pattern, so that's NUL + if (patterns.size == 0) { + ADD(patterns, STRING_OBJ(STATIC_CSTR_TO_STRING(""))); + } + + // If we didn't pass any events, that means clear all events. + if (event_array.size == 0) { + FOR_ALL_AUEVENTS(event) { + FOREACH_ITEM(patterns, pat_object, { + char_u *pat = (char_u *)pat_object.data.string.data; + if (!clear_autocmd(event, pat, au_group, err)) { + goto cleanup; + } + }); + } + } else { + FOREACH_ITEM(event_array, event_str, { + GET_ONE_EVENT(event_nr, event_str, cleanup); + + FOREACH_ITEM(patterns, pat_object, { + char_u *pat = (char_u *)pat_object.data.string.data; + if (!clear_autocmd(event_nr, pat, au_group, err)) { + goto cleanup; + } + }); + }); + } + +cleanup: + api_free_array(event_array); + api_free_array(patterns); + + return; +} + /// Create or get an autocommand group |autocmd-groups|. /// /// To get an existing group id, do: @@ -709,7 +723,7 @@ void nvim_exec_autocmd(Object event, Dict(exec_autocmd) *opts, Error *err) Array event_array = ARRAY_DICT_INIT; - if (!unpack_string_or_array(&event_array, &event, "event", err)) { + if (!unpack_string_or_array(&event_array, &event, "event", true, err)) { goto cleanup; } @@ -808,7 +822,7 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err) return true; } -static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) +static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err) { if (v->type == kObjectTypeString) { ADD(*array, copy_object(*v)); @@ -818,10 +832,119 @@ static bool unpack_string_or_array(Array *array, Object *v, char *k, Error *err) } *array = copy_array(v->data.array); } else { - api_set_error(err, - kErrorTypeValidation, - "'%s' must be an array or a string.", - k); + if (required) { + api_set_error(err, + kErrorTypeValidation, + "'%s' must be an array or a string.", + k); + return false; + } + } + + return true; +} + +// Returns AUGROUP_ERROR if there was a problem with {group} +static int get_augroup_from_object(Object group, Error *err) +{ + int au_group = AUGROUP_ERROR; + + switch (group.type) { + case kObjectTypeNil: + return AUGROUP_DEFAULT; + case kObjectTypeString: + au_group = augroup_find(group.data.string.data); + if (au_group == AUGROUP_ERROR) { + api_set_error(err, + kErrorTypeValidation, + "invalid augroup: %s", group.data.string.data); + + return AUGROUP_ERROR; + } + + return au_group; + case kObjectTypeInteger: + au_group = (int)group.data.integer; + char *name = augroup_name(au_group); + if (!augroup_exists(name)) { + api_set_error(err, kErrorTypeValidation, "invalid augroup: %d", au_group); + return AUGROUP_ERROR; + } + + return au_group; + default: + api_set_error(err, kErrorTypeValidation, "'group' must be a string or an integer."); + return AUGROUP_ERROR; + } +} + +static bool get_patterns_from_pattern_or_buf(Array *patterns, Object pattern, Object buffer, + Error *err) +{ + const char_u pattern_buflocal[BUFLOCAL_PAT_LEN]; + + if (pattern.type != kObjectTypeNil && buffer.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, + "cannot pass both: 'pattern' and 'buffer' for the same autocmd"); + return false; + } else if (pattern.type != kObjectTypeNil) { + Object *v = &pattern; + + if (v->type == kObjectTypeString) { + char_u *pat = (char_u *)v->data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } else if (v->type == kObjectTypeArray) { + if (!check_autocmd_string_array(*patterns, "pattern", err)) { + return false; + } + + Array array = v->data.array; + for (size_t i = 0; i < array.size; i++) { + char_u *pat = (char_u *)array.items[i].data.string.data; + size_t patlen = aucmd_pattern_length(pat); + while (patlen) { + ADD(*patterns, STRING_OBJ(cbuf_to_string((char *)pat, patlen))); + + pat = aucmd_next_pattern(pat, patlen); + patlen = aucmd_pattern_length(pat); + } + } + } else { + api_set_error(err, + kErrorTypeValidation, + "'pattern' must be a string"); + return false; + } + } else if (buffer.type != kObjectTypeNil) { + if (buffer.type != kObjectTypeInteger) { + api_set_error(err, + kErrorTypeValidation, + "'buffer' must be an integer"); + return false; + } + + buf_T *buf = find_buffer_by_handle((Buffer)buffer.data.integer, err); + if (ERROR_SET(err)) { + return false; + } + + snprintf((char *)pattern_buflocal, BUFLOCAL_PAT_LEN, "", (int)buf->handle); + ADD(*patterns, STRING_OBJ(cstr_to_string((char *)pattern_buflocal))); + } + + return true; +} + +static bool clear_autocmd(event_T event, char_u *pat, int au_group, Error *err) +{ + if (do_autocmd_event(event, pat, false, false, (char_u *)"", true, au_group) == FAIL) { + api_set_error(err, kErrorTypeException, "Failed to clear autocmd"); return false; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index 6a29b0fb59..520bb7fbc6 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -123,14 +123,20 @@ return { "nocombine"; }; -- Autocmds + clear_autocmd = { + "buffer"; + "event"; + "group"; + "pattern"; + }; create_autocmd = { "buffer"; "callback"; "command"; "desc"; "group"; - "once"; "nested"; + "once"; "pattern"; }; exec_autocmd = { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index bc7c2e6a60..650349cde7 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -140,8 +140,9 @@ typedef struct { // Useful macro for executing some `code` for each item in an array. #define FOREACH_ITEM(a, __foreach_item, code) \ - for (size_t __foreach_i = 0; __foreach_i < (a).size; __foreach_i++) { \ - Object __foreach_item = (a).items[__foreach_i]; \ + for (size_t (__foreach_item ## _index) = 0; (__foreach_item ## _index) < (a).size; \ + (__foreach_item ## _index)++) { \ + Object __foreach_item = (a).items[__foreach_item ## _index]; \ code; \ } -- cgit From 929293815bc6b9b1b5fdd129970c4e2f7279a6d6 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 31 Mar 2022 09:59:14 +0100 Subject: fix(api): improve autocmd error handling - nvim_del_augroup_* now works with pcall - nvim_del_autocmd now errors for invalid ids --- src/nvim/api/autocmd.c | 28 +++++++++++++++++++++------- src/nvim/autocmd.c | 5 ++++- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 49f6f98ba4..66ac394498 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -609,10 +609,16 @@ cleanup: /// NOTE: Only autocommands created via the API have an id. /// @param id Integer The id returned by nvim_create_autocmd /// @see |nvim_create_autocmd()| -void nvim_del_autocmd(Integer id) +void nvim_del_autocmd(Integer id, Error *err) FUNC_API_SINCE(9) { - autocmd_delete_id(id); + if (id <= 0) { + api_set_error(err, kErrorTypeException, "Invalid autocmd id"); + return; + } + if (!autocmd_delete_id(id)) { + api_set_error(err, kErrorTypeException, "Failed to delete autocmd"); + } } /// Create or get an autocommand group |autocmd-groups|. @@ -664,11 +670,15 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou /// @param id Integer The id of the group. /// @see |nvim_del_augroup_by_name()| /// @see |nvim_create_augroup()| -void nvim_del_augroup_by_id(Integer id) +void nvim_del_augroup_by_id(Integer id, Error *err) FUNC_API_SINCE(9) { - char *name = augroup_name((int)id); - augroup_del(name, false); + TRY_WRAP({ + try_start(); + char *name = augroup_name((int)id); + augroup_del(name, false); + try_end(err); + }); } /// Delete an autocommand group by name. @@ -677,10 +687,14 @@ void nvim_del_augroup_by_id(Integer id) /// this group will also be deleted and cleared. This group will no longer exist. /// @param name String The name of the group. /// @see |autocommand-groups| -void nvim_del_augroup_by_name(String name) +void nvim_del_augroup_by_name(String name, Error *err) FUNC_API_SINCE(9) { - augroup_del(name.data, false); + TRY_WRAP({ + try_start(); + augroup_del(name.data, false); + try_end(err); + }); } /// Execute an autocommand |autocmd-execute|. diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index d4af05b961..c0a22d058c 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2350,17 +2350,20 @@ int autocmd_delete_event(int group, event_T event, char_u *pat) /// Deletes an autocmd by ID. /// Only autocmds created via the API have IDs associated with them. There /// is no way to delete a specific autocmd created via :autocmd -void autocmd_delete_id(int64_t id) +bool autocmd_delete_id(int64_t id) { + assert(id > 0); FOR_ALL_AUEVENTS(event) { FOR_ALL_AUPATS_IN_EVENT(event, ap) { for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) { if (ac->id == id) { aucmd_del(ac); + return true; } } } } + return false; } // =========================================================================== -- cgit From ebab51b192f3737b4d902a500b6366e174e7a891 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Thu, 31 Mar 2022 18:05:17 +0200 Subject: docs(extmark): fix nvim_buf_get_extmarks example (#17934) --- src/nvim/api/extmark.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 797b64e2af..8dca37a321 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -259,9 +259,9 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// local pos = a.nvim_win_get_cursor(0) /// local ns = a.nvim_create_namespace('my-plugin') /// -- Create new extmark at line 1, column 1. -/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, 0, {}) +/// local m1 = a.nvim_buf_set_extmark(0, ns, 0, 0, {}) /// -- Create new extmark at line 3, column 1. -/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, 0, {}) +/// local m2 = a.nvim_buf_set_extmark(0, ns, 0, 2, {}) /// -- Get extmarks only from line 3. /// local ms = a.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) /// -- Get all marks in this buffer + namespace. -- cgit From 38ba2a75fcbb3a893bf8b22838ed06fec6228509 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Thu, 31 Mar 2022 22:16:25 +0200 Subject: vim-patch:8.2.4658: org-mode files are not recognized (#17939) Problem: Org-mode files are not recognized. Solution: Add patterns to recognize "org" files. (closes vim/vim#10046) https://github.com/vim/vim/commit/3a6f952cc87065a4cf1f6502b2054ba99fdf45ed --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 839d160e39..dd08d8bd09 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -381,6 +381,7 @@ let s:filename_checks = { \ 'opam': ['opam', 'file.opam', 'file.opam.template'], \ 'openroad': ['file.or'], \ 'ora': ['file.ora'], + \ 'org': ['file.org', 'file.org_archive'], \ 'pamconf': ['/etc/pam.conf', '/etc/pam.d/file', 'any/etc/pam.conf', 'any/etc/pam.d/file'], \ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment', '.pam_environment', 'pam_env.conf'], \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'], -- cgit From 9d40b2fda96709a8219369316a5768ca4b689e4f Mon Sep 17 00:00:00 2001 From: György Andorka Date: Thu, 31 Mar 2022 19:13:22 +0200 Subject: refactor(api)!: use singular/plural consistently in the autocmd API --- src/nvim/api/autocmd.c | 9 +++++---- src/nvim/api/keysets.lua | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index 482aa80f1e..00a418367f 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -36,7 +36,7 @@ // Used to delete autocmds from nvim_del_autocmd static int64_t next_autocmd_id = 1; -/// Get autocommands that match the requirements passed to {opts}. +/// Get all autocommands that match the corresponding {opts}. /// /// These examples will get autocommands matching ALL the given criteria: ///
@@ -562,7 +562,7 @@ void nvim_del_autocmd(Integer id)
 ///         - group: (string|int) The augroup name or id.
 ///             - NOTE: If not passed, will only delete autocmds *not* in any group.
 ///
-void nvim_clear_autocmd(Dict(clear_autocmd) *opts, Error *err)
+void nvim_clear_autocmds(Dict(clear_autocmds) *opts, Error *err)
   FUNC_API_SINCE(9)
 {
   // TODO(tjdevries): Future improvements:
@@ -697,7 +697,8 @@ void nvim_del_augroup_by_name(String name)
   augroup_del(name.data, false);
 }
 
-/// Execute an autocommand |autocmd-execute|.
+/// Execute all autocommands for {event} that match the corresponding
+///  {opts} |autocmd-execute|.
 /// @param event (String|Array) The event or events to execute
 /// @param opts Dictionary of autocommand options:
 ///             - group (string|integer) optional: the autocommand group name or
@@ -709,7 +710,7 @@ void nvim_del_augroup_by_name(String name)
 ///             - modeline (bool) optional: defaults to true. Process the
 ///             modeline after the autocommands ||.
 /// @see |:doautocmd|
-void nvim_exec_autocmd(Object event, Dict(exec_autocmd) *opts, Error *err)
+void nvim_exec_autocmds(Object event, Dict(exec_autocmds) *opts, Error *err)
   FUNC_API_SINCE(9)
 {
   int au_group = AUGROUP_ALL;
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 520bb7fbc6..8ad4dae928 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -123,7 +123,7 @@ return {
     "nocombine";
   };
   -- Autocmds
-  clear_autocmd = {
+  clear_autocmds = {
     "buffer";
     "event";
     "group";
@@ -139,7 +139,7 @@ return {
     "once";
     "pattern";
   };
-  exec_autocmd = {
+  exec_autocmds = {
     "buffer";
     "group";
     "modeline";
-- 
cgit 


From 8a6cf51a710585fee7edb9d1357791da30f31f44 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 07:11:38 +0800
Subject: vim-patch:8.2.3122: with 'nowrap' cursor position is unexected in
 narrow window (#17935)

Problem:    With 'nowrap' cursor position is unexected in narrow window.
            (Leonid V.  Fedorenchik)
Solution:   Put cursor on the last non-empty line. (closes vim/vim#8525)
https://github.com/vim/vim/commit/30441bb3d5fa73f888b09684db3f54ff5ab48dbc
---
 src/nvim/move.c                     | 7 ++++++-
 src/nvim/testdir/test_listchars.vim | 6 ++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/move.c b/src/nvim/move.c
index 5e02e355bf..eda3298101 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -778,8 +778,13 @@ void curs_columns(win_T *wp, int may_scroll)
   int textwidth = wp->w_width_inner - extra;
   if (textwidth <= 0) {
     // No room for text, put cursor in last char of window.
+    // If not wrapping, the last non-empty line.
     wp->w_wcol = wp->w_width_inner - 1;
-    wp->w_wrow = wp->w_height_inner - 1;
+    if (wp->w_p_wrap) {
+      wp->w_wrow = wp->w_height_inner - 1;
+    } else {
+      wp->w_wrow = wp->w_height_inner - 1 - wp->w_empty_rows;
+    }
   } else if (wp->w_p_wrap
              && wp->w_width_inner != 0) {
     width = textwidth + win_col_off2(wp);
diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim
index c6e2ebd406..b239c9c6b5 100644
--- a/src/nvim/testdir/test_listchars.vim
+++ b/src/nvim/testdir/test_listchars.vim
@@ -542,6 +542,12 @@ func Test_listchars_foldcolumn()
   call VerifyScreenDump(buf, 'Test_listchars_04', {})
   call term_sendkeys(buf, "\>")
   call VerifyScreenDump(buf, 'Test_listchars_05', {})
+  call term_sendkeys(buf, "\h")
+  call term_sendkeys(buf, ":set nowrap foldcolumn=4\")
+  call term_sendkeys(buf, "15\<")
+  call VerifyScreenDump(buf, 'Test_listchars_06', {})
+  call term_sendkeys(buf, "4\<")
+  call VerifyScreenDump(buf, 'Test_listchars_07', {})
 
   " clean up
   call StopVimInTerminal(buf)
-- 
cgit 


From 973e91007ce3f343f7aeed8d30d70a8616ab6bb5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 11:40:45 +0800
Subject: refactor: remove redundant check for w_p_cole (#17944)

No longer needed after #17890.
---
 src/nvim/edit.c   | 12 +++++-------
 src/nvim/normal.c |  7 ++-----
 2 files changed, 7 insertions(+), 12 deletions(-)

(limited to 'src')

diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 815d57121b..a2687ada73 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1486,7 +1486,7 @@ static void ins_redraw(bool ready)
 
   // Trigger CursorMoved if the cursor moved.  Not when the popup menu is
   // visible, the command might delete it.
-  if (ready && (has_event(EVENT_CURSORMOVEDI) || curwin->w_p_cole > 0)
+  if (ready && has_event(EVENT_CURSORMOVEDI)
       && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)
       && !pum_visible()) {
     // Need to update the screen first, to make sure syntax
@@ -1496,12 +1496,10 @@ static void ins_redraw(bool ready)
     if (syntax_present(curwin) && must_redraw) {
       update_screen(0);
     }
-    if (has_event(EVENT_CURSORMOVEDI)) {
-      // Make sure curswant is correct, an autocommand may call
-      // getcurpos()
-      update_curswant();
-      ins_apply_autocmds(EVENT_CURSORMOVEDI);
-    }
+    // Make sure curswant is correct, an autocommand may call
+    // getcurpos()
+    update_curswant();
+    ins_apply_autocmds(EVENT_CURSORMOVEDI);
     curwin->w_last_cursormoved = curwin->w_cursor;
   }
 
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 72e80952ff..8332b8bbd6 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1233,12 +1233,9 @@ static void normal_check_window_scrolled(NormalState *s)
 static void normal_check_cursor_moved(NormalState *s)
 {
   // Trigger CursorMoved if the cursor moved.
-  if (!finish_op && (has_event(EVENT_CURSORMOVED) || curwin->w_p_cole > 0)
+  if (!finish_op && has_event(EVENT_CURSORMOVED)
       && !equalpos(curwin->w_last_cursormoved, curwin->w_cursor)) {
-    if (has_event(EVENT_CURSORMOVED)) {
-      apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
-    }
-
+    apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf);
     curwin->w_last_cursormoved = curwin->w_cursor;
   }
 }
-- 
cgit 


From dc3bbd31a9d6c36944b7f26127d9d46b21a70c2f Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Fri, 1 Apr 2022 08:38:58 +0100
Subject: fix(api): delete all autocmds with the same ID

---
 src/nvim/autocmd.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index c0a22d058c..bcac7fbc4a 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2353,17 +2353,20 @@ int autocmd_delete_event(int group, event_T event, char_u *pat)
 bool autocmd_delete_id(int64_t id)
 {
   assert(id > 0);
+  bool success = false;
+
+  // Note that since multiple AutoCmd objects can have the same ID, we need to do a full scan.
   FOR_ALL_AUEVENTS(event) {
     FOR_ALL_AUPATS_IN_EVENT(event, ap) {
       for (AutoCmd *ac = ap->cmds; ac != NULL; ac = ac->next) {
         if (ac->id == id) {
           aucmd_del(ac);
-          return true;
+          success = true;
         }
       }
     }
   }
-  return false;
+  return success;
 }
 
 // ===========================================================================
-- 
cgit 


From 649a11bc139d109e8a3adc893a9e53ab32cf248c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 17:08:04 +0800
Subject: vim-patch:8.2.3471: crash when using CTRL-T after an empty search
 pattern

Problem:    Crash when using CTRL-T after an empty search pattern.
Solution:   Bail out when there is no previous search pattern. (closes vim/vim#8953)
https://github.com/vim/vim/commit/d8d957de86f218de9124ca1209548f8c6f61b69b
---
 src/nvim/ex_getln.c              |  4 ++++
 src/nvim/testdir/test_search.vim | 10 ++++++++++
 2 files changed, 14 insertions(+)

(limited to 'src')

diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 1d02c74c41..9104a2dd5a 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1606,6 +1606,10 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
 
   if (search_delim == ccline.cmdbuff[skiplen]) {
     pat = last_search_pattern();
+    if (pat == NULL) {
+      restore_last_search_pattern();
+      return FAIL;
+    }
     skiplen = 0;
     patlen = (int)STRLEN(pat);
   } else {
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index a3d5ca96a1..f88017388a 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -1527,4 +1527,14 @@ func Test_incsearch_highlighting_newline()
   bw
 endfunc
 
+func Test_no_last_search_pattern()
+  CheckOption incsearch
+
+  let @/ = ""
+  set incsearch
+  " this was causing a crash
+  call feedkeys("//\x14", 'xt')
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From b8fbd749a9d814f227c26d02f17f805435856677 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 17:12:09 +0800
Subject: vim-patch:8.2.3472: other crashes with empty search pattern not
 tested
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Other crashes with empty search pattern not tested.
Solution:   Add a few more test lines. (Dominique Pellé)
https://github.com/vim/vim/commit/9af9fd6ab637ea507dd9015fa5a84a408c36c1e0
---
 src/nvim/testdir/test_search.vim | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index f88017388a..040267c136 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -1532,8 +1532,11 @@ func Test_no_last_search_pattern()
 
   let @/ = ""
   set incsearch
-  " this was causing a crash
-  call feedkeys("//\x14", 'xt')
+  " these were causing a crash
+  call feedkeys("//\", 'xt')
+  call feedkeys("//\", 'xt')
+  call feedkeys("??\", 'xt')
+  call feedkeys("??\", 'xt')
 endfunc
 
 
-- 
cgit 


From b9454d1676999ae44faa070f79b40863e4ea843b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 17:12:41 +0800
Subject: vim-patch:8.2.3489: ml_get error after search with range

Problem:    ml_get error after search with range.
Solution:   Limit the line number to the buffer line count.
https://github.com/vim/vim/commit/35a319b77f897744eec1155b736e9372c9c5575f
---
 src/nvim/ex_docmd.c              |  5 +++--
 src/nvim/testdir/test_search.vim | 14 ++++++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 20325509c4..bb6f3ba395 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4027,8 +4027,9 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in
 
         // When '/' or '?' follows another address, start from
         // there.
-        if (lnum != MAXLNUM) {
-          curwin->w_cursor.lnum = lnum;
+        if (lnum > 0 && lnum != MAXLNUM) {
+          curwin->w_cursor.lnum
+            = lnum > curbuf->b_ml.ml_line_count ? curbuf->b_ml.ml_line_count : lnum;
         }
 
         // Start a forward search at the end of the line (unless
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index 040267c136..8154bd9c4d 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -1539,5 +1539,19 @@ func Test_no_last_search_pattern()
   call feedkeys("??\", 'xt')
 endfunc
 
+func Test_search_with_invalid_range()
+  new
+  let lines =<< trim END
+    /\%.v
+    5/
+    c
+  END
+  call writefile(lines, 'Xrangesearch')
+  source Xrangesearch
+
+  bwipe!
+  call delete('Xrangesearch')
+endfunc
+
 
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 139828cc7ee807cb6680c87bb2cec1aa90e8b4fd Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 1 Apr 2022 19:46:17 +0800
Subject: vim-patch:8.2.4660: cursorcolumn is sometimes not correct

Problem:    Cursorcolumn is sometimes not correct.
Solution:   Recompute the cursor column when entering Insert mode and the
            cursor is on a character wider than a screen cell.
https://github.com/vim/vim/commit/782c6744b49b30d9460ed00d4773666e42e07163
---
 src/nvim/edit.c                     | 10 +++++++---
 src/nvim/testdir/test_highlight.vim | 22 ++++++++++++++++++++++
 2 files changed, 29 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index a2687ada73..da0b577056 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -390,9 +390,13 @@ static void insert_enter(InsertState *s)
   trigger_modechanged();
   stop_insert_mode = false;
 
-  // Need to recompute the cursor position, it might move when the cursor is
-  // on a TAB or special character.
-  curs_columns(curwin, true);
+  // Need to recompute the cursor position, it might move when the cursor
+  // is on a TAB or special character.
+  // ptr2cells() treats a TAB character as double-width.
+  if (ptr2cells(get_cursor_pos_ptr()) > 1) {
+    curwin->w_valid &= ~VALID_VIRTCOL;
+    curs_columns(curwin, true);
+  }
 
   // Enable langmap or IME, indicated by 'iminsert'.
   // Note that IME may enabled/disabled without us noticing here, thus the
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 6387ec62af..5588e515e1 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -597,6 +597,28 @@ func Test_cursorline_with_visualmode()
   call delete('Xtest_cursorline_with_visualmode')
 endfunc
 
+func Test_cursorcolumn_insert_on_tab()
+  CheckScreendump
+
+  let lines =<< trim END
+    call setline(1, ['123456789', "a\tb"])
+    set cursorcolumn
+    call cursor(2, 2)
+  END
+  call writefile(lines, 'Xcuc_insert_on_tab')
+
+  let buf = RunVimInTerminal('-S Xcuc_insert_on_tab', #{rows: 8})
+  call TermWait(buf)
+  call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_1', {})
+
+  call term_sendkeys(buf, 'i')
+  call TermWait(buf)
+  call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+
+  call StopVimInTerminal(buf)
+  call delete('Xcuc_insert_on_tab')
+endfunc
+
 func Test_cursorcolumn_callback()
   CheckScreendump
   CheckFeature timers
-- 
cgit 


From 54997363801bf9c7885cb8195fabe1ee82132516 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sat, 2 Apr 2022 16:18:18 +0800
Subject: fix(input): do not translate scroll keys into multiclicks

---
 src/nvim/os/input.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index c6c4990334..745b888b5e 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -288,8 +288,13 @@ static uint8_t check_multiclick(int code, int grid, int row, int col)
   static int orig_mouse_row = 0;
   static uint64_t orig_mouse_time = 0;  // time of previous mouse click
 
-  if (code == KE_LEFTRELEASE || code == KE_RIGHTRELEASE
-      || code == KE_MIDDLERELEASE) {
+  if (code == KE_LEFTRELEASE
+      || code == KE_RIGHTRELEASE
+      || code == KE_MIDDLERELEASE
+      || code == KE_MOUSEDOWN
+      || code == KE_MOUSEUP
+      || code == KE_MOUSELEFT
+      || code == KE_MOUSERIGHT) {
     return 0;
   }
   uint64_t mouse_time = os_hrtime();    // time of current mouse click (ns)
-- 
cgit 


From e45d141e28a4b31b4c98f7ed1655e9c7141ae74f Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Sat, 2 Apr 2022 13:36:19 +0200
Subject: vim-patch:8.2.4664: Elvish files are not recognized (#17963)

Problem:    Elvish files are not recognized.
Solution:   Recognize .elv files. (Bruno Roque, closes vim/vim#10058)
https://github.com/vim/vim/commit/c1658a196bb05dd96562fd0a92409be2201b62e9
---
 src/nvim/testdir/test_filetype.vim | 1 +
 1 file changed, 1 insertion(+)

(limited to 'src')

diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index dd08d8bd09..720c003ace 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -168,6 +168,7 @@ let s:filename_checks = {
     \ 'eelixir': ['file.eex', 'file.leex'],
     \ 'elm': ['file.elm'],
     \ 'elmfilt': ['filter-rules'],
+    \ 'elvish': ['file.elv'],
     \ 'epuppet': ['file.epp'],
     \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'],
     \ 'eruby': ['file.erb', 'file.rhtml'],
-- 
cgit 


From ea71c26ec922a80cf2f05d5bc553dc70f0e130f0 Mon Sep 17 00:00:00 2001
From: Tony Chen 
Date: Sat, 2 Apr 2022 12:35:33 -0400
Subject: fix(extmarks): splice extmarks on accepting spell

---
 src/nvim/spell.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index fb644daa39..e65ca2e6a6 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3066,7 +3066,7 @@ void spell_suggest(int count)
     ml_replace(curwin->w_cursor.lnum, p, false);
     curwin->w_cursor.col = c;
 
-    changed_bytes(curwin->w_cursor.lnum, c);
+    inserted_bytes(curwin->w_cursor.lnum, c, stp->st_orglen, stp->st_wordlen);
   } else {
     curwin->w_cursor = prev_cursor;
   }
-- 
cgit 


From a783cdd68d4f2d282725ea60e869ddf639930dc3 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 3 Apr 2022 08:58:49 +0800
Subject: fix(ex_normal): spam \n in Ex mode only if in Cmdline mode (#17977)

When using :normal in Ex mode, the editor is no longer in Cmdline mode,
but the exmode_active flag is still set, causing the wrong character to
be spammed in Insert mode, leading to a hang.
---
 src/nvim/getchar.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index b12b99b7ee..c944a5ec93 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2330,7 +2330,7 @@ static int vgetorpeek(bool advance)
           // cmdline window.
           if (p_im && (State & INSERT)) {
             c = Ctrl_L;
-          } else if (exmode_active) {
+          } else if ((State & CMDLINE) && exmode_active) {
             c = '\n';
           } else if ((State & CMDLINE) || (cmdwin_type > 0 && tc == ESC)) {
             c = Ctrl_C;
-- 
cgit 


From 6786b6afade97771027fda3c1438969def320cc5 Mon Sep 17 00:00:00 2001
From: Lewis Russell 
Date: Sun, 3 Apr 2022 02:26:59 +0100
Subject: vim-patch:8.1.1687: the evalfunc.c file is too big (#17949)

Problem:    The evalfunc.c file is too big.
Solution:   Move testing support to a separate file.
https://github.com/vim/vim/commit/ecaa70ea29c269dd0dabd3cd5acdfa0ce42ccd54
---
 src/nvim/eval.c       | 470 +----------------------------------------
 src/nvim/eval/funcs.c | 103 +--------
 src/nvim/testing.c    | 562 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/nvim/testing.h    |  10 +
 4 files changed, 580 insertions(+), 565 deletions(-)
 create mode 100644 src/nvim/testing.c
 create mode 100644 src/nvim/testing.h

(limited to 'src')

diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 6c72c2866e..c1905b31d0 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -28,7 +28,6 @@
 #include "nvim/eval/typval.h"
 #include "nvim/eval/userfunc.h"
 #include "nvim/ex_cmds2.h"
-#include "nvim/ex_docmd.h"
 #include "nvim/ex_getln.h"
 #include "nvim/ex_session.h"
 #include "nvim/fileio.h"
@@ -41,7 +40,6 @@
 #include "nvim/ops.h"
 #include "nvim/option.h"
 #include "nvim/os/input.h"
-#include "nvim/os/os.h"
 #include "nvim/os/shell.h"
 #include "nvim/path.h"
 #include "nvim/quickfix.h"
@@ -3271,7 +3269,7 @@ char_u *get_user_var_name(expand_T *xp, int idx)
 /// Does not use 'cpo' and always uses 'magic'.
 ///
 /// @return  TRUE if "pat" matches "text".
-static int pattern_match(char_u *pat, char_u *text, bool ic)
+int pattern_match(char_u *pat, char_u *text, bool ic)
 {
   int matches = 0;
   regmatch_T regmatch;
@@ -5905,144 +5903,6 @@ void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
   }
 }
 
-/// Prepare "gap" for an assert error and add the sourcing position.
-void prepare_assert_error(garray_T *gap)
-{
-  char buf[NUMBUFLEN];
-
-  ga_init(gap, 1, 100);
-  if (sourcing_name != NULL) {
-    ga_concat(gap, (char *)sourcing_name);
-    if (sourcing_lnum > 0) {
-      ga_concat(gap, " ");
-    }
-  }
-  if (sourcing_lnum > 0) {
-    vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
-    ga_concat(gap, buf);
-  }
-  if (sourcing_name != NULL || sourcing_lnum > 0) {
-    ga_concat(gap, ": ");
-  }
-}
-
-/// Append "p[clen]" to "gap", escaping unprintable characters.
-/// Changes NL to \n, CR to \r, etc.
-static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
-  FUNC_ATTR_NONNULL_ALL
-{
-  char_u buf[NUMBUFLEN];
-
-  if (clen > 1) {
-    memmove(buf, p, clen);
-    buf[clen] = NUL;
-    ga_concat(gap, (char *)buf);
-  } else {
-    switch (*p) {
-    case BS:
-      ga_concat(gap, "\\b"); break;
-    case ESC:
-      ga_concat(gap, "\\e"); break;
-    case FF:
-      ga_concat(gap, "\\f"); break;
-    case NL:
-      ga_concat(gap, "\\n"); break;
-    case TAB:
-      ga_concat(gap, "\\t"); break;
-    case CAR:
-      ga_concat(gap, "\\r"); break;
-    case '\\':
-      ga_concat(gap, "\\\\"); break;
-    default:
-      if (*p < ' ') {
-        vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
-        ga_concat(gap, (char *)buf);
-      } else {
-        ga_append(gap, *p);
-      }
-      break;
-    }
-  }
-}
-
-/// Append "str" to "gap", escaping unprintable characters.
-/// Changes NL to \n, CR to \r, etc.
-static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
-  FUNC_ATTR_NONNULL_ARG(1)
-{
-  char_u buf[NUMBUFLEN];
-
-  if (str == NULL) {
-    ga_concat(gap, "NULL");
-    return;
-  }
-
-  for (const char_u *p = str; *p != NUL; p++) {
-    int same_len = 1;
-    const char_u *s = p;
-    const int c = mb_ptr2char_adv(&s);
-    const int clen = s - p;
-    while (*s != NUL && c == utf_ptr2char(s)) {
-      same_len++;
-      s += clen;
-    }
-    if (same_len > 20) {
-      ga_concat(gap, "\\[");
-      ga_concat_esc(gap, p, clen);
-      ga_concat(gap, " occurs ");
-      vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
-      ga_concat(gap, (char *)buf);
-      ga_concat(gap, " times]");
-      p = s - 1;
-    } else {
-      ga_concat_esc(gap, p, clen);
-    }
-  }
-}
-
-/// Fill "gap" with information about an assert error.
-void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv,
-                       typval_T *got_tv, assert_type_T atype)
-{
-  char_u *tofree;
-
-  if (opt_msg_tv->v_type != VAR_UNKNOWN) {
-    tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
-    ga_concat(gap, (char *)tofree);
-    xfree(tofree);
-    ga_concat(gap, ": ");
-  }
-
-  if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
-    ga_concat(gap, "Pattern ");
-  } else if (atype == ASSERT_NOTEQUAL) {
-    ga_concat(gap, "Expected not equal to ");
-  } else {
-    ga_concat(gap, "Expected ");
-  }
-
-  if (exp_str == NULL) {
-    tofree = (char_u *)encode_tv2string(exp_tv, NULL);
-    ga_concat_shorten_esc(gap, tofree);
-    xfree(tofree);
-  } else {
-    ga_concat_shorten_esc(gap, exp_str);
-  }
-
-  if (atype != ASSERT_NOTEQUAL) {
-    if (atype == ASSERT_MATCH) {
-      ga_concat(gap, " does not match ");
-    } else if (atype == ASSERT_NOTMATCH) {
-      ga_concat(gap, " does match ");
-    } else {
-      ga_concat(gap, " but got ");
-    }
-    tofree = (char_u *)encode_tv2string(got_tv, NULL);
-    ga_concat_shorten_esc(gap, tofree);
-    xfree(tofree);
-  }
-}
-
 /// Add an assert error to v:errors.
 void assert_error(garray_T *gap)
 {
@@ -6056,328 +5916,6 @@ void assert_error(garray_T *gap)
                         (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
 }
 
-int assert_equal_common(typval_T *argvars, assert_type_T atype)
-  FUNC_ATTR_NONNULL_ALL
-{
-  garray_T ga;
-
-  if (tv_equal(&argvars[0], &argvars[1], false, false)
-      != (atype == ASSERT_EQUAL)) {
-    prepare_assert_error(&ga);
-    fill_assert_error(&ga, &argvars[2], NULL,
-                      &argvars[0], &argvars[1], atype);
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  }
-  return 0;
-}
-
-int assert_equalfile(typval_T *argvars)
-  FUNC_ATTR_NONNULL_ALL
-{
-  char buf1[NUMBUFLEN];
-  char buf2[NUMBUFLEN];
-  const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
-  const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
-  garray_T ga;
-
-  if (fname1 == NULL || fname2 == NULL) {
-    return 0;
-  }
-
-  IObuff[0] = NUL;
-  FILE *const fd1 = os_fopen(fname1, READBIN);
-  char line1[200];
-  char line2[200];
-  ptrdiff_t lineidx = 0;
-  if (fd1 == NULL) {
-    snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
-  } else {
-    FILE *const fd2 = os_fopen(fname2, READBIN);
-    if (fd2 == NULL) {
-      fclose(fd1);
-      snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
-    } else {
-      int64_t linecount = 1;
-      for (int64_t count = 0;; count++) {
-        const int c1 = fgetc(fd1);
-        const int c2 = fgetc(fd2);
-        if (c1 == EOF) {
-          if (c2 != EOF) {
-            STRCPY(IObuff, "first file is shorter");
-          }
-          break;
-        } else if (c2 == EOF) {
-          STRCPY(IObuff, "second file is shorter");
-          break;
-        } else {
-          line1[lineidx] = c1;
-          line2[lineidx] = c2;
-          lineidx++;
-          if (c1 != c2) {
-            snprintf((char *)IObuff, IOSIZE,
-                     "difference at byte %" PRId64 ", line %" PRId64,
-                     count, linecount);
-            break;
-          }
-        }
-        if (c1 == NL) {
-          linecount++;
-          lineidx = 0;
-        } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) {
-          memmove(line1, line1 + 100, lineidx - 100);
-          memmove(line2, line2 + 100, lineidx - 100);
-          lineidx -= 100;
-        }
-      }
-      fclose(fd1);
-      fclose(fd2);
-    }
-  }
-  if (IObuff[0] != NUL) {
-    prepare_assert_error(&ga);
-    if (argvars[2].v_type != VAR_UNKNOWN) {
-      char *const tofree = encode_tv2echo(&argvars[2], NULL);
-      ga_concat(&ga, tofree);
-      xfree(tofree);
-      ga_concat(&ga, ": ");
-    }
-    ga_concat(&ga, (char *)IObuff);
-    if (lineidx > 0) {
-      line1[lineidx] = NUL;
-      line2[lineidx] = NUL;
-      ga_concat(&ga, " after \"");
-      ga_concat(&ga, line1);
-      if (STRCMP(line1, line2) != 0) {
-        ga_concat(&ga, "\" vs \"");
-        ga_concat(&ga, line2);
-      }
-      ga_concat(&ga, "\"");
-    }
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  }
-  return 0;
-}
-
-int assert_inrange(typval_T *argvars)
-  FUNC_ATTR_NONNULL_ALL
-{
-  bool error = false;
-
-  if (argvars[0].v_type == VAR_FLOAT
-      || argvars[1].v_type == VAR_FLOAT
-      || argvars[2].v_type == VAR_FLOAT) {
-    const float_T flower = tv_get_float(&argvars[0]);
-    const float_T fupper = tv_get_float(&argvars[1]);
-    const float_T factual = tv_get_float(&argvars[2]);
-
-    if (factual < flower || factual > fupper) {
-      garray_T ga;
-      prepare_assert_error(&ga);
-      if (argvars[3].v_type != VAR_UNKNOWN) {
-        char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL);
-        ga_concat(&ga, (char *)tofree);
-        xfree(tofree);
-      } else {
-        char msg[80];
-        vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
-                     flower, fupper, factual);
-        ga_concat(&ga, msg);
-      }
-      assert_error(&ga);
-      ga_clear(&ga);
-      return 1;
-    }
-  } else {
-    const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
-    const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
-    const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
-
-    if (error) {
-      return 0;
-    }
-    if (actual < lower || actual > upper) {
-      garray_T ga;
-      prepare_assert_error(&ga);
-
-      char msg[55];
-      vim_snprintf(msg, sizeof(msg),
-                   "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
-                   lower, upper);  // -V576
-      fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
-                        ASSERT_INRANGE);
-      assert_error(&ga);
-      ga_clear(&ga);
-      return 1;
-    }
-  }
-  return 0;
-}
-
-/// Common for assert_true() and assert_false().
-int assert_bool(typval_T *argvars, bool is_true)
-  FUNC_ATTR_NONNULL_ALL
-{
-  bool error = false;
-  garray_T ga;
-
-  if ((argvars[0].v_type != VAR_NUMBER
-       || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
-       || error)
-      && (argvars[0].v_type != VAR_BOOL
-          || (argvars[0].vval.v_bool
-              != (BoolVarValue)(is_true
-                                ? kBoolVarTrue
-                                : kBoolVarFalse)))) {
-    prepare_assert_error(&ga);
-    fill_assert_error(&ga, &argvars[1],
-                      (char_u *)(is_true ? "True" : "False"),
-                      NULL, &argvars[0], ASSERT_OTHER);
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  }
-  return 0;
-}
-
-int assert_exception(typval_T *argvars)
-  FUNC_ATTR_NONNULL_ALL
-{
-  garray_T ga;
-
-  const char *const error = tv_get_string_chk(&argvars[0]);
-  if (vimvars[VV_EXCEPTION].vv_str == NULL) {
-    prepare_assert_error(&ga);
-    ga_concat(&ga, "v:exception is not set");
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  } else if (error != NULL
-             && strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) {
-    prepare_assert_error(&ga);
-    fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
-                      &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER);
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  }
-  return 0;
-}
-
-static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd)
-  FUNC_ATTR_NONNULL_ALL
-{
-  if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
-    char *const tofree = encode_tv2echo(&argvars[2], NULL);
-    ga_concat(gap, tofree);
-    xfree(tofree);
-  } else {
-    ga_concat(gap, cmd);
-  }
-}
-
-int assert_beeps(typval_T *argvars, bool no_beep)
-  FUNC_ATTR_NONNULL_ALL
-{
-  const char *const cmd = tv_get_string_chk(&argvars[0]);
-  int ret = 0;
-
-  called_vim_beep = false;
-  suppress_errthrow = true;
-  emsg_silent = false;
-  do_cmdline_cmd(cmd);
-  if (no_beep ? called_vim_beep : !called_vim_beep) {
-    garray_T ga;
-    prepare_assert_error(&ga);
-    if (no_beep) {
-      ga_concat(&ga, "command did beep: ");
-    } else {
-      ga_concat(&ga, "command did not beep: ");
-    }
-    ga_concat(&ga, cmd);
-    assert_error(&ga);
-    ga_clear(&ga);
-    ret = 1;
-  }
-
-  suppress_errthrow = false;
-  emsg_on_display = false;
-  return ret;
-}
-
-int assert_fails(typval_T *argvars)
-  FUNC_ATTR_NONNULL_ALL
-{
-  const char *const cmd = tv_get_string_chk(&argvars[0]);
-  garray_T ga;
-  int ret = 0;
-  int save_trylevel = trylevel;
-
-  // trylevel must be zero for a ":throw" command to be considered failed
-  trylevel = 0;
-  called_emsg = false;
-  suppress_errthrow = true;
-  emsg_silent = true;
-
-  do_cmdline_cmd(cmd);
-  if (!called_emsg) {
-    prepare_assert_error(&ga);
-    ga_concat(&ga, "command did not fail: ");
-    assert_append_cmd_or_arg(&ga, argvars, cmd);
-    assert_error(&ga);
-    ga_clear(&ga);
-    ret = 1;
-  } else if (argvars[1].v_type != VAR_UNKNOWN) {
-    char buf[NUMBUFLEN];
-    const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
-
-    if (error == NULL
-        || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) {
-      prepare_assert_error(&ga);
-      fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
-                        &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER);
-      ga_concat(&ga, ": ");
-      assert_append_cmd_or_arg(&ga, argvars, cmd);
-      assert_error(&ga);
-      ga_clear(&ga);
-      ret = 1;
-    }
-  }
-
-  trylevel = save_trylevel;
-  called_emsg = false;
-  suppress_errthrow = false;
-  emsg_silent = false;
-  emsg_on_display = false;
-  set_vim_var_string(VV_ERRMSG, NULL, 0);
-  return ret;
-}
-
-int assert_match_common(typval_T *argvars, assert_type_T atype)
-  FUNC_ATTR_NONNULL_ALL
-{
-  char buf1[NUMBUFLEN];
-  char buf2[NUMBUFLEN];
-  const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
-  const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
-
-  if (pat == NULL || text == NULL) {
-    emsg(_(e_invarg));
-  } else if (pattern_match((char_u *)pat, (char_u *)text, false)
-             != (atype == ASSERT_MATCH)) {
-    garray_T ga;
-    prepare_assert_error(&ga);
-    fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
-    assert_error(&ga);
-    ga_clear(&ga);
-    return 1;
-  }
-  return 0;
-}
-
 /// Find a window: When using a Window ID in any tab page, when using a number
 /// in the current tab page.
 win_T *find_win_by_nr_or_id(typval_T *vp)
@@ -8665,6 +8203,12 @@ int eval_isnamec1(int c)
   return ASCII_ISALPHA(c) || c == '_';
 }
 
+/// Get typval_T v: variable value.
+typval_T *get_vim_var_tv(int idx)
+{
+  return &vimvars[idx].vv_tv;
+}
+
 /// Get number v: variable value.
 varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE
 {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index ae9cb3b1e8..267b8a433b 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -64,6 +64,7 @@
 #include "nvim/state.h"
 #include "nvim/syntax.h"
 #include "nvim/tag.h"
+#include "nvim/testing.h"
 #include "nvim/ui.h"
 #include "nvim/undo.h"
 #include "nvim/version.h"
@@ -448,90 +449,6 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
   }
 }
 
-/// "assert_beeps(cmd [, error])" function
-static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_beeps(argvars, false);
-}
-
-/// "assert_nobeep(cmd [, error])" function
-static void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_beeps(argvars, true);
-}
-
-/// "assert_equal(expected, actual[, msg])" function
-static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
-}
-
-/// "assert_equalfile(fname-one, fname-two[, msg])" function
-static void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_equalfile(argvars);
-}
-
-/// "assert_notequal(expected, actual[, msg])" function
-static void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
-}
-
-/// "assert_report(msg)
-static void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  garray_T ga;
-
-  prepare_assert_error(&ga);
-  ga_concat(&ga, tv_get_string(&argvars[0]));
-  assert_error(&ga);
-  ga_clear(&ga);
-  rettv->vval.v_number = 1;
-}
-
-/// "assert_exception(string[, msg])" function
-static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_exception(argvars);
-}
-
-/// "assert_fails(cmd [, error [, msg]])" function
-static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_fails(argvars);
-}
-
-// "assert_false(actual[, msg])" function
-static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_bool(argvars, false);
-}
-
-/// "assert_inrange(lower, upper[, msg])" function
-static void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_inrange(argvars);
-}
-
-/// "assert_match(pattern, actual[, msg])" function
-static void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
-}
-
-/// "assert_notmatch(pattern, actual[, msg])" function
-static void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
-}
-
-/// "assert_true(actual[, msg])" function
-static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  rettv->vval.v_number = assert_bool(argvars, true);
-}
-
 /// "atan2()" function
 static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 {
@@ -10896,24 +10813,6 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
   channel_create_event(chan, NULL);
 }
 
-/// "test_garbagecollect_now()" function
-static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
-  // This is dangerous, any Lists and Dicts used internally may be freed
-  // while still in use.
-  garbage_collect(true);
-}
-
-/// "test_write_list_log()" function
-static void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
-{
-  const char *const fname = tv_get_string_chk(&argvars[0]);
-  if (fname == NULL) {
-    return;
-  }
-  list_write_log(fname);
-}
-
 /// "timer_info([timer])" function
 static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 {
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
new file mode 100644
index 0000000000..5771ec4445
--- /dev/null
+++ b/src/nvim/testing.c
@@ -0,0 +1,562 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// testing.c: Support for tests
+
+#include "nvim/eval/encode.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/eval.h"
+#include "nvim/os/os.h"
+#include "nvim/testing.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "testing.c.generated.h"
+#endif
+
+/// Prepare "gap" for an assert error and add the sourcing position.
+static void prepare_assert_error(garray_T *gap)
+{
+  char buf[NUMBUFLEN];
+
+  ga_init(gap, 1, 100);
+  if (sourcing_name != NULL) {
+    ga_concat(gap, (char *)sourcing_name);
+    if (sourcing_lnum > 0) {
+      ga_concat(gap, " ");
+    }
+  }
+  if (sourcing_lnum > 0) {
+    vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
+    ga_concat(gap, buf);
+  }
+  if (sourcing_name != NULL || sourcing_lnum > 0) {
+    ga_concat(gap, ": ");
+  }
+}
+
+/// Append "p[clen]" to "gap", escaping unprintable characters.
+/// Changes NL to \n, CR to \r, etc.
+static void ga_concat_esc(garray_T *gap, const char_u *p, int clen)
+  FUNC_ATTR_NONNULL_ALL
+{
+  char_u buf[NUMBUFLEN];
+
+  if (clen > 1) {
+    memmove(buf, p, (size_t)clen);
+    buf[clen] = NUL;
+    ga_concat(gap, (char *)buf);
+  } else {
+    switch (*p) {
+    case BS:
+      ga_concat(gap, "\\b"); break;
+    case ESC:
+      ga_concat(gap, "\\e"); break;
+    case FF:
+      ga_concat(gap, "\\f"); break;
+    case NL:
+      ga_concat(gap, "\\n"); break;
+    case TAB:
+      ga_concat(gap, "\\t"); break;
+    case CAR:
+      ga_concat(gap, "\\r"); break;
+    case '\\':
+      ga_concat(gap, "\\\\"); break;
+    default:
+      if (*p < ' ') {
+        vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
+        ga_concat(gap, (char *)buf);
+      } else {
+        ga_append(gap, (char)(*p));
+      }
+      break;
+    }
+  }
+}
+
+/// Append "str" to "gap", escaping unprintable characters.
+/// Changes NL to \n, CR to \r, etc.
+static void ga_concat_shorten_esc(garray_T *gap, const char_u *str)
+  FUNC_ATTR_NONNULL_ARG(1)
+{
+  char_u buf[NUMBUFLEN];
+
+  if (str == NULL) {
+    ga_concat(gap, "NULL");
+    return;
+  }
+
+  for (const char_u *p = str; *p != NUL; p++) {
+    int same_len = 1;
+    const char_u *s = p;
+    const int c = mb_ptr2char_adv(&s);
+    const int clen = (int)(s - p);
+    while (*s != NUL && c == utf_ptr2char(s)) {
+      same_len++;
+      s += clen;
+    }
+    if (same_len > 20) {
+      ga_concat(gap, "\\[");
+      ga_concat_esc(gap, p, clen);
+      ga_concat(gap, " occurs ");
+      vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
+      ga_concat(gap, (char *)buf);
+      ga_concat(gap, " times]");
+      p = s - 1;
+    } else {
+      ga_concat_esc(gap, p, clen);
+    }
+  }
+}
+
+/// Fill "gap" with information about an assert error.
+static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str,
+                              typval_T *exp_tv, typval_T *got_tv, assert_type_T atype)
+{
+  char_u *tofree;
+
+  if (opt_msg_tv->v_type != VAR_UNKNOWN) {
+    tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL);
+    ga_concat(gap, (char *)tofree);
+    xfree(tofree);
+    ga_concat(gap, ": ");
+  }
+
+  if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) {
+    ga_concat(gap, "Pattern ");
+  } else if (atype == ASSERT_NOTEQUAL) {
+    ga_concat(gap, "Expected not equal to ");
+  } else {
+    ga_concat(gap, "Expected ");
+  }
+
+  if (exp_str == NULL) {
+    tofree = (char_u *)encode_tv2string(exp_tv, NULL);
+    ga_concat_shorten_esc(gap, tofree);
+    xfree(tofree);
+  } else {
+    ga_concat_shorten_esc(gap, exp_str);
+  }
+
+  if (atype != ASSERT_NOTEQUAL) {
+    if (atype == ASSERT_MATCH) {
+      ga_concat(gap, " does not match ");
+    } else if (atype == ASSERT_NOTMATCH) {
+      ga_concat(gap, " does match ");
+    } else {
+      ga_concat(gap, " but got ");
+    }
+    tofree = (char_u *)encode_tv2string(got_tv, NULL);
+    ga_concat_shorten_esc(gap, tofree);
+    xfree(tofree);
+  }
+}
+
+static int assert_equal_common(typval_T *argvars, assert_type_T atype)
+  FUNC_ATTR_NONNULL_ALL
+{
+  garray_T ga;
+
+  if (tv_equal(&argvars[0], &argvars[1], false, false)
+      != (atype == ASSERT_EQUAL)) {
+    prepare_assert_error(&ga);
+    fill_assert_error(&ga, &argvars[2], NULL,
+                      &argvars[0], &argvars[1], atype);
+    assert_error(&ga);
+    ga_clear(&ga);
+    return 1;
+  }
+  return 0;
+}
+
+static int assert_match_common(typval_T *argvars, assert_type_T atype)
+  FUNC_ATTR_NONNULL_ALL
+{
+  char buf1[NUMBUFLEN];
+  char buf2[NUMBUFLEN];
+  const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1);
+  const char *const text = tv_get_string_buf_chk(&argvars[1], buf2);
+
+  if (pat == NULL || text == NULL) {
+    emsg(_(e_invarg));
+  } else if (pattern_match((char_u *)pat, (char_u *)text, false)
+             != (atype == ASSERT_MATCH)) {
+    garray_T ga;
+    prepare_assert_error(&ga);
+    fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype);
+    assert_error(&ga);
+    ga_clear(&ga);
+    return 1;
+  }
+  return 0;
+}
+
+/// Common for assert_true() and assert_false().
+static int assert_bool(typval_T *argvars, bool is_true)
+  FUNC_ATTR_NONNULL_ALL
+{
+  bool error = false;
+  garray_T ga;
+
+  if ((argvars[0].v_type != VAR_NUMBER
+       || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true
+       || error)
+      && (argvars[0].v_type != VAR_BOOL
+          || (argvars[0].vval.v_bool
+              != (BoolVarValue)(is_true
+                                ? kBoolVarTrue
+                                : kBoolVarFalse)))) {
+    prepare_assert_error(&ga);
+    fill_assert_error(&ga, &argvars[1],
+                      (char_u *)(is_true ? "True" : "False"),
+                      NULL, &argvars[0], ASSERT_OTHER);
+    assert_error(&ga);
+    ga_clear(&ga);
+    return 1;
+  }
+  return 0;
+}
+
+static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd)
+  FUNC_ATTR_NONNULL_ALL
+{
+  if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) {
+    char *const tofree = encode_tv2echo(&argvars[2], NULL);
+    ga_concat(gap, tofree);
+    xfree(tofree);
+  } else {
+    ga_concat(gap, cmd);
+  }
+}
+
+static int assert_beeps(typval_T *argvars, bool no_beep)
+  FUNC_ATTR_NONNULL_ALL
+{
+  const char *const cmd = tv_get_string_chk(&argvars[0]);
+  int ret = 0;
+
+  called_vim_beep = false;
+  suppress_errthrow = true;
+  emsg_silent = false;
+  do_cmdline_cmd(cmd);
+  if (no_beep ? called_vim_beep : !called_vim_beep) {
+    garray_T ga;
+    prepare_assert_error(&ga);
+    if (no_beep) {
+      ga_concat(&ga, "command did beep: ");
+    } else {
+      ga_concat(&ga, "command did not beep: ");
+    }
+    ga_concat(&ga, cmd);
+    assert_error(&ga);
+    ga_clear(&ga);
+    ret = 1;
+  }
+
+  suppress_errthrow = false;
+  emsg_on_display = false;
+  return ret;
+}
+
+/// "assert_beeps(cmd [, error])" function
+void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_beeps(argvars, false);
+}
+
+/// "assert_nobeep(cmd [, error])" function
+void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_beeps(argvars, true);
+}
+
+/// "assert_equal(expected, actual[, msg])" function
+void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
+}
+
+static int assert_equalfile(typval_T *argvars)
+  FUNC_ATTR_NONNULL_ALL
+{
+  char buf1[NUMBUFLEN];
+  char buf2[NUMBUFLEN];
+  const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
+  const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
+  garray_T ga;
+
+  if (fname1 == NULL || fname2 == NULL) {
+    return 0;
+  }
+
+  IObuff[0] = NUL;
+  FILE *const fd1 = os_fopen(fname1, READBIN);
+  char line1[200];
+  char line2[200];
+  ptrdiff_t lineidx = 0;
+  if (fd1 == NULL) {
+    snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
+  } else {
+    FILE *const fd2 = os_fopen(fname2, READBIN);
+    if (fd2 == NULL) {
+      fclose(fd1);
+      snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
+    } else {
+      int64_t linecount = 1;
+      for (int64_t count = 0;; count++) {
+        const int c1 = fgetc(fd1);
+        const int c2 = fgetc(fd2);
+        if (c1 == EOF) {
+          if (c2 != EOF) {
+            STRCPY(IObuff, "first file is shorter");
+          }
+          break;
+        } else if (c2 == EOF) {
+          STRCPY(IObuff, "second file is shorter");
+          break;
+        } else {
+          line1[lineidx] = (char)c1;
+          line2[lineidx] = (char)c2;
+          lineidx++;
+          if (c1 != c2) {
+            snprintf((char *)IObuff, IOSIZE,
+                     "difference at byte %" PRId64 ", line %" PRId64,
+                     count, linecount);
+            break;
+          }
+        }
+        if (c1 == NL) {
+          linecount++;
+          lineidx = 0;
+        } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) {
+          memmove(line1, line1 + 100, (size_t)(lineidx - 100));
+          memmove(line2, line2 + 100, (size_t)(lineidx - 100));
+          lineidx -= 100;
+        }
+      }
+      fclose(fd1);
+      fclose(fd2);
+    }
+  }
+  if (IObuff[0] != NUL) {
+    prepare_assert_error(&ga);
+    if (argvars[2].v_type != VAR_UNKNOWN) {
+      char *const tofree = encode_tv2echo(&argvars[2], NULL);
+      ga_concat(&ga, tofree);
+      xfree(tofree);
+      ga_concat(&ga, ": ");
+    }
+    ga_concat(&ga, (char *)IObuff);
+    if (lineidx > 0) {
+      line1[lineidx] = NUL;
+      line2[lineidx] = NUL;
+      ga_concat(&ga, " after \"");
+      ga_concat(&ga, line1);
+      if (STRCMP(line1, line2) != 0) {
+        ga_concat(&ga, "\" vs \"");
+        ga_concat(&ga, line2);
+      }
+      ga_concat(&ga, "\"");
+    }
+    assert_error(&ga);
+    ga_clear(&ga);
+    return 1;
+  }
+  return 0;
+}
+
+/// "assert_equalfile(fname-one, fname-two[, msg])" function
+void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_equalfile(argvars);
+}
+
+/// "assert_notequal(expected, actual[, msg])" function
+void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
+}
+
+/// "assert_exception(string[, msg])" function
+void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  garray_T ga;
+
+  const char *const error = tv_get_string_chk(&argvars[0]);
+  if (*get_vim_var_str(VV_EXCEPTION) == NUL) {
+    prepare_assert_error(&ga);
+    ga_concat(&ga, "v:exception is not set");
+    assert_error(&ga);
+    ga_clear(&ga);
+    rettv->vval.v_number = 1;
+  } else if (error != NULL
+             && strstr((char *)get_vim_var_str(VV_EXCEPTION), error) == NULL) {
+    prepare_assert_error(&ga);
+    fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
+                      get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
+    assert_error(&ga);
+    ga_clear(&ga);
+    rettv->vval.v_number = 1;
+  }
+}
+
+/// "assert_fails(cmd [, error [, msg]])" function
+void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  const char *const cmd = tv_get_string_chk(&argvars[0]);
+  garray_T ga;
+  int save_trylevel = trylevel;
+
+  // trylevel must be zero for a ":throw" command to be considered failed
+  trylevel = 0;
+  called_emsg = false;
+  suppress_errthrow = true;
+  emsg_silent = true;
+
+  do_cmdline_cmd(cmd);
+  if (!called_emsg) {
+    prepare_assert_error(&ga);
+    ga_concat(&ga, "command did not fail: ");
+    assert_append_cmd_or_arg(&ga, argvars, cmd);
+    assert_error(&ga);
+    ga_clear(&ga);
+    rettv->vval.v_number = 1;
+  } else if (argvars[1].v_type != VAR_UNKNOWN) {
+    char buf[NUMBUFLEN];
+    const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
+
+    if (error == NULL
+        || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL) {
+      prepare_assert_error(&ga);
+      fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
+                        get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
+      ga_concat(&ga, ": ");
+      assert_append_cmd_or_arg(&ga, argvars, cmd);
+      assert_error(&ga);
+      ga_clear(&ga);
+      rettv->vval.v_number = 1;
+    }
+  }
+
+  trylevel = save_trylevel;
+  called_emsg = false;
+  suppress_errthrow = false;
+  emsg_silent = false;
+  emsg_on_display = false;
+  set_vim_var_string(VV_ERRMSG, NULL, 0);
+}
+
+// "assert_false(actual[, msg])" function
+void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_bool(argvars, false);
+}
+
+static int assert_inrange(typval_T *argvars)
+  FUNC_ATTR_NONNULL_ALL
+{
+  bool error = false;
+
+  if (argvars[0].v_type == VAR_FLOAT
+      || argvars[1].v_type == VAR_FLOAT
+      || argvars[2].v_type == VAR_FLOAT) {
+    const float_T flower = tv_get_float(&argvars[0]);
+    const float_T fupper = tv_get_float(&argvars[1]);
+    const float_T factual = tv_get_float(&argvars[2]);
+
+    if (factual < flower || factual > fupper) {
+      garray_T ga;
+      prepare_assert_error(&ga);
+      if (argvars[3].v_type != VAR_UNKNOWN) {
+        char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL);
+        ga_concat(&ga, (char *)tofree);
+        xfree(tofree);
+      } else {
+        char msg[80];
+        vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
+                     flower, fupper, factual);
+        ga_concat(&ga, msg);
+      }
+      assert_error(&ga);
+      ga_clear(&ga);
+      return 1;
+    }
+  } else {
+    const varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
+    const varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
+    const varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
+
+    if (error) {
+      return 0;
+    }
+    if (actual < lower || actual > upper) {
+      garray_T ga;
+      prepare_assert_error(&ga);
+
+      char msg[55];
+      vim_snprintf(msg, sizeof(msg),
+                   "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
+                   lower, upper);  // -V576
+      fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2],
+                        ASSERT_INRANGE);
+      assert_error(&ga);
+      ga_clear(&ga);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/// "assert_inrange(lower, upper[, msg])" function
+void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_inrange(argvars);
+}
+
+/// "assert_match(pattern, actual[, msg])" function
+void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
+}
+
+/// "assert_notmatch(pattern, actual[, msg])" function
+void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
+}
+
+/// "assert_report(msg)" function
+void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  garray_T ga;
+
+  prepare_assert_error(&ga);
+  ga_concat(&ga, tv_get_string(&argvars[0]));
+  assert_error(&ga);
+  ga_clear(&ga);
+  rettv->vval.v_number = 1;
+}
+
+/// "assert_true(actual[, msg])" function
+void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  rettv->vval.v_number = assert_bool(argvars, true);
+}
+
+/// "test_garbagecollect_now()" function
+void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+  // This is dangerous, any Lists and Dicts used internally may be freed
+  // while still in use.
+  garbage_collect(true);
+}
+
+/// "test_write_list_log()" function
+void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
+{
+  const char *const fname = tv_get_string_chk(&argvars[0]);
+  if (fname == NULL) {
+    return;
+  }
+  list_write_log(fname);
+}
+
diff --git a/src/nvim/testing.h b/src/nvim/testing.h
new file mode 100644
index 0000000000..34ee1d10df
--- /dev/null
+++ b/src/nvim/testing.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_TESTING_H
+#define NVIM_TESTING_H
+
+#include "nvim/eval/typval.h"
+#include "nvim/eval/funcs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "testing.h.generated.h"
+#endif
+#endif  // NVIM_TESTING_H
-- 
cgit 


From e9e16655af1bacd4b1499fed00a142512c120710 Mon Sep 17 00:00:00 2001
From: Shougo 
Date: Sun, 3 Apr 2022 21:27:46 +0900
Subject: [RFC] vim-patch:8.1.1378: delete() can not handle a file name that
 looks li… (#16268)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Problem:    Delete() can not handle a file name that looks like a pattern.
Solution:   Use readdir() instead of appending "/*" and expanding wildcards.
            (Ken Takata, closes vim/vim#4424, closes vim/vim#696)
https://github.com/vim/vim/commit/701ff0a3e53d253d7300c385e582659bbff7860d

Cherry-pick a change to Test_delete_rf() from patch 8.1.1921.

Co-authored-by: zeertzjq 
---
 src/nvim/eval/funcs.c               | 62 ++++++++-----------------------
 src/nvim/fileio.c                   | 74 ++++++++++++++++++++++++++++++-------
 src/nvim/fileio.h                   |  4 ++
 src/nvim/testdir/test_functions.vim | 19 ++++++++--
 4 files changed, 94 insertions(+), 65 deletions(-)

(limited to 'src')

diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 267b8a433b..92672be8d0 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -6702,15 +6702,21 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
   }
 }
 
-/// Evaluate "expr" for readdir().
-static varnumber_T readdir_checkitem(typval_T *expr, const char *name)
+/// Evaluate "expr" (= "context") for readdir().
+static varnumber_T readdir_checkitem(void *context, const char *name)
+  FUNC_ATTR_NONNULL_ALL
 {
+  typval_T *expr = (typval_T *)context;
   typval_T save_val;
   typval_T rettv;
   typval_T argv[2];
   varnumber_T retval = 0;
   bool error = false;
 
+  if (expr->v_type == VAR_UNKNOWN) {
+    return 1;
+  }
+
   prepare_vimvar(VV_VAL, &save_val);
   set_vim_var_string(VV_VAL, name, -1);
   argv[0].v_type = VAR_STRING;
@@ -6736,54 +6742,16 @@ theend:
 /// "readdir()" function
 static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
 {
-  typval_T *expr;
-  const char *path;
-  garray_T ga;
-  Directory dir;
-
   tv_list_alloc_ret(rettv, kListLenUnknown);
-  path = tv_get_string(&argvars[0]);
-  expr = &argvars[1];
-  ga_init(&ga, (int)sizeof(char *), 20);
-
-  if (!os_scandir(&dir, path)) {
-    smsg(_(e_notopen), path);
-  } else {
-    for (;;) {
-      bool ignore;
-
-      path = os_scandir_next(&dir);
-      if (path == NULL) {
-        break;
-      }
-
-      ignore = (path[0] == '.'
-                && (path[1] == NUL || (path[1] == '.' && path[2] == NUL)));
-      if (!ignore && expr->v_type != VAR_UNKNOWN) {
-        varnumber_T r = readdir_checkitem(expr, path);
 
-        if (r < 0) {
-          break;
-        }
-        if (r == 0) {
-          ignore = true;
-        }
-      }
-
-      if (!ignore) {
-        ga_grow(&ga, 1);
-        ((char **)ga.ga_data)[ga.ga_len++] = xstrdup(path);
-      }
-    }
-
-    os_closedir(&dir);
-  }
-
-  if (rettv->vval.v_list != NULL && ga.ga_len > 0) {
-    sort_strings((char_u **)ga.ga_data, ga.ga_len);
+  const char *path = tv_get_string(&argvars[0]);
+  typval_T *expr = &argvars[1];
+  garray_T ga;
+  int ret = readdir_core(&ga, path, (void *)expr, readdir_checkitem);
+  if (ret == OK && ga.ga_len > 0) {
     for (int i = 0; i < ga.ga_len; i++) {
-      path = ((const char **)ga.ga_data)[i];
-      tv_list_append_string(rettv->vval.v_list, path, -1);
+      const char *p = ((const char **)ga.ga_data)[i];
+      tv_list_append_string(rettv->vval.v_list, p, -1);
     }
   }
   ga_clear_strings(&ga);
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 7905b29876..5e11a9683e 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -22,6 +22,7 @@
 #include "nvim/diff.h"
 #include "nvim/edit.h"
 #include "nvim/eval/userfunc.h"
+#include "nvim/eval/typval.h"
 #include "nvim/ex_cmds.h"
 #include "nvim/ex_docmd.h"
 #include "nvim/ex_eval.h"
@@ -5343,37 +5344,82 @@ static void vim_maketempdir(void)
   (void)umask(umask_save);
 }
 
+/// Core part of "readdir()" function.
+/// Retrieve the list of files/directories of "path" into "gap".
+///
+/// @return  OK for success, FAIL for failure.
+int readdir_core(garray_T *gap, const char *path, void *context, CheckItem checkitem)
+  FUNC_ATTR_NONNULL_ARG(1, 2)
+{
+  ga_init(gap, (int)sizeof(char *), 20);
+
+  Directory dir;
+  if (!os_scandir(&dir, path)) {
+    smsg(_(e_notopen), path);
+    return FAIL;
+  }
+
+  for (;;) {
+    const char *p = os_scandir_next(&dir);
+    if (p == NULL) {
+      break;
+    }
+
+    bool ignore = (p[0] == '.' && (p[1] == NUL || (p[1] == '.' && p[2] == NUL)));
+    if (!ignore && checkitem != NULL) {
+      varnumber_T r = checkitem(context, p);
+      if (r < 0) {
+        break;
+      }
+      if (r == 0) {
+        ignore = true;
+      }
+    }
+
+    if (!ignore) {
+      ga_grow(gap, 1);
+      ((char **)gap->ga_data)[gap->ga_len++] = xstrdup(p);
+    }
+  }
+
+  os_closedir(&dir);
+
+  if (gap->ga_len > 0) {
+    sort_strings((char_u **)gap->ga_data, gap->ga_len);
+  }
+
+  return OK;
+}
+
 /// Delete "name" and everything in it, recursively.
 ///
-/// @param name The path which should be deleted.
+/// @param name  The path which should be deleted.
 ///
 /// @return  0 for success, -1 if some file was not deleted.
 int delete_recursive(const char *name)
+  FUNC_ATTR_NONNULL_ALL
 {
   int result = 0;
 
   if (os_isrealdir(name)) {
-    snprintf((char *)NameBuff, MAXPATHL, "%s/*", name);  // NOLINT
-
-    char_u **files;
-    int file_count;
-    char_u *exp = vim_strsave(NameBuff);
-    if (gen_expand_wildcards(1, &exp, &file_count, &files,
-                             EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS
-                             | EW_DODOT | EW_EMPTYOK) == OK) {
-      for (int i = 0; i < file_count; i++) {
-        if (delete_recursive((const char *)files[i]) != 0) {
+    char *exp = xstrdup(name);
+    garray_T ga;
+    if (readdir_core(&ga, exp, NULL, NULL) == OK) {
+      for (int i = 0; i < ga.ga_len; i++) {
+        vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp, ((char_u **)ga.ga_data)[i]);
+        if (delete_recursive((const char *)NameBuff) != 0) {
           result = -1;
         }
       }
-      FreeWild(file_count, files);
+      ga_clear_strings(&ga);
     } else {
       result = -1;
     }
-
+    // Note: "name" value may be changed in delete_recursive().  Must use the saved value.
+    result = os_rmdir(exp) == 0 ? 0 : -1;
     xfree(exp);
-    os_rmdir(name);
   } else {
+    // Delete symlink only.
     result = os_remove(name) == 0 ? 0 : -1;
   }
 
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 186d0b90ab..62d8b6142e 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -3,6 +3,8 @@
 
 #include "nvim/autocmd.h"
 #include "nvim/buffer_defs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/garray.h"
 #include "nvim/os/os.h"
 
 // Values for readfile() flags
@@ -17,6 +19,8 @@
 
 #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
 
+typedef varnumber_T (*CheckItem)(void *expr, const char *name);
+
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 // Events for autocommands
 # include "fileio.h.generated.h"
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index c2b5653a29..79f718f4e8 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1620,10 +1620,6 @@ func Test_platform_name()
 endfunc
 
 func Test_readdir()
-  if isdirectory('Xdir')
-    call delete('Xdir', 'rf')
-  endif
-
   call mkdir('Xdir')
   call writefile([], 'Xdir/foo.txt')
   call writefile([], 'Xdir/bar.txt')
@@ -1653,6 +1649,21 @@ func Test_readdir()
   call delete('Xdir', 'rf')
 endfunc
 
+func Test_delete_rf()
+  call mkdir('Xdir')
+  call writefile([], 'Xdir/foo.txt')
+  call writefile([], 'Xdir/bar.txt')
+  call mkdir('Xdir/[a-1]')  " issue #696
+  call writefile([], 'Xdir/[a-1]/foo.txt')
+  call writefile([], 'Xdir/[a-1]/bar.txt')
+  call assert_true(filereadable('Xdir/foo.txt'))
+  call assert_true('Xdir/[a-1]/foo.txt'->filereadable())
+
+  call assert_equal(0, delete('Xdir', 'rf'))
+  call assert_false(filereadable('Xdir/foo.txt'))
+  call assert_false(filereadable('Xdir/[a-1]/foo.txt'))
+endfunc
+
 func Test_call()
   call assert_equal(3, call('len', [123]))
   call assert_equal(3, 'len'->call([123]))
-- 
cgit 


From d73bf3138a802bb6c1c654cd913d4e91932287f8 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 3 Apr 2022 20:38:31 +0800
Subject: vim-patch:8.2.4672: using :normal with Ex mode may make :substitute
 hang (#17983)

Problem:    Using :normal with Ex mode may make :substitute hang.
Solution:   When getting an empty line behave like 'q' was typed.
            (closes vim/vim#10070)
https://github.com/vim/vim/commit/ce416b453a849c837f9f6ffc91dd4792d84e1bfd

Cherry-pick a comment from patch 8.2.0363.
---
 src/nvim/ex_cmds.c               |  6 ++++++
 src/nvim/testdir/test_normal.vim | 10 ++++++++++
 2 files changed, 16 insertions(+)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index ff803c3553..b493ad61ae 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3867,6 +3867,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
               if (resp != NULL) {
                 typed = *resp;
                 xfree(resp);
+                // When ":normal" runs out of characters we get
+                // an empty line.  Use "q" to get out of the
+                // loop.
+                if (ex_normal_busy && typed == NUL) {
+                  typed = 'q';
+                }
               }
             } else {
               char_u *orig_line = NULL;
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index f45cd96733..1feffe1f8c 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -1843,6 +1843,16 @@ fun! Test_normal33_g_cmd2()
   bw!
 endfunc
 
+func Test_normal_ex_substitute()
+  " This was hanging on the substitute prompt.
+  new
+  call setline(1, 'a')
+  exe "normal! gggQs/a/b/c\"
+  call assert_equal('a', getline(1))
+  bwipe!
+endfunc
+
+" Test for g CTRL-G
 func Test_g_ctrl_g()
   new
 
-- 
cgit 


From 9955209afbedd00907a558a6d7d98a643c55455a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 3 Apr 2022 22:53:37 +0800
Subject: fix(substitute): properly check for empty command line

---
 src/nvim/ex_cmds.c | 17 ++++++++++-------
 src/nvim/getchar.c |  2 --
 2 files changed, 10 insertions(+), 9 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index b493ad61ae..71b3517adc 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3860,19 +3860,22 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
               prompt = xmallocz(ec + 1);
               memset(prompt, ' ', sc);
               memset(prompt + sc, '^', ec - sc + 1);
-              resp = (char_u *)getcmdline_prompt(NUL, prompt, 0, EXPAND_NOTHING,
+              resp = (char_u *)getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING,
                                                  NULL, CALLBACK_NONE);
               msg_putchar('\n');
               xfree(prompt);
               if (resp != NULL) {
                 typed = *resp;
                 xfree(resp);
-                // When ":normal" runs out of characters we get
-                // an empty line.  Use "q" to get out of the
-                // loop.
-                if (ex_normal_busy && typed == NUL) {
-                  typed = 'q';
-                }
+              } else {
+                // getcmdline_prompt() returns NULL if there is no command line to return.
+                typed = NUL;
+              }
+              // When ":normal" runs out of characters we get
+              // an empty line.  Use "q" to get out of the
+              // loop.
+              if (ex_normal_busy && typed == NUL) {
+                typed = 'q';
               }
             } else {
               char_u *orig_line = NULL;
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index c944a5ec93..8190c20c1c 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2330,8 +2330,6 @@ static int vgetorpeek(bool advance)
           // cmdline window.
           if (p_im && (State & INSERT)) {
             c = Ctrl_L;
-          } else if ((State & CMDLINE) && exmode_active) {
-            c = '\n';
           } else if ((State & CMDLINE) || (cmdwin_type > 0 && tc == ESC)) {
             c = Ctrl_C;
           } else {
-- 
cgit 


From 271bb32855853b011fceaf0ad2f829bce66b2a19 Mon Sep 17 00:00:00 2001
From: Brian Leung 
Date: Sun, 3 Apr 2022 01:37:28 -0700
Subject: vim-patch:8.2.4639: not sufficient parenthesis in preprocessor macros

Problem:    Not sufficient parenthesis in preprocessor macros.
Solution:   Add more parenthesis.
https://github.com/vim/vim/commit/9dac9b1751dd43c02470cc6a2aecaeea27abcc80
---
 src/nvim/buffer.h       | 2 +-
 src/nvim/buffer_defs.h  | 4 ++--
 src/nvim/ex_cmds.h      | 2 +-
 src/nvim/ex_cmds_defs.h | 4 ++--
 src/nvim/fileio.c       | 2 +-
 src/nvim/getchar.h      | 4 ++--
 src/nvim/globals.h      | 8 ++++----
 src/nvim/macros.h       | 2 +-
 src/nvim/mouse.h        | 4 ++--
 src/nvim/option_defs.h  | 4 ++--
 src/nvim/regexp_defs.h  | 2 +-
 src/nvim/screen.h       | 4 ++--
 src/nvim/spell_defs.h   | 6 +++---
 src/nvim/syntax_defs.h  | 2 +-
 src/nvim/vim.h          | 6 +++---
 15 files changed, 28 insertions(+), 28 deletions(-)

(limited to 'src')

diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 9e2ca999e4..850ab175a5 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -23,7 +23,7 @@ enum getf_retvalues {
   GETFILE_ERROR       = 1,    // normal error
   GETFILE_NOT_WRITTEN = 2,    // "not written" error
   GETFILE_SAME_FILE   = 0,    // success, same file
-  GETFILE_OPEN_OTHER  = -1,   // success, opened another file
+  GETFILE_OPEN_OTHER  = (-1),  // success, opened another file
   GETFILE_UNUSED      = 8,
 };
 
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 08ca1a6247..422741cc2f 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -661,7 +661,7 @@ struct file_buffer {
   // flags for use of ":lmap" and IM control
   long b_p_iminsert;            // input mode for insert
   long b_p_imsearch;            // input mode for search
-#define B_IMODE_USE_INSERT -1   //  Use b_p_iminsert value for search
+#define B_IMODE_USE_INSERT (-1)  //  Use b_p_iminsert value for search
 #define B_IMODE_NONE 0          //  Input via none
 #define B_IMODE_LMAP 1          //  Input via langmap
 #define B_IMODE_LAST 1
@@ -1420,7 +1420,7 @@ struct window_S {
   int w_briopt_list;                // additional indent for lists
 
   // transform a pointer to a "onebuf" option into a "allbuf" option
-#define GLOBAL_WO(p)    ((char *)p + sizeof(winopt_T))
+#define GLOBAL_WO(p)    ((char *)(p) + sizeof(winopt_T))
 
   long w_scbind_pos;
 
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index 241674c24c..1c95b75001 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -21,7 +21,7 @@
 
 // for lnum argument in do_ecmd()
 #define ECMD_LASTL      (linenr_T)0     // use last position in loaded file
-#define ECMD_LAST       (linenr_T)-1    // use last position in all files
+#define ECMD_LAST       ((linenr_T)-1)  // use last position in all files
 #define ECMD_ONE        (linenr_T)1     // use first line
 
 /// Previous :substitute replacement string definition
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 92675499be..39dfc1b94c 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -87,8 +87,8 @@ typedef struct exarg exarg_T;
 
 // behavior for bad character, "++bad=" argument
 #define BAD_REPLACE     '?'     // replace it with '?' (default)
-#define BAD_KEEP        -1      // leave it
-#define BAD_DROP        -2      // erase it
+#define BAD_KEEP        (-1)    // leave it
+#define BAD_DROP        (-2)    // erase it
 
 typedef void (*ex_func_T)(exarg_T *eap);
 
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 5e11a9683e..64ad9a6d80 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -80,7 +80,7 @@
 #define FIO_ENDIAN_L   0x80    // little endian
 #define FIO_NOCONVERT  0x2000  // skip encoding conversion
 #define FIO_UCSBOM     0x4000  // check for BOM at start of file
-#define FIO_ALL        -1      // allow all formats
+#define FIO_ALL        (-1)    // allow all formats
 
 /* When converting, a read() or write() may leave some bytes to be converted
  * for the next call.  The value is guessed... */
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index f24a4e7c7c..9b8605f1df 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -61,8 +61,8 @@ typedef struct map_arguments MapArguments;
 #define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \
                              { 0 }, 0, NULL, 0, LUA_NOREF, false, NULL, 0, NULL }
 
-#define KEYLEN_PART_KEY -1  // keylen value for incomplete key-code
-#define KEYLEN_PART_MAP -2  // keylen value for incomplete mapping
+#define KEYLEN_PART_KEY (-1)  // keylen value for incomplete key-code
+#define KEYLEN_PART_MAP (-2)  // keylen value for incomplete mapping
 
 /// Maximum number of streams to read script from
 enum { NSCRIPT = 15, };
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 605a2183f3..ace7647b35 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -430,7 +430,7 @@ EXTERN win_T *lastwin;               // last window
 EXTERN win_T *prevwin INIT(= NULL);  // previous window
 #define ONE_WINDOW (firstwin == lastwin)
 #define FOR_ALL_FRAMES(frp, first_frame) \
-  for (frp = first_frame; frp != NULL; frp = frp->fr_next)  // NOLINT
+  for ((frp) = first_frame; (frp) != NULL; (frp) = (frp)->fr_next)  // NOLINT
 
 // When using this macro "break" only breaks out of the inner loop. Use "goto"
 // to break out of the tabpage loop.
@@ -461,7 +461,7 @@ EXTERN tabpage_T *lastused_tabpage;
 EXTERN bool redraw_tabline INIT(= false);  // need to redraw tabline
 
 // Iterates over all tabs in the tab list
-#define FOR_ALL_TABS(tp) for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+#define FOR_ALL_TABS(tp) for (tabpage_T *(tp) = first_tabpage; (tp) != NULL; (tp) = (tp)->tp_next)
 
 // All buffers are linked in a list. 'firstbuf' points to the first entry,
 // 'lastbuf' to the last entry and 'curbuf' to the currently active buffer.
@@ -477,7 +477,7 @@ EXTERN buf_T *curbuf INIT(= NULL);    // currently active buffer
 
 // Iterate through all the signs placed in a buffer
 #define FOR_ALL_SIGNS_IN_BUF(buf, sign) \
-  for (sign = buf->b_signlist; sign != NULL; sign = sign->se_next)   // NOLINT
+  for ((sign) = (buf)->b_signlist; (sign) != NULL; (sign) = (sign)->se_next)   // NOLINT
 
 
 // List of files being edited (global argument list).  curwin->w_alist points
@@ -618,7 +618,7 @@ EXTERN int inhibit_delete_count INIT(= 0);
 #define DBCS_CHT       950     // taiwan
 #define DBCS_CHTU      9950    // euc-tw
 #define DBCS_2BYTE     1       // 2byte-
-#define DBCS_DEBUG     -1
+#define DBCS_DEBUG     (-1)
 
 /// Encoding used when 'fencs' is set to "default"
 EXTERN char_u *fenc_default INIT(= NULL);
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index d5611f587b..5017e286d3 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -103,7 +103,7 @@
 // MB_PTR_BACK(): backup a pointer to the previous character, taking care of
 // multi-byte characters if needed. Only use with "p" > "s" !
 #define MB_PTR_BACK(s, p) \
-  (p -= utf_head_off((char_u *)s, (char_u *)p - 1) + 1)
+  (p -= utf_head_off((char_u *)(s), (char_u *)(p) - 1) + 1)
 
 // MB_CHAR2BYTES(): convert character to bytes and advance pointer to bytes
 #define MB_CHAR2BYTES(c, b) ((b) += utf_char2bytes((c), (b)))
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index bf4f9c57e5..04acb88bb3 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -38,8 +38,8 @@
 // Direction for nv_mousescroll() and ins_mousescroll()
 #define MSCR_DOWN       0     // DOWN must be FALSE
 #define MSCR_UP         1
-#define MSCR_LEFT       -1
-#define MSCR_RIGHT      -2
+#define MSCR_LEFT       (-1)
+#define MSCR_RIGHT      (-2)
 
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 98c2b84770..6ce95ea690 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -57,7 +57,7 @@
 #define ENC_DFLT       "utf-8"
 
 // end-of-line style
-#define EOL_UNKNOWN     -1      // not defined yet
+#define EOL_UNKNOWN     (-1)    // not defined yet
 #define EOL_UNIX        0       // NL
 #define EOL_DOS         1       // CR NL
 #define EOL_MAC         2       // CR
@@ -899,7 +899,7 @@ enum {
 };
 
 // Value for b_p_ul indicating the global value must be used.
-#define NO_LOCAL_UNDOLEVEL -123456
+#define NO_LOCAL_UNDOLEVEL (-123456)
 
 #define SB_MAX 100000  // Maximum 'scrollback' value.
 
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 1d112bd64a..913cfb2074 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -34,7 +34,7 @@
 
 // In the NFA engine: how many states are allowed.
 #define NFA_MAX_STATES 100000
-#define NFA_TOO_EXPENSIVE -1
+#define NFA_TOO_EXPENSIVE (-1)
 
 // Which regexp engine to use? Needed for vim_regcomp().
 // Must match with 'regexpengine'.
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index d15e4b7e45..5e86dacd25 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -65,8 +65,8 @@ extern StlClickDefinition *tab_page_click_defs;
 /// Size of the tab_page_click_defs array
 extern long tab_page_click_defs_size;
 
-#define W_ENDCOL(wp)   (wp->w_wincol + wp->w_width)
-#define W_ENDROW(wp)   (wp->w_winrow + wp->w_height)
+#define W_ENDCOL(wp)   ((wp)->w_wincol + (wp)->w_width)
+#define W_ENDROW(wp)   ((wp)->w_winrow + (wp)->w_height)
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
 # include "screen.h.generated.h"
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 220f51cd2f..12e44cb7ff 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -93,9 +93,9 @@ typedef int salfirst_T;
 
 // Values for SP_*ERROR are negative, positive values are used by
 // read_cnt_string().
-#define SP_TRUNCERROR   -1      // spell file truncated error
-#define SP_FORMERROR    -2      // format error in spell file
-#define SP_OTHERERROR   -3      // other error while reading spell file
+#define SP_TRUNCERROR   (-1)    // spell file truncated error
+#define SP_FORMERROR    (-2)    // format error in spell file
+#define SP_OTHERERROR   (-3)    // other error while reading spell file
 
 // Structure used to store words and other info for one language, loaded from
 // a .spl file.
diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h
index a005f67209..c656f21181 100644
--- a/src/nvim/syntax_defs.h
+++ b/src/nvim/syntax_defs.h
@@ -7,7 +7,7 @@
 #define SST_MAX_ENTRIES 1000   // maximal size for state stack array
 #define SST_FIX_STATES  7      // size of sst_stack[].
 #define SST_DIST        16     // normal distance between entries
-#define SST_INVALID    (synstate_T *)-1        // invalid syn_state pointer
+#define SST_INVALID    ((synstate_T *)-1)      // invalid syn_state pointer
 
 typedef struct syn_state synstate_T;
 
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 5d632b1b25..f65dd4f0d4 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -264,7 +264,7 @@ enum { FOLD_TEXT_LEN = 51, };  //!< buffer size for get_foldtext()
 
 // Prefer using semsg(), because perror() may send the output to the wrong
 // destination and mess up the screen.
-#define PERROR(msg) (void)semsg("%s: %s", msg, strerror(errno))
+#define PERROR(msg) (void)semsg("%s: %s", (msg), strerror(errno))
 
 #define SHOWCMD_COLS 10                 // columns needed by shown command
 
@@ -319,7 +319,7 @@ enum { FOLD_TEXT_LEN = 51, };  //!< buffer size for get_foldtext()
 #endif
 
 // Replacement for nchar used by nv_replace().
-#define REPLACE_CR_NCHAR    -1
-#define REPLACE_NL_NCHAR    -2
+#define REPLACE_CR_NCHAR    (-1)
+#define REPLACE_NL_NCHAR    (-2)
 
 #endif  // NVIM_VIM_H
-- 
cgit 


From 69e11b58b4db0952f11a5ff85aa7150b5f5b8db8 Mon Sep 17 00:00:00 2001
From: Brian Leung 
Date: Sun, 3 Apr 2022 02:36:01 -0700
Subject: vim-patch:8.2.4402: missing parenthesis may cause unexpected problems

Problem:    Missing parenthesis may cause unexpected problems.
Solution:   Add more parenthesis is macros.
https://github.com/vim/vim/commit/ae6f1d8b14c2f63811ee83ef14e32086fb3e9b83
---
 src/nvim/charset.c     |  4 ++--
 src/nvim/eval/funcs.c  |  2 +-
 src/nvim/fold.c        |  2 +-
 src/nvim/getchar.c     |  2 +-
 src/nvim/macros.h      |  2 +-
 src/nvim/memline.c     |  2 +-
 src/nvim/normal.c      |  2 +-
 src/nvim/os/win_defs.h |  2 +-
 src/nvim/quickfix.c    | 20 ++++++++++----------
 src/nvim/regexp.c      | 18 +++++++++---------
 src/nvim/screen.c      | 14 +++++++-------
 src/nvim/search.c      | 10 +++++-----
 src/nvim/sha256.c      | 15 +++++++--------
 src/nvim/spell.c       | 12 ++++++------
 src/nvim/spellfile.c   |  2 +-
 src/nvim/syntax.c      |  6 +++---
 src/nvim/window.c      |  2 +-
 17 files changed, 58 insertions(+), 59 deletions(-)

(limited to 'src')

diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 986a89b0a5..ec1866e9cc 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -425,9 +425,9 @@ char_u *str_foldcase(char_u *str, int orglen, char_u *buf, int buflen)
   int len = orglen;
 
 #define GA_CHAR(i) ((char_u *)ga.ga_data)[i]
-#define GA_PTR(i) ((char_u *)ga.ga_data + i)
+#define GA_PTR(i) ((char_u *)ga.ga_data + (i))
 #define STR_CHAR(i) (buf == NULL ? GA_CHAR(i) : buf[i])
-#define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + i)
+#define STR_PTR(i) (buf == NULL ? GA_PTR(i) : buf + (i))
 
   // Copy "str" into "buf" or allocated memory, unmodified.
   if (buf == NULL) {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 92672be8d0..f7d9f76534 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -6550,7 +6550,7 @@ static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *c
                                                   uint32_t *const z, uint32_t *const w)
   FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE
 {
-#define ROTL(x, k) ((x << k) | (x >> (32 - k)))
+#define ROTL(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
   const uint32_t result = ROTL(*y * 5, 7) * 9;
   const uint32_t t = *y << 9;
   *z ^= *x;
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 54430d46af..3643ceb460 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -2753,7 +2753,7 @@ static void truncate_fold(win_T *const wp, fold_T *fp, linenr_T end)
 #define FOLD_END(fp) ((fp)->fd_top + (fp)->fd_len - 1)
 #define VALID_FOLD(fp, gap) \
   ((gap)->ga_len > 0 && (fp) < ((fold_T *)(gap)->ga_data + (gap)->ga_len))
-#define FOLD_INDEX(fp, gap) ((size_t)(fp - ((fold_T *)(gap)->ga_data)))
+#define FOLD_INDEX(fp, gap) ((size_t)((fp) - ((fold_T *)(gap)->ga_data)))
 void foldMoveRange(win_T *const wp, garray_T *gap, const linenr_T line1, const linenr_T line2,
                    const linenr_T dest)
 {
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index c944a5ec93..c19405ccbb 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2573,7 +2573,7 @@ int inchar(char_u *buf, int maxlen, long wait_time)
     // Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
     // and buf may be pointing inside typebuf.tb_buf[].
     if (got_int) {
-#define DUM_LEN MAXMAPLEN * 3 + 3
+#define DUM_LEN (MAXMAPLEN * 3 + 3)
       char_u dum[DUM_LEN + 1];
 
       for (;;) {
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 5017e286d3..be1ab935c0 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -133,7 +133,7 @@
 #define ARRAY_LAST_ENTRY(arr) (arr)[ARRAY_SIZE(arr) - 1]
 
 // Duplicated in os/win_defs.h to avoid include-order sensitivity.
-#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
+#define RGB_(r, g, b) (((r) << 16) | ((g) << 8) | (b))
 
 #define STR_(x) #x
 #define STR(x) STR_(x)
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 1df32f79e1..418887a6b1 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -230,7 +230,7 @@ static linenr_T lowest_marked = 0;
 #define ML_INSERT       0x12        // insert line
 #define ML_FIND         0x13        // just find the line
 #define ML_FLUSH        0x02        // flush locked block
-#define ML_SIMPLE(x)    (x & 0x10)  // DEL, INS or FIND
+#define ML_SIMPLE(x)    ((x) & 0x10)  // DEL, INS or FIND
 
 // argument for ml_upd_block0()
 typedef enum {
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 8332b8bbd6..5e46bd468d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2616,7 +2616,7 @@ void may_clear_cmdline(void)
 }
 
 // Routines for displaying a partly typed command
-#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30
+#define SHOWCMD_BUFLEN (SHOWCMD_COLS + 1 + 30)
 static char_u showcmd_buf[SHOWCMD_BUFLEN];
 static char_u old_showcmd_buf[SHOWCMD_BUFLEN];    // For push_showcmd()
 static bool showcmd_is_clear = true;
diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h
index efef77be7b..1ae86d6bbe 100644
--- a/src/nvim/os/win_defs.h
+++ b/src/nvim/os/win_defs.h
@@ -36,7 +36,7 @@
 // Windows defines a RGB macro that produces 0x00bbggrr color values for use
 // with GDI. Our macro is different, and we don't use GDI.
 // Duplicated from macros.h to avoid include-order sensitivity.
-#define RGB_(r, g, b) ((r << 16) | (g << 8) | b)
+#define RGB_(r, g, b) (((r) << 16) | ((g) << 8) | (b))
 
 #ifdef _MSC_VER
 # ifndef inline
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index c8d9e91fb5..1d589e8ca0 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -229,28 +229,28 @@ typedef struct vgr_args_S {
 static char_u *e_no_more_items = (char_u *)N_("E553: No more items");
 
 // Quickfix window check helper macro
-#define IS_QF_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref == NULL)
+#define IS_QF_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref == NULL)
 // Location list window check helper macro
-#define IS_LL_WINDOW(wp) (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
+#define IS_LL_WINDOW(wp) (bt_quickfix((wp)->w_buffer) && (wp)->w_llist_ref != NULL)
 
 // Quickfix and location list stack check helper macros
-#define IS_QF_STACK(qi)       (qi->qfl_type == QFLT_QUICKFIX)
-#define IS_LL_STACK(qi)       (qi->qfl_type == QFLT_LOCATION)
-#define IS_QF_LIST(qfl)       (qfl->qfl_type == QFLT_QUICKFIX)
-#define IS_LL_LIST(qfl)       (qfl->qfl_type == QFLT_LOCATION)
+#define IS_QF_STACK(qi)       ((qi)->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_STACK(qi)       ((qi)->qfl_type == QFLT_LOCATION)
+#define IS_QF_LIST(qfl)       ((qfl)->qfl_type == QFLT_QUICKFIX)
+#define IS_LL_LIST(qfl)       ((qfl)->qfl_type == QFLT_LOCATION)
 
 //
 // Return location list for window 'wp'
 // For location list window, return the referenced location list
 //
-#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? wp->w_llist_ref : wp->w_llist)
+#define GET_LOC_LIST(wp) (IS_LL_WINDOW(wp) ? (wp)->w_llist_ref : (wp)->w_llist)
 
 // Macro to loop through all the items in a quickfix list
 // Quickfix item index starts from 1, so i below starts at 1
 #define FOR_ALL_QFL_ITEMS(qfl, qfp, i) \
-  for (i = 1, qfp = qfl->qf_start;  /* NOLINT(readability/braces) */ \
-       !got_int && i <= qfl->qf_count && qfp != NULL; \
-       i++, qfp = qfp->qf_next)
+  for ((i) = 1, (qfp) = (qfl)->qf_start;  /* NOLINT(readability/braces) */ \
+       !got_int && (i) <= (qfl)->qf_count && (qfp) != NULL; \
+       (i)++, (qfp) = (qfp)->qf_next)
 
 
 // Looking up a buffer can be slow if there are many.  Remember the last one
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index d3c43e530a..53add57736 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -279,15 +279,15 @@ static void init_class_tab(void)
   done = true;
 }
 
-# define ri_digit(c)    (c < 0x100 && (class_tab[c] & RI_DIGIT))
-# define ri_hex(c)      (c < 0x100 && (class_tab[c] & RI_HEX))
-# define ri_octal(c)    (c < 0x100 && (class_tab[c] & RI_OCTAL))
-# define ri_word(c)     (c < 0x100 && (class_tab[c] & RI_WORD))
-# define ri_head(c)     (c < 0x100 && (class_tab[c] & RI_HEAD))
-# define ri_alpha(c)    (c < 0x100 && (class_tab[c] & RI_ALPHA))
-# define ri_lower(c)    (c < 0x100 && (class_tab[c] & RI_LOWER))
-# define ri_upper(c)    (c < 0x100 && (class_tab[c] & RI_UPPER))
-# define ri_white(c)    (c < 0x100 && (class_tab[c] & RI_WHITE))
+# define ri_digit(c)    ((c) < 0x100 && (class_tab[c] & RI_DIGIT))
+# define ri_hex(c)      ((c) < 0x100 && (class_tab[c] & RI_HEX))
+# define ri_octal(c)    ((c) < 0x100 && (class_tab[c] & RI_OCTAL))
+# define ri_word(c)     ((c) < 0x100 && (class_tab[c] & RI_WORD))
+# define ri_head(c)     ((c) < 0x100 && (class_tab[c] & RI_HEAD))
+# define ri_alpha(c)    ((c) < 0x100 && (class_tab[c] & RI_ALPHA))
+# define ri_lower(c)    ((c) < 0x100 && (class_tab[c] & RI_LOWER))
+# define ri_upper(c)    ((c) < 0x100 && (class_tab[c] & RI_UPPER))
+# define ri_white(c)    ((c) < 0x100 && (class_tab[c] & RI_WHITE))
 
 // flags for regflags
 #define RF_ICASE    1   // ignore case
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index f922a50260..baeb2fcb03 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2119,13 +2119,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
 
   // draw_state: items that are drawn in sequence:
 #define WL_START        0               // nothing done yet
-#define WL_CMDLINE     WL_START + 1    // cmdline window column
-#define WL_FOLD        WL_CMDLINE + 1  // 'foldcolumn'
-#define WL_SIGN        WL_FOLD + 1     // column for signs
-#define WL_NR           WL_SIGN + 1     // line number
-#define WL_BRI         WL_NR + 1       // 'breakindent'
-#define WL_SBR         WL_BRI + 1       // 'showbreak' or 'diff'
-#define WL_LINE         WL_SBR + 1      // text in the line
+#define WL_CMDLINE      (WL_START + 1)    // cmdline window column
+#define WL_FOLD         (WL_CMDLINE + 1)  // 'foldcolumn'
+#define WL_SIGN         (WL_FOLD + 1)     // column for signs
+#define WL_NR           (WL_SIGN + 1)     // line number
+#define WL_BRI          (WL_NR + 1)       // 'breakindent'
+#define WL_SBR          (WL_BRI + 1)      // 'showbreak' or 'diff'
+#define WL_LINE         (WL_SBR + 1)      // text in the line
   int draw_state = WL_START;            // what to draw next
 
   int syntax_flags    = 0;
diff --git a/src/nvim/search.c b/src/nvim/search.c
index a69bd641f8..c30e69391f 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -4830,15 +4830,15 @@ typedef struct {
 /// bonus if the first letter is matched
 #define FIRST_LETTER_BONUS 15
 /// penalty applied for every letter in str before the first match
-#define LEADING_LETTER_PENALTY -5
+#define LEADING_LETTER_PENALTY (-5)
 /// maximum penalty for leading letters
-#define MAX_LEADING_LETTER_PENALTY -15
+#define MAX_LEADING_LETTER_PENALTY (-15)
 /// penalty for every letter that doesn't match
-#define UNMATCHED_LETTER_PENALTY -1
+#define UNMATCHED_LETTER_PENALTY (-1)
 /// penalty for gap in matching positions (-2 * k)
-#define GAP_PENALTY -2
+#define GAP_PENALTY (-2)
 /// Score for a string that doesn't fuzzy match the pattern
-#define SCORE_NONE -9999
+#define SCORE_NONE (-9999)
 
 #define FUZZY_MATCH_RECURSION_LIMIT 10
 
diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c
index 9d6a2f2c41..2c6199bf8b 100644
--- a/src/nvim/sha256.c
+++ b/src/nvim/sha256.c
@@ -73,8 +73,8 @@ static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFE
   GET_UINT32(W[14], data, 56);
   GET_UINT32(W[15], data, 60);
 
-#define  SHR(x, n) ((x & 0xFFFFFFFF) >> n)
-#define ROTR(x, n) (SHR(x, n) | (x << (32 - n)))
+#define  SHR(x, n) (((x) & 0xFFFFFFFF) >> (n))
+#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n))))
 
 #define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^  SHR(x, 3))
 #define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^  SHR(x, 10))
@@ -82,17 +82,16 @@ static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFE
 #define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
 #define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
 
-#define F0(x, y, z) ((x & y) | (z & (x | y)))
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F0(x, y, z) (((x) & (y)) | ((z) & ((x) | (y))))
+#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
 
 #define R(t) \
-  (W[t] = S1(W[t -  2]) + W[t -  7] + \
-          S0(W[t - 15]) + W[t - 16])
+  (W[t] = S1(W[(t) -  2]) + W[(t) -  7] + S0(W[(t) - 15]) + W[(t) - 16])
 
 #define P(a, b, c, d, e, f, g, h, x, K) { \
-  temp1 = h + S3(e) + F1(e, f, g) + K + x; \
+  temp1 = (h) + S3(e) + F1(e, f, g) + (K) + (x); \
   temp2 = S2(a) + F0(a, b, c); \
-  d += temp1; h = temp1 + temp2; \
+  (d) += temp1; (h) = temp1 + temp2; \
 }
 
   A = ctx->state[0];
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index fb644daa39..97f39c925a 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -62,11 +62,11 @@
 // Disadvantage: When "the" is typed as "hte" it sounds quite different ("@"
 // vs "ht") and goes down in the list.
 // Used when 'spellsuggest' is set to "best".
-#define RESCORE(word_score, sound_score) ((3 * word_score + sound_score) / 4)
+#define RESCORE(word_score, sound_score) ((3 * (word_score) + (sound_score)) / 4)
 
 // Do the opposite: based on a maximum end score and a known sound score,
 // compute the maximum word score that can be used.
-#define MAXSCORE(word_score, sound_score) ((4 * word_score - sound_score) / 3)
+#define MAXSCORE(word_score, sound_score) ((4 * (word_score) - (sound_score)) / 3)
 
 #include 
 #include 
@@ -122,7 +122,7 @@
 #define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP)
 
 // Result values.  Lower number is accepted over higher one.
-#define SP_BANNED       -1
+#define SP_BANNED       (-1)
 #define SP_RARE         0
 #define SP_OK           1
 #define SP_LOCAL        2
@@ -176,7 +176,7 @@ typedef struct {
 #define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i])
 
 // True if a word appears in the list of banned words.
-#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&su->su_banned, word)))
+#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&(su)->su_banned, word)))
 
 // Number of suggestions kept when cleaning up.  We need to keep more than
 // what is displayed, because when rescore_suggestions() is called the score
@@ -225,7 +225,7 @@ typedef struct {
 #define SCORE_SFMAX2    300     // maximum score for second try
 #define SCORE_SFMAX3    400     // maximum score for third try
 
-#define SCORE_BIG       SCORE_INS * 3   // big difference
+#define SCORE_BIG       (SCORE_INS * 3)  // big difference
 #define SCORE_MAXMAX    999999          // accept any score
 #define SCORE_LIMITMAX  350             // for spell_edit_score_limit()
 
@@ -5284,7 +5284,7 @@ static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *
 }
 
 static sftword_T dumsft;
-#define HIKEY2SFT(p)  ((sftword_T *)(p - (dumsft.sft_word - (char_u *)&dumsft)))
+#define HIKEY2SFT(p)  ((sftword_T *)((p) - (dumsft.sft_word - (char_u *)&dumsft)))
 #define HI2SFT(hi)     HIKEY2SFT((hi)->hi_key)
 
 // Prepare for calling suggest_try_soundalike().
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 07058b34fd..3d3e6e728c 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -1850,7 +1850,7 @@ static void spell_reload_one(char_u *fname, bool added_word)
 // In the postponed prefixes tree wn_flags is used to store the WFP_ flags,
 // but it must be negative to indicate the prefix tree to tree_add_word().
 // Use a negative number with the lower 8 bits zero.
-#define PFX_FLAGS       -256
+#define PFX_FLAGS       (-256)
 
 // flags for "condit" argument of store_aff_word()
 #define CONDIT_COMB     1       // affix must combine
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index f829b6a270..a82e8fa103 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -181,7 +181,7 @@ static char *(spo_name_tab[SPO_COUNT]) =
 
 #define SYN_ITEMS(buf)  ((synpat_T *)((buf)->b_syn_patterns.ga_data))
 
-#define NONE_IDX        -2      // value of sp_sync_idx for "NONE"
+#define NONE_IDX        (-2)    // value of sp_sync_idx for "NONE"
 
 /*
  * Flags for b_syn_sync_flags:
@@ -267,9 +267,9 @@ static int keepend_level = -1;
 static char msg_no_items[] = N_("No Syntax items defined for this buffer");
 
 // value of si_idx for keywords
-#define KEYWORD_IDX     -1
+#define KEYWORD_IDX     (-1)
 // valid of si_cont_list for containing all but contained groups
-#define ID_LIST_ALL     (int16_t *)-1
+#define ID_LIST_ALL     ((int16_t *)-1)
 
 static int next_seqnr = 1;              // value to use for si_seqnr
 
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 632e9b3f44..9ac51bb108 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -59,7 +59,7 @@
 #endif
 
 
-#define NOWIN           (win_T *)-1     // non-existing window
+#define NOWIN           ((win_T *)-1)   // non-existing window
 
 #define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height())
 
-- 
cgit 


From a973fa5b4397933e94d888d06e435a986fc4a218 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 09:12:20 +0800
Subject: test(old): fix test_regexp_latin.vim encoding (#17989)

Cherry-pick a change from Vim patch 8.2.3982
---
 src/nvim/testdir/test_regexp_latin.vim | 61 ++++++++++++++++++----------------
 1 file changed, 32 insertions(+), 29 deletions(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim
index b733e334de..13e44b090f 100644
--- a/src/nvim/testdir/test_regexp_latin.vim
+++ b/src/nvim/testdir/test_regexp_latin.vim
@@ -6,7 +6,10 @@ scriptencoding latin1
 source check.vim
 
 func s:equivalence_test()
-  let str = "AÀÃÂÃÄÅ B C D EÈÉÊË F G H IÃŒÃÃŽÃ J K L M NÑ OÒÓÔÕÖØ P Q R S T UÙÚÛÜ V W X Yà Z aàáâãäå b c d eèéêë f g h iìíîï j k l m nñ oòóôõöø p q r s t uùúûü v w x yýÿ z"
+  let str = 'AÀÁÂÃÄÅ B C D EÈÉÊË F G H IÌÍÎÏ J K L M NÑ OÒÓÔÕÖØ P Q R S T UÙÚÛÜ V W X YÝ Z '
+  \      .. 'aàáâãäå b c d eèéêë f g h iìíîï j k l m nñ oòóôõöø p q r s t uùúûü v w x yýÿ z '
+  \      .. "0 1 2 3 4 5 6 7 8 9 "
+  \      .. "` ~ ! ? ; : . , / \\ ' \" | < > [ ] { } ( ) @ # $ % ^ & * _ - + \b \e \f \n \r \t"
   let groups = split(str)
   for group1 in groups
       for c in split(group1, '\zs')
@@ -337,7 +340,7 @@ func Test_regexp_single_line_pat()
   call add(tl, [2, '\v((ab)|c*)+', 'abcccaba', 'abcccab', '', 'ab'])
   call add(tl, [2, '\v(a(c*)+b)+', 'acbababaaa', 'acbabab', 'ab', ''])
   call add(tl, [2, '\v(a|b*)+', 'aaaa', 'aaaa', ''])
-  call add(tl, [2, '\p*', 'aá 	', 'aá '])
+  call add(tl, [2, '\p*', 'aá 	', 'aá '])
 
   " Test greedy-ness and lazy-ness
   call add(tl, [2, 'a\{-2,7}','aaaaaaaaaaaaa', 'aa'])
@@ -790,12 +793,12 @@ endfunc
 " Check patterns matching cursor position.
 func s:curpos_test2()
   new
-  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
-        \ '3 foobar eins zwei drei vier fünf sechse',
-        \ '4 foobar eins zwei drei vier fünf sechse',
-        \ '5	foobar eins zwei drei vier fünf sechse',
-        \ '6	foobar eins zwei drei vier fünf sechse',
-        \ '7	foobar eins zwei drei vier fünf sechse'])
+  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
+        \ '3 foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '5	foobar eins zwei drei vier fünf sechse',
+        \ '6	foobar eins zwei drei vier fünf sechse',
+        \ '7	foobar eins zwei drei vier fünf sechse'])
   call setpos('.', [0, 2, 10, 0])
   s/\%.c.*//g
   call setpos('.', [0, 3, 15, 0])
@@ -805,10 +808,10 @@ func s:curpos_test2()
   call assert_equal(['1',
         \ '2 foobar ',
         \ '',
-        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
         \ '5	_',
-        \ '6	foobar eins zwei drei vier fünf sechse',
-        \ '7	foobar eins zwei drei vier fünf sechse'],
+        \ '6	foobar eins zwei drei vier fünf sechse',
+        \ '7	foobar eins zwei drei vier fünf sechse'],
         \ getline(1, '$'))
   call assert_fails('call search("\\%.1l")', 'E1204:')
   call assert_fails('call search("\\%.1c")', 'E1204:')
@@ -819,12 +822,12 @@ endfunc
 " Check patterns matching before or after cursor position.
 func s:curpos_test3()
   new
-  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
-        \ '3 foobar eins zwei drei vier fünf sechse',
-        \ '4 foobar eins zwei drei vier fünf sechse',
-        \ '5	foobar eins zwei drei vier fünf sechse',
-        \ '6	foobar eins zwei drei vier fünf sechse',
-        \ '7	foobar eins zwei drei vier fünf sechse'])
+  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
+        \ '3 foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '5	foobar eins zwei drei vier fünf sechse',
+        \ '6	foobar eins zwei drei vier fünf sechse',
+        \ '7	foobar eins zwei drei vier fünf sechse'])
   call setpos('.', [0, 2, 10, 0])
   " Note: This removes all columns, except for the column directly in front of
   " the cursor. Bug????
@@ -838,27 +841,27 @@ func s:curpos_test3()
   call setpos('.', [0, 6, 4, 0])
   :s/\%>.v.*$/_/
   call assert_equal(['1',
-        \ ' eins zwei drei vier fünf sechse',
+        \ ' eins zwei drei vier fünf sechse',
         \ '3 foobar e',
-        \ '4 foobar eins zwei drei vier fünf sechse',
-        \ '_foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '_foobar eins zwei drei vier fünf sechse',
         \ '6	fo_',
-        \ '7	foobar eins zwei drei vier fünf sechse'],
+        \ '7	foobar eins zwei drei vier fünf sechse'],
         \ getline(1, '$'))
   sil %d
-  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
-        \ '3 foobar eins zwei drei vier fünf sechse',
-        \ '4 foobar eins zwei drei vier fünf sechse',
-        \ '5	foobar eins zwei drei vier fünf sechse',
-        \ '6	foobar eins zwei drei vier fünf sechse',
-        \ '7	foobar eins zwei drei vier fünf sechse'])
+  call setline(1, ['1', '2 foobar eins zwei drei vier fünf sechse',
+        \ '3 foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '5	foobar eins zwei drei vier fünf sechse',
+        \ '6	foobar eins zwei drei vier fünf sechse',
+        \ '7	foobar eins zwei drei vier fünf sechse'])
   call setpos('.', [0, 4, 4, 0])
   %s/\%<.l.*//
   call setpos('.', [0, 5, 4, 0])
   %s/\%>.l.*//
   call assert_equal(['', '', '',
-        \ '4 foobar eins zwei drei vier fünf sechse',
-        \ '5	foobar eins zwei drei vier fünf sechse',
+        \ '4 foobar eins zwei drei vier fünf sechse',
+        \ '5	foobar eins zwei drei vier fünf sechse',
         \ '', ''],
         \ getline(1, '$'))
   bwipe!
-- 
cgit 


From 1f038bc592282ca60982b288b4c4bc48fcb37839 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 08:30:55 +0800
Subject: test(old): fix test_spell.vim encoding

---
 src/nvim/testdir/test_spell.vim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 1ecb5c8070..d3a11aebd8 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -407,7 +407,7 @@ func Test_zz_basic()
         \ )
 
   call assert_equal("gebletegek", soundfold('goobledygoook'))
-  call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold())
+  call assert_equal("kepereneven", 'kóopërÿnôven'->soundfold())
   call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale'))
 endfunc
 
@@ -588,7 +588,7 @@ func Test_zz_sal_and_addition()
   mkspell! Xtest Xtest
   set spl=Xtest.latin1.spl spell
   call assert_equal('kbltykk', soundfold('goobledygoook'))
-  call assert_equal('kprnfn', soundfold('kóopërÿnôven'))
+  call assert_equal('kprnfn', soundfold('kóopërÿnôven'))
   call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale'))
 
   "also use an addition file
-- 
cgit 


From 945caeeda2f0a9c660cf05f655dad5d7a88cd4f5 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 07:28:03 +0800
Subject: vim-patch:8.2.4247: stack corruption when looking for spell
 suggestions

Problem:    Stack corruption when looking for spell suggestions.
Solution:   Prevent the depth increased too much.  Add a five second time
            limit to finding suggestions.
https://github.com/vim/vim/commit/06f15416bb8d5636200a10776f1752c4d6e49f31

Cherry-pick parentheses from patch 8.2.4402.
---
 src/nvim/spell.c                | 11 +++++++++--
 src/nvim/testdir/test_spell.vim |  8 ++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index 97f39c925a..c4504a36ee 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3690,7 +3690,7 @@ static void suggest_try_change(suginfo_T *su)
 
 // Check the maximum score, if we go over it we won't try this change.
 #define TRY_DEEPER(su, stack, depth, add) \
-  (stack[depth].ts_score + (add) < su->su_maxscore)
+  ((depth) < MAXWLEN && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
 
 // Try finding suggestions by adding/removing/swapping letters.
 //
@@ -3794,6 +3794,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
     }
   }
 
+  // The loop may take an indefinite amount of time. Break out after five
+  // sectonds. TODO(vim): add an option for the time limit.
+  proftime_T time_limit = profile_setlimit(5000);
+
   // Loop to find all suggestions.  At each round we either:
   // - For the current state try one operation, advance "ts_curi",
   //   increase "depth".
@@ -3824,7 +3828,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
 
         // At end of a prefix or at start of prefixtree: check for
         // following word.
-        if (byts[arridx] == 0 || n == STATE_NOPREFIX) {
+        if (depth < MAXWLEN && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
           // Set su->su_badflags to the caps type at this position.
           // Use the caps type until here for the prefix itself.
           n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
@@ -4927,6 +4931,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
       if (--breakcheckcount == 0) {
         os_breakcheck();
         breakcheckcount = 1000;
+        if (profile_passed_limit(time_limit)) {
+          got_int = true;
+        }
       }
     }
   }
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index d3a11aebd8..56ed97cdd9 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -681,6 +681,14 @@ func Test_spell_long_word()
   set nospell
 endfunc
 
+func Test_spellsuggest_too_deep()
+  " This was incrementing "depth" over MAXWLEN.
+  new
+  norm s000G00ý000000000000
+  sil norm ..vzG................vvzG0     v z=
+  bwipe!
+endfunc
+
 func LoadAffAndDic(aff_contents, dic_contents)
   throw 'skipped: Nvim does not support enc=latin1'
   set enc=latin1
-- 
cgit 


From 683648a396a804a8d54731283d525f0c34193361 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 07:46:33 +0800
Subject: vim-patch:8.2.4258: Coverity warns for array overrun

Problem:    Coverity warns for array overrun.
Solution:   Restrict depth to MAXWLEN - 1.
https://github.com/vim/vim/commit/6970e1e36a1ecdb4d330d6ac9ca76f97fa515e36
---
 src/nvim/spell.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index c4504a36ee..2d617bba4b 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -3690,7 +3690,7 @@ static void suggest_try_change(suginfo_T *su)
 
 // Check the maximum score, if we go over it we won't try this change.
 #define TRY_DEEPER(su, stack, depth, add) \
-  ((depth) < MAXWLEN && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
+  ((depth) < MAXWLEN - 1 && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
 
 // Try finding suggestions by adding/removing/swapping letters.
 //
@@ -3828,7 +3828,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
 
         // At end of a prefix or at start of prefixtree: check for
         // following word.
-        if (depth < MAXWLEN && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
+        if (depth < MAXWLEN - 1 && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
           // Set su->su_badflags to the caps type at this position.
           // Use the caps type until here for the prefix itself.
           n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
-- 
cgit 


From 64869831171ffa455f35d1a1ce3a3f9c7e7416a2 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 11:15:31 +0800
Subject: vim-patch:8.2.4253: using freed memory when substitute with function
 call

Problem:    Using freed memory when substitute uses a recursive function call.
Solution:   Make a copy of the substitute text.
https://github.com/vim/vim/commit/37f47958b8a2a44abc60614271d9537e7f14e51a

'compatible' doesn't seem needed for the test.
---
 src/nvim/ex_cmds.c                   | 16 ++++++++++++----
 src/nvim/testdir/test_substitute.vim | 18 ++++++++++++++++++
 2 files changed, 30 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 71b3517adc..830e764104 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3627,13 +3627,20 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
 
   sub_firstline = NULL;
 
-  // ~ in the substitute pattern is replaced with the old pattern.
-  // We do it here once to avoid it to be replaced over and over again.
-  // But don't do it when it starts with "\=", then it's an expression.
   assert(sub != NULL);
 
   bool sub_needs_free = false;
-  if (!(sub[0] == '\\' && sub[1] == '=')) {
+  char_u *sub_copy = NULL;
+
+  // If the substitute pattern starts with "\=" then it's an expression.
+  // Make a copy, a recursive function may free it.
+  // Otherwise, '~' in the substitute pattern is replaced with the old
+  // pattern.  We do it here once to avoid it to be replaced over and over
+  // again.
+  if (sub[0] == '\\' && sub[1] == '=') {
+    sub = vim_strsave(sub);
+    sub_copy = sub;
+  } else {
     char_u *source = sub;
     sub = regtilde(sub, p_magic);
     // When previewing, the new pattern allocated by regtilde() needs to be freed
@@ -4412,6 +4419,7 @@ skip:
   }
 
   vim_regfree(regmatch.regprog);
+  xfree(sub_copy);
 
   // Restore the flag values, they can be used for ":&&".
   subflags.do_all = save_do_all;
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index 9710a7ab84..86fd0147a5 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -819,4 +819,22 @@ func Test_substitute_skipped_range()
   bwipe!
 endfunc
 
+" This was using "old_sub" after it was freed.
+func Test_using_old_sub()
+  " set compatible maxfuncdepth=10
+  set maxfuncdepth=10
+  new
+  call setline(1, 'some text.')
+  func Repl()
+    ~
+    s/
+  endfunc
+  silent!  s/\%')/\=Repl()
+
+  delfunc Repl
+  bwipe!
+  set nocompatible
+endfunc
+
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From bbfc44e255ec6d1a6ced68dde64ff5d3c68b9ceb Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Mon, 4 Apr 2022 11:39:12 +0800
Subject: refactor: pass "preview" to regtilde()

---
 src/nvim/ex_cmds.c | 9 ++++-----
 src/nvim/regexp.c  | 4 ++--
 src/nvim/shada.c   | 2 +-
 3 files changed, 7 insertions(+), 8 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 830e764104..65cb544efd 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3642,7 +3642,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, bool do_buf_event, handle
     sub_copy = sub;
   } else {
     char_u *source = sub;
-    sub = regtilde(sub, p_magic);
+    sub = regtilde(sub, p_magic, preview);
     // When previewing, the new pattern allocated by regtilde() needs to be freed
     // in this function because it will not be used or freed by regtilde() later.
     sub_needs_free = preview && sub != source;
@@ -4420,6 +4420,9 @@ skip:
 
   vim_regfree(regmatch.regprog);
   xfree(sub_copy);
+  if (sub_needs_free) {
+    xfree(sub);
+  }
 
   // Restore the flag values, they can be used for ":&&".
   subflags.do_all = save_do_all;
@@ -4452,10 +4455,6 @@ skip:
 
   kv_destroy(preview_lines.subresults);
 
-  if (sub_needs_free) {
-    xfree(sub);
-  }
-
   return preview_buf;
 #undef ADJUST_SUB_FIRSTLNUM
 #undef PUSH_PREVIEW_LINES
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 53add57736..9a04cc428a 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1531,7 +1531,7 @@ static fptr_T do_Lower(int *d, int c)
  *
  * The tildes are parsed once before the first call to vim_regsub().
  */
-char_u *regtilde(char_u *source, int magic)
+char_u *regtilde(char_u *source, int magic, bool preview)
 {
   char_u      *newsub = source;
   char_u      *tmpsub;
@@ -1576,7 +1576,7 @@ char_u *regtilde(char_u *source, int magic)
   }
 
   // Only change reg_prev_sub when not previewing.
-  if (!(State & CMDPREVIEW)) {
+  if (!preview) {
     xfree(reg_prev_sub);
     if (newsub != source) {             // newsub was allocated, just keep it
       reg_prev_sub = newsub;
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index b909888783..6c0add87d3 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1238,7 +1238,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
       // string is close to useless: you can only use it with :& or :~ and
       // that’s all because s//~ is not available until the first call to
       // regtilde. Vim was not calling this for some reason.
-      (void)(char *)regtilde((char_u *)cur_entry.data.sub_string.sub, p_magic);
+      (void)(char *)regtilde((char_u *)cur_entry.data.sub_string.sub, p_magic, false);
       // Do not free shada entry: its allocated memory was saved above.
       break;
     case kSDItemHistoryEntry:
-- 
cgit 


From b08cf73be959397b5715395f1465fb76a76a6a05 Mon Sep 17 00:00:00 2001
From: Miyelsh 
Date: Mon, 4 Apr 2022 03:18:00 -0400
Subject: refactor(pos.h): remove unused include; make formatting consistent
 (#17892)

- remove include of limit.h from pos.h, because it is no longer used
- make formatting more consistent in pos.h
---
 src/nvim/pos.h | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index 51991ed314..c60b008696 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -1,10 +1,8 @@
 #ifndef NVIM_POS_H
 #define NVIM_POS_H
 
-// for INT_MAX, LONG_MAX et al.
-#include 
-
-typedef long linenr_T;         // line number type
+/// Line number type
+typedef long linenr_T;
 /// Format used to print values which have linenr_T type
 #define PRIdLINENR "ld"
 
@@ -15,31 +13,29 @@ typedef int colnr_T;
 
 /// Maximal (invalid) line number
 enum { MAXLNUM = 0x7fffffff, };
+
 /// Maximal column number
 /// MAXCOL used to be INT_MAX, but with 64 bit ints that results in running
 /// out of memory when trying to allocate a very long line.
 enum { MAXCOL = 0x7fffffff, };
-// Minimum line number
+
+/// Minimum line number
 enum { MINLNUM = 1, };
-// minimum column number
+
+/// Minimum column number
 enum { MINCOL = 1, };
 
-/*
- * position in file or buffer
- */
+/// position in file or buffer
 typedef struct {
-  linenr_T lnum;        // line number
-  colnr_T col;          // column number
+  linenr_T lnum;        ///< line number
+  colnr_T col;          ///< column number
   colnr_T coladd;
 } pos_T;
 
-
-/*
- * Same, but without coladd.
- */
+/// position in file or buffer, but without coladd
 typedef struct {
-  linenr_T lnum;        // line number
-  colnr_T col;          // column number
+  linenr_T lnum;        ///< line number
+  colnr_T col;          ///< column number
 } lpos_T;
 
 #endif  // NVIM_POS_H
-- 
cgit 


From e135adcb8c4f32332ba87ea6681f41330b909e1c Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 5 Apr 2022 09:00:48 +0800
Subject: vim-patch:8.2.4687: "vimgrep /%v/ *" may cause a crash (#17995)

Problem:    "vimgrep /\%v/ *" may cause a crash.
Solution:   When compiling the pattern with the old engine fails, restore the
            regprog of the new engine instead of leaving it NULL.
            (closes vim/vim#10079)
https://github.com/vim/vim/commit/e8a4c0d91f89544e4f94b7bd612b5fb780944c33
---
 src/nvim/regexp.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index 9a04cc428a..b61c9a2cf5 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -2504,7 +2504,8 @@ long vim_regexec_multi(
     char_u *pat = vim_strsave(((nfa_regprog_T *)rmp->regprog)->pattern);
 
     p_re = BACKTRACKING_ENGINE;
-    vim_regfree(rmp->regprog);
+    regprog_T *prev_prog = rmp->regprog;
+
     report_re_switch(pat);
     // checking for \z misuse was already done when compiling for NFA,
     // allow all here
@@ -2512,7 +2513,13 @@ long vim_regexec_multi(
     rmp->regprog = vim_regcomp(pat, re_flags);
     reg_do_extmatch = 0;
 
-    if (rmp->regprog != NULL) {
+    if (rmp->regprog == NULL) {
+      // Somehow compiling the pattern failed now, put back the
+      // previous one to avoid "regprog" becoming NULL.
+      rmp->regprog = prev_prog;
+    } else {
+      vim_regfree(prev_prog);
+
       rmp->regprog->re_in_use = true;
       result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col,
                                                    tm, timed_out);
-- 
cgit 


From 969d600f2a107507c60e4ac3f3a8c03210662f96 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Tue, 5 Apr 2022 21:38:53 +0800
Subject: vim-patch:8.2.{4692,4691,4690}: fix Insert mode  mapping
 bug (#17999)

vim-patch:8.2.4692: no test for what 8.2.4691 fixes

Problem:    No test for what 8.2.4691 fixes.
Solution:   Add a test.  Use a more generic sotlution. (closes vim/vim#10090)
https://github.com/vim/vim/commit/0f68e6c07aaf62c034a242f183b93c1bb44e7f93

Test cannot be used because it must use test_setmouse(). Use a Lua test.

Reverted patches:

vim-patch:8.2.4691: solution for  in a mapping causes trouble

Problem:    Solution for  in a mapping causes trouble.
Solution:   Use another solution: put back CTRL-O after reading the 
            sequence.
https://github.com/vim/vim/commit/ca9d8d2cb9fc6b9240f2a74ccd36f9d966488294

vim-patch:8.2.4689: using  in a mapping does not work for mouse keys

Problem:    Using  in a mapping does not work for mouse keys in Insert
            mode. (Sergey Vlasov)
Solution:   When reading the  argument do not use the stuff buffer.
            (closes vim/vim#10080)
https://github.com/vim/vim/commit/d0fb2d804183c2786578b4c32ba5b92938f93d0e
---
 src/nvim/getchar.c                | 16 +++++++++++++---
 src/nvim/normal.c                 |  9 ++++++---
 src/nvim/testdir/test_mapping.vim | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 6 deletions(-)

(limited to 'src')

diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 669d181364..eddd5ccd14 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -1253,7 +1253,14 @@ static int old_mod_mask;    // mod_mask for ungotten character
 static int old_mouse_grid;  // mouse_grid related to old_char
 static int old_mouse_row;   // mouse_row related to old_char
 static int old_mouse_col;   // mouse_col related to old_char
+static int old_KeyStuffed;  // whether old_char was stuffed
 
+static bool can_get_old_char(void)
+{
+    // If the old character was not stuffed and characters have been added to
+    // the stuff buffer, need to first get the stuffed characters instead.
+    return old_char != -1 && (old_KeyStuffed || stuff_empty());
+}
 
 /*
  * Save all three kinds of typeahead, so that the user must type at a prompt.
@@ -1454,7 +1461,7 @@ int vgetc(void)
    * If a character was put back with vungetc, it was already processed.
    * Return it directly.
    */
-  if (old_char != -1) {
+  if (can_get_old_char()) {
     c = old_char;
     old_char = -1;
     mod_mask = old_mod_mask;
@@ -1660,7 +1667,7 @@ int plain_vgetc(void)
  */
 int vpeekc(void)
 {
-  if (old_char != -1) {
+  if (can_get_old_char()) {
     return old_char;
   }
   return vgetorpeek(false);
@@ -2052,7 +2059,9 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
   return map_result_nomatch;
 }
 
-// unget one character (can only be done once!)
+/// unget one character (can only be done once!)
+/// If the character was stuffed, vgetc() will get it next time it was called.
+/// Otherwise vgetc() will only get it when the stuff buffer is empty.
 void vungetc(int c)
 {
   old_char = c;
@@ -2060,6 +2069,7 @@ void vungetc(int c)
   old_mouse_grid = mouse_grid;
   old_mouse_row = mouse_row;
   old_mouse_col = mouse_col;
+  old_KeyStuffed = KeyStuffed;
 }
 
 /// Gets a byte:
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 5e46bd468d..9cee2de0c5 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -1517,9 +1517,12 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
   for (;;) {
     which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
     if (is_drag) {
-      /* If the next character is the same mouse event then use that
-       * one. Speeds up dragging the status line. */
-      if (vpeekc() != NUL) {
+      // If the next character is the same mouse event then use that
+      // one. Speeds up dragging the status line.
+      // Note: Since characters added to the stuff buffer in the code
+      // below need to come before the next character, do not do this
+      // when the current character was stuffed.
+      if (!KeyStuffed && vpeekc() != NUL) {
         int nc;
         int save_mouse_grid = mouse_grid;
         int save_mouse_row = mouse_row;
diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim
index 98440ccdd7..a8dd0ca286 100644
--- a/src/nvim/testdir/test_mapping.vim
+++ b/src/nvim/testdir/test_mapping.vim
@@ -676,4 +676,38 @@ func Test_plug_remap()
   %bw!
 endfunc
 
+" Test for mapping  in Insert mode
+func Test_mouse_drag_insert_map()
+  CheckFunction test_setmouse
+  set mouse=a
+  func ClickExpr()
+    call test_setmouse(1, 1)
+    return "\"
+  endfunc
+  func DragExpr()
+    call test_setmouse(1, 2)
+    return "\"
+  endfunc
+  inoremap   ClickExpr()
+  imap   DragExpr()
+
+  inoremap  let g:dragged = 1
+  exe "normal i\\"
+  call assert_equal(1, g:dragged)
+  call assert_equal('v', mode())
+  exe "normal! \\"
+  unlet g:dragged
+
+  inoremap  
+  exe "normal i\\"
+  call assert_equal('n', mode())
+
+  iunmap 
+  iunmap 
+  iunmap 
+  delfunc ClickExpr
+  delfunc DragExpr
+  set mouse&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 463174b5d76d9a88fab02db144aaaac7fa19efd1 Mon Sep 17 00:00:00 2001
From: Famiu Haque 
Date: Tue, 8 Feb 2022 20:28:52 +0600
Subject: fix(ui): make window resize commands manage cmdheight

Previously, the window resize commands did not resize the value of `cmdheight` when they caused a change in the topframe height, leaving a gap between the end of topframe and the start of the command line, this commit fixes that by making window resize commands automatically change the value of cmdheight if the resize affects the height of topframe.
---
 src/nvim/window.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9ac51bb108..cc21bf25b0 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -5412,11 +5412,17 @@ void win_setheight_win(int height, win_T *win)
     // line, clear it.
     if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
       grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0);
+      if (msg_grid.chars) {
+        clear_cmdline = true;
+      }
     }
     cmdline_row = row;
+    p_ch = MAX(Rows - cmdline_row, 1);
+    curtab->tp_ch_used = p_ch;
     msg_row = row;
     msg_col = 0;
     redraw_all_later(NOT_VALID);
+    showmode();
   }
 }
 
@@ -5452,7 +5458,9 @@ static void frame_setheight(frame_T *curfrp, int height)
   if (curfrp->fr_parent == NULL) {
     // topframe: can only change the command line
     if (height > ROWS_AVAIL) {
-      height = ROWS_AVAIL;
+      // If height is greater than the available space, try to create space for the frame by
+      // reducing 'cmdheight' if possible, while making sure `cmdheight` doesn't go below 1.
+      height = MIN(ROWS_AVAIL + (p_ch - 1), height);
     }
     if (height > 0) {
       frame_new_height(curfrp, height, false, false);
-- 
cgit 


From 54cec455ccdae2a63144809f947dad5cf510b7c7 Mon Sep 17 00:00:00 2001
From: KillTheMule 
Date: Mon, 12 Oct 2020 18:01:43 +0200
Subject: fix(ui): inccomand performance degradation

It was broken since the introduction of the macro.
---
 src/nvim/ex_cmds.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 65cb544efd..5d5b342c35 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -4313,18 +4313,22 @@ skip:
 
 #define PUSH_PREVIEW_LINES() \
   do { \
-    linenr_T match_lines = current_match.end.lnum \
-                           - current_match.start.lnum +1; \
-    if (preview_lines.subresults.size > 0) { \
-      linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
-      if (last == current_match.start.lnum) { \
-        preview_lines.lines_needed += match_lines - 1; \
+    if (preview) { \
+      linenr_T match_lines = current_match.end.lnum \
+                             - current_match.start.lnum +1; \
+      if (preview_lines.subresults.size > 0) { \
+        linenr_T last = kv_last(preview_lines.subresults).end.lnum; \
+        if (last == current_match.start.lnum) { \
+          preview_lines.lines_needed += match_lines - 1; \
+        } else { \
+          preview_lines.lines_needed += match_lines; \
+        } \
+      } else { \
+        preview_lines.lines_needed += match_lines; \
       } \
-    } else { \
-      preview_lines.lines_needed += match_lines; \
+      kv_push(preview_lines.subresults, current_match); \
     } \
-    kv_push(preview_lines.subresults, current_match); \
-  } while (0)
+    } while (0)
 
             // Push the match to preview_lines.
             PUSH_PREVIEW_LINES();
-- 
cgit 


From 128bedc0d2435bbc754cdb954447fc1cbfd4ac13 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Wed, 6 Apr 2022 05:12:49 +0800
Subject: vim-patch:8.2.4696: delete() with "rf" argument does not report a
 failure (#18002)

Problem:    delete() with "rf" argument does not report a failure.
Solution:   Return -1 if the directory could not be removed. (closes vim/vim#10078)
https://github.com/vim/vim/commit/478700336d1c72e133b8ff6841e968c1bb1658ed
---
 src/nvim/fileio.c                   | 7 +++++--
 src/nvim/testdir/test_functions.vim | 9 +++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 64ad9a6d80..d68dda15f3 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -5408,15 +5408,18 @@ int delete_recursive(const char *name)
       for (int i = 0; i < ga.ga_len; i++) {
         vim_snprintf((char *)NameBuff, MAXPATHL, "%s/%s", exp, ((char_u **)ga.ga_data)[i]);
         if (delete_recursive((const char *)NameBuff) != 0) {
+          // Remember the failure but continue deleting any further
+          // entries.
           result = -1;
         }
       }
       ga_clear_strings(&ga);
+      if (os_rmdir(exp) != 0) {
+        result = -1;
+      }
     } else {
       result = -1;
     }
-    // Note: "name" value may be changed in delete_recursive().  Must use the saved value.
-    result = os_rmdir(exp) == 0 ? 0 : -1;
     xfree(exp);
   } else {
     // Delete symlink only.
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 79f718f4e8..f8be250f73 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1662,6 +1662,15 @@ func Test_delete_rf()
   call assert_equal(0, delete('Xdir', 'rf'))
   call assert_false(filereadable('Xdir/foo.txt'))
   call assert_false(filereadable('Xdir/[a-1]/foo.txt'))
+
+  if has('unix')
+    call mkdir('Xdir/Xdir2', 'p')
+    silent !chmod 555 Xdir
+    call assert_equal(-1, delete('Xdir/Xdir2', 'rf'))
+    call assert_equal(-1, delete('Xdir', 'rf'))
+    silent !chmod 755 Xdir
+    call assert_equal(0, delete('Xdir', 'rf'))
+  endif
 endfunc
 
 func Test_call()
-- 
cgit 


From 74a27748e674de4b24af35a6cc3aec2abf8222b4 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Sun, 3 Apr 2022 10:55:41 +0800
Subject: fix(autocmd): restore autocmd showing behavior

---
 src/nvim/autocmd.c | 105 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 73 insertions(+), 32 deletions(-)

(limited to 'src')

diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index bcac7fbc4a..5fc36a8412 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -135,7 +135,7 @@ static inline const char *get_deleted_augroup(void) FUNC_ATTR_ALWAYS_INLINE
 }
 
 // Show the autocommands for one AutoPat.
-static void aupat_show(AutoPat *ap)
+static void aupat_show(AutoPat *ap, event_T event, int previous_group)
 {
   // Check for "got_int" (here and at various places below), which is set
   // when "q" has been hit for the "--more--" prompt
@@ -148,6 +148,31 @@ static void aupat_show(AutoPat *ap)
     return;
   }
 
+  char *name = augroup_name(ap->group);
+
+  msg_putchar('\n');
+  if (got_int) {
+    return;
+  }
+  // When switching groups, we need to show the new group information.
+  if (ap->group != previous_group) {
+    // show the group name, if it's not the default group
+    if (ap->group != AUGROUP_DEFAULT) {
+      if (name == NULL) {
+        msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
+      } else {
+        msg_puts_attr(name, HL_ATTR(HLF_T));
+      }
+      msg_puts("  ");
+    }
+    // show the event name
+    msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
+    msg_putchar('\n');
+    if (got_int) {
+      return;
+    }
+  }
+
   msg_col = 4;
   msg_outtrans(ap->pat);
 
@@ -180,53 +205,69 @@ static void aupat_show(AutoPat *ap)
   }
 }
 
-static void au_show_for_all_events(int group)
+static void au_show_for_all_events(int group, char_u *pat)
 {
   FOR_ALL_AUEVENTS(event) {
-    au_show_for_event(group, event);
+    au_show_for_event(group, event, pat);
   }
 }
 
-static void au_show_for_event(int group, event_T event)
+static void au_show_for_event(int group, event_T event, char_u *pat)
 {
   // Return early if there are no autocmds for this event
   if (au_event_is_empty(event)) {
     return;
   }
 
+  // always need to show group information before the first pattern for the event
   int previous_group = AUGROUP_ERROR;
-  FOR_ALL_AUPATS_IN_EVENT(event, ap) {
-    if (group != AUGROUP_ALL && group != ap->group) {
-      continue;
-    }
 
-    char *name = augroup_name(ap->group);
-
-    msg_putchar('\n');
-    // When switching groups, we need to show the new group information.
-    if (ap->group != previous_group) {
-      // show the group name, if it's not the default group
-      if (ap->group != AUGROUP_DEFAULT) {
-        if (name == NULL) {
-          msg_puts_attr(get_deleted_augroup(), HL_ATTR(HLF_E));
-        } else {
-          msg_puts_attr(name, HL_ATTR(HLF_T));
-        }
-        msg_puts("  ");
+  if (*pat == NUL) {
+    FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+      if (group == AUGROUP_ALL || ap->group == group) {
+        aupat_show(ap, event, previous_group);
+        previous_group = ap->group;
       }
-
-      // show the event name
-      msg_puts_attr(event_nr2name(event), HL_ATTR(HLF_T));
-      msg_putchar('\n');
     }
+    return;
+  }
 
-    if (got_int) {
-      return;
+  char_u buflocal_pat[BUFLOCAL_PAT_LEN];  // for ""
+  // Loop through all the specified patterns.
+  int patlen = (int)aucmd_pattern_length(pat);
+  while (patlen) {
+    // detect special  buffer-local patterns
+    if (aupat_is_buflocal(pat, patlen)) {
+      // normalize pat into standard "#N" form
+      aupat_normalize_buflocal_pat(buflocal_pat, pat, patlen, aupat_get_buflocal_nr(pat, patlen));
+      pat = buflocal_pat;
+      patlen = (int)STRLEN(buflocal_pat);
     }
 
-    aupat_show(ap);
+    assert(*pat != NUL);
 
-    previous_group = ap->group;
+    // Find AutoPat entries with this pattern.
+    // always goes at or after the last one, so start at the end.
+    FOR_ALL_AUPATS_IN_EVENT(event, ap) {
+      if (ap->pat != NULL) {
+        // Accept a pattern when:
+        // - a group was specified and it's that group
+        // - the length of the pattern matches
+        // - the pattern matches.
+        // For , this condition works because we normalize
+        // all buffer-local patterns.
+        if ((group == AUGROUP_ALL || ap->group == group)
+            && ap->patlen == patlen
+            && STRNCMP(pat, ap->pat, patlen) == 0) {
+          // Show autocmd's for this autopat, or buflocals 
+          aupat_show(ap, event, previous_group);
+          previous_group = ap->group;
+        }
+      }
+    }
+
+    pat = aucmd_next_pattern(pat, (size_t)patlen);
+    patlen = (int)aucmd_pattern_length(pat);
   }
 }
 
@@ -805,11 +846,11 @@ void do_autocmd(char_u *arg_in, int forceit)
     msg_puts_title(_("\n--- Autocommands ---"));
 
     if (*arg == '*' || *arg == '|' || *arg == NUL) {
-      au_show_for_all_events(group);
+      au_show_for_all_events(group, pat);
     } else {
       event_T event = event_name2nr(arg, &arg);
       assert(event < NUM_EVENTS);
-      au_show_for_event(group, event);
+      au_show_for_event(group, event, pat);
     }
   } else {
     if (*arg == '*' || *arg == NUL || *arg == '|') {
@@ -900,7 +941,6 @@ int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, char_u *
       assert(*pat != NUL);
 
       // Find AutoPat entries with this pattern.
-      // always goes at or after the last one, so start at the end.
       prev_ap = &first_autopat[(int)event];
       while ((ap = *prev_ap) != NULL) {
         if (ap->pat != NULL) {
@@ -980,6 +1020,7 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro
     patlen = (int)STRLEN(buflocal_pat);
   }
 
+  // always goes at or after the last one, so start at the end.
   if (last_autopat[(int)event] != NULL) {
     prev_ap = &last_autopat[(int)event];
   } else {
-- 
cgit 


From 233014f92b5d4d5bf8a6f019241aafd1b05dd383 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Wed, 6 Apr 2022 12:04:19 +0800
Subject: vim-patch:8.2.0836: not all :cdo output is visible (#18007)

Problem:    Not all :cdo output is visible.
Solution:   Reset 'shortmess' temporarily. (Yegappan Lakshmanan, closes vim/vim#6155)
https://github.com/vim/vim/commit/14798ab9a5ee4b94f6c12f1986207569356acfc8

Cherry pick relevant changes form patches 8.1.1826 and 8.2.0557.
---
 src/nvim/ex_cmds2.c           |  6 ++++++
 src/nvim/testdir/test_cdo.vim | 45 +++++++++++++++++++++++++++----------------
 2 files changed, 34 insertions(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index fe1dec7298..ac1d760bce 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -1470,7 +1470,13 @@ void ex_listdo(exarg_T *eap)
 
         size_t qf_idx = qf_get_cur_idx(eap);
 
+        // Clear 'shm' to avoid that the file message overwrites
+        // any output from the command.
+        p_shm_save = vim_strsave(p_shm);
+        set_option_value("shm", 0L, "", 0);
         ex_cnext(eap);
+        set_option_value("shm", 0L, (char *)p_shm_save, 0);
+        xfree(p_shm_save);
 
         // If jumping to the next quickfix entry fails, quit here.
         if (qf_get_cur_idx(eap) == qf_idx) {
diff --git a/src/nvim/testdir/test_cdo.vim b/src/nvim/testdir/test_cdo.vim
index aa2e4f1a8c..dbed7df4ac 100644
--- a/src/nvim/testdir/test_cdo.vim
+++ b/src/nvim/testdir/test_cdo.vim
@@ -1,30 +1,29 @@
 " Tests for the :cdo, :cfdo, :ldo and :lfdo commands
 
-if !has('quickfix')
-  throw 'Skipped: quickfix feature missing'
-endif
+source check.vim
+CheckFeature quickfix
 
 " Create the files used by the tests
-function SetUp()
+func SetUp()
   call writefile(["Line1", "Line2", "Line3"], 'Xtestfile1')
   call writefile(["Line1", "Line2", "Line3"], 'Xtestfile2')
   call writefile(["Line1", "Line2", "Line3"], 'Xtestfile3')
-endfunction
+endfunc
 
 " Remove the files used by the tests
-function TearDown()
+func TearDown()
   call delete('Xtestfile1')
   call delete('Xtestfile2')
   call delete('Xtestfile3')
-endfunction
+endfunc
 
 " Returns the current line in ' L C' format
-function GetRuler()
+func GetRuler()
   return expand('%') . ' ' . line('.') . 'L' . ' ' . col('.') . 'C'
-endfunction
+endfunc
 
 " Tests for the :cdo and :ldo commands
-function XdoTests(cchar)
+func XdoTests(cchar)
   enew
 
   " Shortcuts for calling the cdo and ldo commands
@@ -133,10 +132,10 @@ function XdoTests(cchar)
   exe XdoCmd
   call assert_equal(['Xtestfile3 3L 1C'], l)
 
-endfunction
+endfunc
 
 " Tests for the :cfdo and :lfdo commands
-function XfdoTests(cchar)
+func XfdoTests(cchar)
   enew
 
   " Shortcuts for calling the cfdo and lfdo commands
@@ -190,16 +189,28 @@ function XfdoTests(cchar)
   exe XfdoCmd
   call assert_equal(['Xtestfile2 2L 5C'], l)
 
-endfunction
+endfunc
 
 " Tests for cdo and cfdo
-function Test_cdo()
+func Test_cdo()
   call XdoTests('c')
   call XfdoTests('c')
-endfunction
+endfunc
 
 " Tests for ldo and lfdo
-function Test_ldo()
+func Test_ldo()
   call XdoTests('l')
   call XfdoTests('l')
-endfunction
+endfunc
+
+" Test for making 'shm' doesn't interfere with the output.
+func Test_cdo_print()
+  enew | only!
+  cgetexpr ["Xtestfile1:1:Line1", "Xtestfile2:1:Line1", "Xtestfile3:1:Line1"]
+  cdo print
+  call assert_equal('Line1', Screenline(&lines))
+  call assert_equal('Line1', Screenline(&lines - 3))
+  call assert_equal('Line1', Screenline(&lines - 6))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From f85f4e25d2d5edf46fcb950ced028685fe95a7d7 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 7 Apr 2022 09:09:08 +0200
Subject: vim-patch:8.2.4701: Kuka Robot Language files not recognized (#18012)

Problem:    Kuka Robot Language files not recognized.
Solution:   Recognize *.src and *.dat files. (Patrick Meiser-Knosowski,
            closes vim/vim#10096)
https://github.com/vim/vim/commit/3ad2090316edc85e93094bba7af64f9991cc7f85
---
 src/nvim/testdir/test_filetype.vim | 49 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

(limited to 'src')

diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 720c003ace..a31ff50a48 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -290,6 +290,7 @@ let s:filename_checks = {
     \ 'kivy': ['file.kv'],
     \ 'kix': ['file.kix'],
     \ 'kotlin': ['file.kt', 'file.ktm', 'file.kts'],
+    \ 'krl': ['file.sub', 'file.Sub', 'file.SUB'],
     \ 'kscript': ['file.ks'],
     \ 'kwt': ['file.k'],
     \ 'lace': ['file.ace', 'file.ACE'],
@@ -842,6 +843,30 @@ func Test_d_file()
   filetype off
 endfunc
 
+func Test_dat_file()
+  filetype on
+
+  call writefile(['&ACCESS'], 'datfile.dat')
+  split datfile.dat
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('datfile.dat')
+
+  call writefile(['  DEFDAT datfile'], 'datfile.Dat')
+  split datfile.Dat
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('datfile.Dat')
+
+  call writefile(['', 'defdat  datfile'], 'datfile.DAT')
+  split datfile.DAT
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('datfile.DAT')
+
+  filetype off
+endfunc
+
 func Test_dep3patch_file()
   filetype on
 
@@ -1284,6 +1309,30 @@ func Test_pp_file()
   filetype off
 endfunc
 
+func Test_src_file()
+  filetype on
+
+  call writefile(['&ACCESS'], 'srcfile.src')
+  split srcfile.src
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('srcfile.src')
+
+  call writefile(['  DEF srcfile()'], 'srcfile.Src')
+  split srcfile.Src
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('srcfile.Src')
+
+  call writefile(['', 'global  def  srcfile()'], 'srcfile.SRC')
+  split srcfile.SRC
+  call assert_equal('krl', &filetype)
+  bwipe!
+  call delete('srcfile.SRC')
+
+  filetype off
+endfunc
+
 func Test_tex_file()
   filetype on
 
-- 
cgit 


From 2a574f7aaaf5cd0803faa9e4337bf3e21e8b8d2a Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 7 Apr 2022 21:13:09 +0800
Subject: fix(input): fix clearing of reg_executing

vim-patch:8.2.4705
---
 src/nvim/ex_docmd.c                 |  4 ++++
 src/nvim/ex_docmd.h                 |  1 +
 src/nvim/getchar.c                  | 21 +++++++++++++++++----
 src/nvim/globals.h                  |  2 ++
 src/nvim/testdir/test_registers.vim | 18 ++++++++++++++++++
 5 files changed, 42 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index bb6f3ba395..1f17101aca 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1248,6 +1248,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe
   const int save_msg_scroll = msg_scroll;
   cmdmod_T save_cmdmod;
   const int save_reg_executing = reg_executing;
+  const bool save_pending_end_reg_executing = pending_end_reg_executing;
   char_u *cmd;
 
   memset(&ea, 0, sizeof(ea));
@@ -2018,6 +2019,7 @@ doend:
   undo_cmdmod(&ea, save_msg_scroll);
   cmdmod = save_cmdmod;
   reg_executing = save_reg_executing;
+  pending_end_reg_executing = save_pending_end_reg_executing;
 
   if (ea.did_sandbox) {
     sandbox--;
@@ -8529,6 +8531,7 @@ bool save_current_state(save_state_T *sst)
   sst->save_finish_op = finish_op;
   sst->save_opcount = opcount;
   sst->save_reg_executing = reg_executing;
+  sst->save_pending_end_reg_executing = pending_end_reg_executing;
 
   msg_scroll = false;   // no msg scrolling in Normal mode
   restart_edit = 0;     // don't go to Insert mode
@@ -8559,6 +8562,7 @@ void restore_current_state(save_state_T *sst)
   finish_op = sst->save_finish_op;
   opcount = sst->save_opcount;
   reg_executing = sst->save_reg_executing;
+  pending_end_reg_executing = sst->save_pending_end_reg_executing;
 
   // don't reset msg_didout now
   msg_didout |= sst->save_msg_didout;
diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h
index be9f97e27d..874e0e599e 100644
--- a/src/nvim/ex_docmd.h
+++ b/src/nvim/ex_docmd.h
@@ -29,6 +29,7 @@ typedef struct {
   bool save_finish_op;
   long save_opcount;
   int save_reg_executing;
+  bool save_pending_end_reg_executing;
   tasave_T tabuf;
 } save_state_T;
 
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index eddd5ccd14..12c08baead 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2060,7 +2060,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
 }
 
 /// unget one character (can only be done once!)
-/// If the character was stuffed, vgetc() will get it next time it was called.
+/// If the character was stuffed, vgetc() will get it next time it is called.
 /// Otherwise vgetc() will only get it when the stuff buffer is empty.
 void vungetc(int c)
 {
@@ -2072,6 +2072,20 @@ void vungetc(int c)
   old_KeyStuffed = KeyStuffed;
 }
 
+/// When peeking and not getting a character, reg_executing cannot be cleared
+/// yet, so set a flag to clear it later.
+static void check_end_reg_executing(bool advance)
+{
+  if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) {
+    if (advance) {
+      reg_executing = 0;
+      pending_end_reg_executing = false;
+    } else {
+      pending_end_reg_executing = true;
+    }
+  }
+}
+
 /// Gets a byte:
 /// 1. from the stuffbuffer
 ///    This is used for abbreviated commands like "D" -> "d$".
@@ -2126,9 +2140,7 @@ static int vgetorpeek(bool advance)
 
   init_typebuf();
   start_stuff();
-  if (advance && typebuf.tb_maplen == 0) {
-    reg_executing = 0;
-  }
+  check_end_reg_executing(advance);
   do {
     // get a character: 1. from the stuffbuffer
     if (typeahead_char != 0) {
@@ -2155,6 +2167,7 @@ static int vgetorpeek(bool advance)
       // If a mapped key sequence is found we go back to the start to
       // try re-mapping.
       for (;;) {
+        check_end_reg_executing(advance);
         // os_breakcheck() is slow, don't use it too often when
         // inside a mapping.  But call it each time for typed
         // characters.
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index ace7647b35..4aa49337cf 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -643,6 +643,8 @@ EXTERN bool ex_no_reprint INIT(=false);   // No need to print after z or p.
 
 EXTERN int reg_recording INIT(= 0);     // register for recording  or zero
 EXTERN int reg_executing INIT(= 0);     // register being executed or zero
+// Flag set when peeking a character and found the end of executed register
+EXTERN bool pending_end_reg_executing INIT(= false);
 EXTERN int reg_recorded INIT(= 0);      // last recorded register or zero
 
 EXTERN int no_mapping INIT(= false);    // currently no mapping allowed
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index f78b748d71..c623edd5a1 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -690,5 +690,23 @@ func Test_record_in_select_mode()
   bwipe!
 endfunc
 
+func Test_end_reg_executing()
+  nnoremap s 
+  let @a = 's'
+  call feedkeys("@aqaq\", 'tx')
+  call assert_equal('', @a)
+  call assert_equal('', getline(1))
+
+  call setline(1, 'aaa')
+  nnoremap s qa
+  let @a = 'fa'
+  call feedkeys("@asq\", 'tx')
+  call assert_equal('', @a)
+  call assert_equal('aaa', getline(1))
+
+  nunmap s
+  bwipe!
+endfunc
+
 
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 64802da6c4304a2700d9471d17c0aae143d9aab1 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 7 Apr 2022 14:27:47 +0800
Subject: fix(event-loop): check if executed register has ended

---
 src/nvim/getchar.c | 2 +-
 src/nvim/state.c   | 5 ++++-
 2 files changed, 5 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 12c08baead..299385cb17 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2074,7 +2074,7 @@ void vungetc(int c)
 
 /// When peeking and not getting a character, reg_executing cannot be cleared
 /// yet, so set a flag to clear it later.
-static void check_end_reg_executing(bool advance)
+void check_end_reg_executing(bool advance)
 {
   if (reg_executing != 0 && (typebuf.tb_maplen == 0 || pending_end_reg_executing)) {
     if (advance) {
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 3a7636085b..34e3ddf654 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -40,7 +40,7 @@ void state_enter(VimState *s)
     int key;
 
 getkey:
-    // Expand mappings first by calling vpeekc() directly.
+    // Apply mappings first by calling vpeekc() directly.
     // - If vpeekc() returns non-NUL, there is a character already available for processing, so
     //   don't block for events. vgetc() may still block, in case of an incomplete UTF-8 sequence.
     // - If vpeekc() returns NUL, vgetc() will block, and there are three cases:
@@ -76,6 +76,9 @@ getkey:
     }
 
     if (key == K_EVENT) {
+      // An event handler may use the value of reg_executing.
+      // Clear it if it should be cleared when getting the next character.
+      check_end_reg_executing(true);
       may_sync_undo();
     }
 
-- 
cgit 


From 1edca3872e7c80a5396b84ffddb879cc18633d56 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 7 Apr 2022 23:26:03 +0800
Subject: vim-patch:8.2.4707: redrawing could be a bit more efficient (#18022)

Problem:    Redrawing could be a bit more efficient.
Solution:   Optimize redrawing. (closes vim/vim#10105)
https://github.com/vim/vim/commit/8c9796085071950f9a03ca0fe116608e4f86aac2
---
 src/nvim/change.c                   |  6 ++++--
 src/nvim/edit.c                     | 41 ++++++++++++++++++-------------------
 src/nvim/testdir/test_highlight.vim |  8 ++++++++
 3 files changed, 32 insertions(+), 23 deletions(-)

(limited to 'src')

diff --git a/src/nvim/change.c b/src/nvim/change.c
index 0644b1d601..44abd69733 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -286,9 +286,11 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra
         set_topline(wp, wp->w_topline);
       }
 
-      // Relative numbering may require updating more.
+      // If lines have been added or removed, relative numbering always
+      // requires a redraw.
       if (wp->w_p_rnu && xtra != 0) {
-        redraw_later(wp, SOME_VALID);
+        wp->w_last_cursor_lnum_rnu = 0;
+        redraw_later(wp, VALID);
       }
 
       // Cursor line highlighting probably need to be updated with
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index da0b577056..3ab176b2a3 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -390,12 +390,9 @@ static void insert_enter(InsertState *s)
   trigger_modechanged();
   stop_insert_mode = false;
 
-  // Need to recompute the cursor position, it might move when the cursor
-  // is on a TAB or special character.
-  // ptr2cells() treats a TAB character as double-width.
-  if (ptr2cells(get_cursor_pos_ptr()) > 1) {
-    curwin->w_valid &= ~VALID_VIRTCOL;
-    curs_columns(curwin, true);
+  // need to position cursor again when on a TAB
+  if (gchar_cursor() == TAB) {
+    curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
   }
 
   // Enable langmap or IME, indicated by 'iminsert'.
@@ -7330,21 +7327,21 @@ static void mb_replace_pop_ins(int cc)
       // Not a multi-byte char, put it back.
       replace_push(c);
       break;
+    }
+
+    buf[0] = c;
+    assert(n > 1);
+    for (i = 1; i < n; i++) {
+      buf[i] = replace_pop();
+    }
+    if (utf_iscomposing(utf_ptr2char(buf))) {
+      ins_bytes_len(buf, n);
     } else {
-      buf[0] = c;
-      assert(n > 1);
-      for (i = 1; i < n; i++) {
-        buf[i] = replace_pop();
-      }
-      if (utf_iscomposing(utf_ptr2char(buf))) {
-        ins_bytes_len(buf, n);
-      } else {
-        // Not a composing char, put it back.
-        for (i = n - 1; i >= 0; i--) {
-          replace_push(buf[i]);
-        }
-        break;
+      // Not a composing char, put it back.
+      for (i = n - 1; i >= 0; i--) {
+        replace_push(buf[i]);
       }
+      break;
     }
   }
 }
@@ -8054,8 +8051,10 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
 
   State = NORMAL;
   trigger_modechanged();
-  // need to position cursor again (e.g. when on a TAB )
-  changed_cline_bef_curs();
+  // need to position cursor again when on a TAB
+  if (gchar_cursor() == TAB) {
+    curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
+  }
 
   setmouse();
   ui_cursor_shape();            // may show different cursor shape
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index 5588e515e1..efdf44a0d6 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -615,6 +615,14 @@ func Test_cursorcolumn_insert_on_tab()
   call TermWait(buf)
   call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
 
+  call term_sendkeys(buf, "\")
+  call TermWait(buf)
+  call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_3', {})
+
+  call term_sendkeys(buf, 'i')
+  call TermWait(buf)
+  call VerifyScreenDump(buf, 'Test_cursorcolumn_insert_on_tab_2', {})
+
   call StopVimInTerminal(buf)
   call delete('Xcuc_insert_on_tab')
 endfunc
-- 
cgit 


From 8c25dbff468a6360ac8e22ba1e3fec659db16ab8 Mon Sep 17 00:00:00 2001
From: Christian Clason 
Date: Thu, 7 Apr 2022 20:24:55 +0200
Subject: vim-patch:8.2.4708: PHP test files are not recognized (#18025)

Problem:    PHP test files are not recognized.
Solution:   Add the *.phpt pattern. (Julien Voisin, closes vim/vim#10112)
https://github.com/vim/vim/commit/177847e67a495f80a15b6dfd0a3fcd151b44249e
---
 src/nvim/testdir/test_filetype.vim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index a31ff50a48..42271a014d 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -395,7 +395,7 @@ let s:filename_checks = {
     \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'],
     \ 'pf': ['pf.conf'],
     \ 'pfmain': ['main.cf'],
-    \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'],
+    \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp', 'file.phpt'],
     \ 'lpc': ['file.lpc', 'file.ulpc'],
     \ 'pike': ['file.pike', 'file.pmod'],
     \ 'cmod': ['file.cmod'],
-- 
cgit 


From 65a5cea0d32a006c996cce08b4a25716e0836c57 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 07:12:47 +0800
Subject: vim-patch:8.2.4710: smart indenting does not work after completion
 (#18030)

Problem:    Smart indenting does not work after completion.
Solution:   Set "can_si". (Christian Brabandt, closes vim/vim#10113, closes vim/vim#558)
https://github.com/vim/vim/commit/ac72c21da696cf6c31630a9e5ff4c0d3e2049c11
---
 src/nvim/edit.c                        |  1 +
 src/nvim/testdir/test_ins_complete.vim | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+)

(limited to 'src')

diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 3ab176b2a3..3eb4ab9517 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1393,6 +1393,7 @@ static void insert_do_complete(InsertState *s)
     compl_cont_status = 0;
   }
   compl_busy = false;
+  can_si = true;  // allow smartindenting
 }
 
 static void insert_do_cindent(InsertState *s)
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 186fa8871f..24eaf9e8b1 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -707,4 +707,23 @@ func Test_z1_complete_no_history()
   close!
 endfunc
 
+func FooBarComplete(findstart, base)
+  if a:findstart
+    return col('.') - 1
+  else
+    return ["Foo", "Bar", "}"]
+  endif
+endfunc
+
+func Test_complete_smartindent()
+  new
+  setlocal smartindent completefunc=FooBarComplete
+
+  exe "norm! o{\\\\}\\"
+  let result = getline(1,'$')
+  call assert_equal(['', '{','}',''], result)
+  bw!
+  delfunction! FooBarComplete
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 8f3245dbfa91d384215c78da32e4d1739c8a1f34 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 7 Apr 2022 21:58:35 +0800
Subject: refactor(window): cherry-pick win_close_buffer() from Vim patch
 8.1.1391

---
 src/nvim/window.c | 66 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 36 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/nvim/window.c b/src/nvim/window.c
index cc21bf25b0..e471c0b304 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2541,6 +2541,41 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev
   return true;
 }
 
+/// Close the buffer of "win" and unload it if "free_buf" is true.
+/// "abort_if_last" is passed to close_buffer(): abort closing if all other
+/// windows are closed.
+static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
+{
+  // Free independent synblock before the buffer is freed.
+  if (win->w_buffer != NULL) {
+    reset_synblock(win);
+  }
+
+  // When a quickfix/location list window is closed and the buffer is
+  // displayed in only one window, then unlist the buffer.
+  if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
+      && win->w_buffer->b_nwindows == 1) {
+    win->w_buffer->b_p_bl = false;
+  }
+
+  // Close the link to the buffer.
+  if (win->w_buffer != NULL) {
+    bufref_T bufref;
+    set_bufref(&bufref, curbuf);
+    win->w_closing = true;
+    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last);
+    if (win_valid_any_tab(win)) {
+      win->w_closing = false;
+    }
+
+    // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
+    // "wipe".
+    if (!bufref_valid(&bufref)) {
+      curbuf = firstbuf;
+    }
+  }
+}
+
 // Close window "win".  Only works for the current tab page.
 // If "free_buf" is true related buffer may be unloaded.
 //
@@ -2679,36 +2714,7 @@ int win_close(win_T *win, bool free_buf, bool force)
     return OK;
   }
 
-  // Free independent synblock before the buffer is freed.
-  if (win->w_buffer != NULL) {
-    reset_synblock(win);
-  }
-
-  // When a quickfix/location list window is closed and the buffer is
-  // displayed in only one window, then unlist the buffer.
-  if (win->w_buffer != NULL && bt_quickfix(win->w_buffer)
-      && win->w_buffer->b_nwindows == 1) {
-    win->w_buffer->b_p_bl = false;
-  }
-
-  /*
-   * Close the link to the buffer.
-   */
-  if (win->w_buffer != NULL) {
-    bufref_T bufref;
-    set_bufref(&bufref, curbuf);
-    win->w_closing = true;
-    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true);
-    if (win_valid_any_tab(win)) {
-      win->w_closing = false;
-    }
-
-    // Make sure curbuf is valid. It can become invalid if 'bufhidden' is
-    // "wipe".
-    if (!bufref_valid(&bufref)) {
-      curbuf = firstbuf;
-    }
-  }
+  win_close_buffer(win, free_buf, true);
 
   if (only_one_window() && win_valid(win) && win->w_buffer == NULL
       && (last_window(win) || curtab != prev_curtab
-- 
cgit 


From 44b59d1a696b35d2520dbea2de3aab01e740a7ca Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Thu, 7 Apr 2022 21:46:07 +0800
Subject: vim-patch:8.2.0004: get E685 and E931 if buffer reload is interrupted

Problem:    Get E685 and E931 if buffer reload is interrupted.
Solution:   Do not abort deleting a dummy buffer. (closes vim/vim#5361)
https://github.com/vim/vim/commit/a6e8f888e7fc31b8ab7233509254fb2e2fe4089f
---
 src/nvim/buffer.c                  | 39 ++++++++++++++++++++++----------------
 src/nvim/buffer.h                  |  7 ++++---
 src/nvim/ex_cmds.c                 |  6 +++---
 src/nvim/ex_getln.c                |  2 +-
 src/nvim/memory.c                  |  2 +-
 src/nvim/quickfix.c                |  4 ++--
 src/nvim/testdir/test_trycatch.vim | 27 ++++++++++++++++++++++++--
 src/nvim/window.c                  |  4 ++--
 8 files changed, 61 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index bf592a626d..4ec23244cd 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -174,7 +174,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
   if (ml_open(curbuf) == FAIL) {
     // There MUST be a memfile, otherwise we can't do anything
     // If we can't create one for the current buffer, take another buffer
-    close_buffer(NULL, curbuf, 0, false);
+    close_buffer(NULL, curbuf, 0, false, false);
 
     curbuf = NULL;
     FOR_ALL_BUFFERS(buf) {
@@ -402,8 +402,10 @@ bool buf_valid(buf_T *buf)
 ///               there to be only one window with this buffer. e.g. when
 ///               ":quit" is supposed to close the window but autocommands
 ///               close all other windows.
+/// @param ignore_abort
+///               If true, don't abort even when aborting() returns true.
 /// @return  true when we got to the end and b_nwindows was decremented.
-bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
+bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last, bool ignore_abort)
 {
   bool unload_buf = (action != 0);
   bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
@@ -494,7 +496,8 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
         return false;
       }
     }
-    if (aborting()) {       // autocmds may abort script processing
+    // autocmds may abort script processing
+    if (!ignore_abort && aborting()) {
       return false;
     }
   }
@@ -552,14 +555,16 @@ bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last)
 
   buf->b_nwindows = nwindows;
 
-  buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
+  buf_freeall(buf, ((del_buf ? BFA_DEL : 0)
+                    + (wipe_buf ? BFA_WIPE : 0)
+                    + (ignore_abort ? BFA_IGNORE_ABORT : 0)));
 
   if (!bufref_valid(&bufref)) {
     // Autocommands may have deleted the buffer.
     return false;
   }
-  if (aborting()) {
-    // Autocmds may abort script processing.
+  // autocmds may abort script processing.
+  if (!ignore_abort && aborting()) {
     return false;
   }
 
@@ -660,9 +665,10 @@ void buf_clear(void)
 /// buf_freeall() - free all things allocated for a buffer that are related to
 /// the file.  Careful: get here with "curwin" NULL when exiting.
 ///
-/// @param flags BFA_DEL buffer is going to be deleted
-///              BFA_WIPE buffer is going to be wiped out
-///              BFA_KEEP_UNDO  do not free undo information
+/// @param flags BFA_DEL           buffer is going to be deleted
+///              BFA_WIPE          buffer is going to be wiped out
+///              BFA_KEEP_UNDO     do not free undo information
+///              BFA_IGNORE_ABORT  don't abort even when aborting() returns true
 void buf_freeall(buf_T *buf, int flags)
 {
   bool is_curbuf = (buf == curbuf);
@@ -706,7 +712,8 @@ void buf_freeall(buf_T *buf, int flags)
     goto_tabpage_win(the_curtab, the_curwin);
     unblock_autocmds();
   }
-  if (aborting()) {  // autocmds may abort script processing
+  // autocmds may abort script processing
+  if ((flags & BFA_IGNORE_ABORT) == 0 && aborting()) {
     return;
   }
 
@@ -877,7 +884,7 @@ void handle_swap_exists(bufref_T *old_curbuf)
     // open a new, empty buffer.
     swap_exists_action = SEA_NONE;      // don't want it again
     swap_exists_did_quit = true;
-    close_buffer(curwin, curbuf, DOBUF_UNLOAD, false);
+    close_buffer(curwin, curbuf, DOBUF_UNLOAD, false, false);
     if (old_curbuf == NULL
         || !bufref_valid(old_curbuf)
         || old_curbuf->br_buf == curbuf) {
@@ -1074,7 +1081,7 @@ static int empty_curbuf(int close_others, int forceit, int action)
   // the old one.  But do_ecmd() may have done that already, check
   // if the buffer still exists.
   if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0) {
-    close_buffer(NULL, buf, action, false);
+    close_buffer(NULL, buf, action, false, false);
   }
 
   if (!close_others) {
@@ -1259,7 +1266,7 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
     if (buf != curbuf) {
       close_windows(buf, false);
       if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0) {
-        close_buffer(NULL, buf, action, false);
+        close_buffer(NULL, buf, action, false, false);
       }
       return OK;
     }
@@ -1485,7 +1492,7 @@ void set_curbuf(buf_T *buf, int action)
                    ? action
                    : (action == DOBUF_GOTO && !buf_hide(prevbuf)
                       && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
-                   false);
+                   false, false);
       if (curwin != previouswin && win_valid(previouswin)) {
         // autocommands changed curwin, Grr!
         curwin = previouswin;
@@ -2805,7 +2812,7 @@ int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message)
         return FAIL;
       }
       // delete from the list
-      close_buffer(NULL, obuf, DOBUF_WIPE, false);
+      close_buffer(NULL, obuf, DOBUF_WIPE, false, false);
     }
     sfname = vim_strsave(sfname);
 #ifdef USE_FNAME_CASE
@@ -5650,7 +5657,7 @@ void wipe_buffer(buf_T *buf, bool aucmd)
     // Don't trigger BufDelete autocommands here.
     block_autocmds();
   }
-  close_buffer(NULL, buf, DOBUF_WIPE, false);
+  close_buffer(NULL, buf, DOBUF_WIPE, false, true);
   if (!aucmd) {
     unblock_autocmds();
   }
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index 850ab175a5..7f4bbcc9e5 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -58,9 +58,10 @@ enum dobuf_start_values {
 
 // flags for buf_freeall()
 enum bfa_values {
-  BFA_DEL       = 1,  // buffer is going to be deleted
-  BFA_WIPE      = 2,  // buffer is going to be wiped out
-  BFA_KEEP_UNDO = 4,  // do not free undo information
+  BFA_DEL          = 1,  // buffer is going to be deleted
+  BFA_WIPE         = 2,  // buffer is going to be wiped out
+  BFA_KEEP_UNDO    = 4,  // do not free undo information
+  BFA_IGNORE_ABORT = 8,  // do not abort for aborting()
 };
 
 #ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 5d5b342c35..4e98319723 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2527,9 +2527,9 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
         // Close the link to the current buffer. This will set
         // oldwin->w_buffer to NULL.
         u_sync(false);
-        const bool did_decrement = close_buffer(oldwin, curbuf,
-                                                (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
-                                                false);
+        const bool did_decrement
+          = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD,
+                         false, false);
 
         // Autocommands may have closed the window.
         if (win_valid(the_curwin)) {
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 9104a2dd5a..dd6e4630d2 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -6544,7 +6544,7 @@ static int open_cmdwin(void)
     // win_close() may have already wiped the buffer when 'bh' is
     // set to 'wipe', autocommands may have closed other windows
     if (bufref_valid(&bufref) && bufref.br_buf != curbuf) {
-      close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false);
+      close_buffer(NULL, bufref.br_buf, DOBUF_WIPE, false, false);
     }
 
     // Restore window sizes.
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 6cdc4f1fde..c895cc1ec6 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -691,7 +691,7 @@ void free_all_mem(void)
     bufref_T bufref;
     set_bufref(&bufref, buf);
     nextbuf = buf->b_next;
-    close_buffer(NULL, buf, DOBUF_WIPE, false);
+    close_buffer(NULL, buf, DOBUF_WIPE, false, false);
     // Didn't work, try next one.
     buf = bufref_valid(&bufref) ? nextbuf : firstbuf;
   }
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 1d589e8ca0..4ad5e40fee 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -1721,7 +1721,7 @@ static void wipe_qf_buffer(qf_info_T *qi)
     if (qfbuf != NULL && qfbuf->b_nwindows == 0) {
       // If the quickfix buffer is not loaded in any window, then
       // wipe the buffer.
-      close_buffer(NULL, qfbuf, DOBUF_WIPE, false);
+      close_buffer(NULL, qfbuf, DOBUF_WIPE, false, false);
       qi->qf_bufnr = INVALID_QFBUFNR;
     }
   }
@@ -5843,7 +5843,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
 static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start)
 {
   if (curbuf != buf) {          // safety check
-    close_buffer(NULL, buf, DOBUF_UNLOAD, false);
+    close_buffer(NULL, buf, DOBUF_UNLOAD, false, true);
 
     // When autocommands/'autochdir' option changed directory: go back.
     restore_start_dir(dirname_start);
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 7e513180a3..adc1745b39 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -1972,6 +1972,29 @@ func Test_builtin_func_error()
   call assert_equal('jlmnpqrtueghivyzACD', g:Xpath)
 endfunc
 
-" Modelines								    {{{1
+func Test_reload_in_try_catch()
+  call writefile(['x'], 'Xreload')
+  set autoread
+  edit Xreload
+  tabnew
+  call writefile(['xx'], 'Xreload')
+  augroup ReLoad
+    au FileReadPost Xreload let x = doesnotexist
+    au BufReadPost Xreload let x = doesnotexist
+  augroup END
+  try
+    edit Xreload
+  catch
+  endtry
+  tabnew
+
+  tabclose
+  tabclose
+  autocmd! ReLoad
+  set noautoread
+  bwipe! Xreload
+  call delete('Xreload')
+endfunc
+
+" Modeline								    {{{1
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
-"-------------------------------------------------------------------------------
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e471c0b304..20f2447bbe 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2563,7 +2563,7 @@ static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
     bufref_T bufref;
     set_bufref(&bufref, curbuf);
     win->w_closing = true;
-    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last);
+    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, false);
     if (win_valid_any_tab(win)) {
       win->w_closing = false;
     }
@@ -2885,7 +2885,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
 
   if (win->w_buffer != NULL) {
     // Close the link to the buffer.
-    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false);
+    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, false);
   }
 
   // Careful: Autocommands may have closed the tab page or made it the
-- 
cgit 


From b7bc931f631febeeffee528cc3b5667cfbf60a90 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 07:57:50 +0800
Subject: vim-patch:8.2.4700: buffer remains active if WinClosed event throws
 an exception

Problem:    Buffer remains active if a WinClosed event throws an exception.
Solution:   Ignore aborting() when closing the buffer. (closes vim/vim#10097)
https://github.com/vim/vim/commit/c947b9ae419114ebfef9725814ea41a466fcf879
---
 src/nvim/testdir/test_autocmd.vim | 17 +++++++++++++++++
 src/nvim/window.c                 |  2 +-
 2 files changed, 18 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 76c69ad10b..ce2ca1322e 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -299,6 +299,23 @@ func Test_WinClosed()
   unlet g:triggered
 endfunc
 
+func Test_WinClosed_throws()
+  vnew
+  let bnr = bufnr()
+  call assert_equal(1, bufloaded(bnr))
+  augroup test-WinClosed
+    autocmd WinClosed * throw 'foo'
+  augroup END
+  try
+    close
+  catch /.*/
+  endtry
+  call assert_equal(0, bufloaded(bnr))
+
+  autocmd! test-WinClosed
+  augroup! test-WinClosed
+endfunc
+
 func s:AddAnAutocmd()
   augroup vimBarTest
     au BufReadCmd * echo 'hello'
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 20f2447bbe..21350f1a38 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2563,7 +2563,7 @@ static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last)
     bufref_T bufref;
     set_bufref(&bufref, curbuf);
     win->w_closing = true;
-    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, false);
+    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, true);
     if (win_valid_any_tab(win)) {
       win->w_closing = false;
     }
-- 
cgit 


From 191f009ab74111bab6e60ffc1dac5484196b7a6b Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 07:58:34 +0800
Subject: vim-patch:8.2.4706: buffer remains active with WinClosed and tabpages

Problem:    Buffer remains active if a WinClosed event throws an exception
            when there are multiple tabpages.
Solution:   Ignore aborting() when closing the buffer. (closes vim/vim#10101)
https://github.com/vim/vim/commit/6a06940f8ae7283999c83ccdf268540220573105
---
 src/nvim/testdir/test_autocmd.vim | 17 +++++++++++++++++
 src/nvim/window.c                 |  2 +-
 2 files changed, 18 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index ce2ca1322e..c1a120efa4 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -316,6 +316,23 @@ func Test_WinClosed_throws()
   augroup! test-WinClosed
 endfunc
 
+func Test_WinClosed_throws_with_tabs()
+  tabnew
+  let bnr = bufnr()
+  call assert_equal(1, bufloaded(bnr))
+  augroup test-WinClosed
+    autocmd WinClosed * throw 'foo'
+  augroup END
+  try
+    close
+  catch /.*/
+  endtry
+  call assert_equal(0, bufloaded(bnr))
+
+  autocmd! test-WinClosed
+  augroup! test-WinClosed
+endfunc
+
 func s:AddAnAutocmd()
   augroup vimBarTest
     au BufReadCmd * echo 'hello'
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 21350f1a38..9aa2f947cb 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -2885,7 +2885,7 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
 
   if (win->w_buffer != NULL) {
     // Close the link to the buffer.
-    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, false);
+    close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true);
   }
 
   // Careful: Autocommands may have closed the tab page or made it the
-- 
cgit 


From 356baae80ad42e8a13d650675a0e56e931f08541 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 10:25:22 +0800
Subject: vim-patch:8.2.4704: using "else" after return or break increases
 indent (#18032)

Problem:    Using "else" after return or break increases indent.
Solution:   Remove "else" and reduce indent. (Goc Dundar, closes vim/vim#10099)
https://github.com/vim/vim/commit/f26c16144ddb27642c09f2cf5271afd163b36306
---
 src/nvim/fileio.c  |  38 ++++++-------
 src/nvim/memline.c | 159 +++++++++++++++++++++++++----------------------------
 src/nvim/option.c  |  66 +++++++++++-----------
 src/nvim/syntax.c  |  79 +++++++++++++-------------
 4 files changed, 166 insertions(+), 176 deletions(-)

(limited to 'src')

diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index d68dda15f3..d4407b4982 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1015,27 +1015,27 @@ retry:
                 }
                 read_buf_col += n;
                 break;
-              } else {
-                // Append whole line and new-line.  Change NL
-                // to NUL to reverse the effect done below.
-                for (ni = 0; ni < n; ni++) {
-                  if (p[ni] == NL) {
-                    ptr[tlen++] = NUL;
-                  } else {
-                    ptr[tlen++] = p[ni];
-                  }
+              }
+
+              // Append whole line and new-line.  Change NL
+              // to NUL to reverse the effect done below.
+              for (ni = 0; ni < n; ni++) {
+                if (p[ni] == NL) {
+                  ptr[tlen++] = NUL;
+                } else {
+                  ptr[tlen++] = p[ni];
                 }
-                ptr[tlen++] = NL;
-                read_buf_col = 0;
-                if (++read_buf_lnum > from) {
-                  // When the last line didn't have an
-                  // end-of-line don't add it now either.
-                  if (!curbuf->b_p_eol) {
-                    --tlen;
-                  }
-                  size = tlen;
-                  break;
+              }
+              ptr[tlen++] = NL;
+              read_buf_col = 0;
+              if (++read_buf_lnum > from) {
+                // When the last line didn't have an
+                // end-of-line don't add it now either.
+                if (!curbuf->b_p_eol) {
+                  tlen--;
                 }
+                size = tlen;
+                break;
               }
             }
           }
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index 418887a6b1..5f6e1ea273 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -2327,95 +2327,88 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
          * We are finished, break the loop here.
          */
         break;
-      } else {                        // pointer block full
-        /*
-         * split the pointer block
-         * allocate a new pointer block
-         * move some of the pointer into the new block
-         * prepare for updating the parent block
-         */
-        for (;;) {             // do this twice when splitting block 1
-          hp_new = ml_new_ptr(mfp);
-          if (hp_new == NULL) {             // TODO: try to fix tree
-            return FAIL;
-          }
-          pp_new = hp_new->bh_data;
-
-          if (hp->bh_bnum != 1) {
-            break;
-          }
-
-          /*
-           * if block 1 becomes full the tree is given an extra level
-           * The pointers from block 1 are moved into the new block.
-           * block 1 is updated to point to the new block
-           * then continue to split the new block
-           */
-          memmove(pp_new, pp, (size_t)page_size);
-          pp->pb_count = 1;
-          pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
-          pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
-          pp->pb_pointer[0].pe_old_lnum = 1;
-          pp->pb_pointer[0].pe_page_count = 1;
-          mf_put(mfp, hp, true, false);             // release block 1
-          hp = hp_new;                          // new block is to be split
-          pp = pp_new;
-          CHECK(stack_idx != 0, _("stack_idx should be 0"));
-          ip->ip_index = 0;
-          ++stack_idx;                  // do block 1 again later
-        }
-        /*
-         * move the pointers after the current one to the new block
-         * If there are none, the new entry will be in the new block.
-         */
-        total_moved = pp->pb_count - pb_idx - 1;
-        if (total_moved) {
-          memmove(&pp_new->pb_pointer[0],
-                  &pp->pb_pointer[pb_idx + 1],
-                  (size_t)(total_moved) * sizeof(PTR_EN));
-          pp_new->pb_count = total_moved;
-          pp->pb_count -= total_moved - 1;
-          pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
-          pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
-          pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
-          if (lnum_right) {
-            pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
-          }
-        } else {
-          pp_new->pb_count = 1;
-          pp_new->pb_pointer[0].pe_bnum = bnum_right;
-          pp_new->pb_pointer[0].pe_line_count = line_count_right;
-          pp_new->pb_pointer[0].pe_page_count = page_count_right;
-          pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
-        }
-        pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
-        pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
-        pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
-        if (lnum_left) {
-          pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+      }
+      // pointer block full
+      //
+      // split the pointer block
+      // allocate a new pointer block
+      // move some of the pointer into the new block
+      // prepare for updating the parent block
+      for (;;) {             // do this twice when splitting block 1
+        hp_new = ml_new_ptr(mfp);
+        if (hp_new == NULL) {             // TODO(vim): try to fix tree
+          return FAIL;
         }
-        lnum_left = 0;
-        lnum_right = 0;
+        pp_new = hp_new->bh_data;
 
-        /*
-         * recompute line counts
-         */
-        line_count_right = 0;
-        for (i = 0; i < (int)pp_new->pb_count; ++i) {
-          line_count_right += pp_new->pb_pointer[i].pe_line_count;
+        if (hp->bh_bnum != 1) {
+          break;
         }
-        line_count_left = 0;
-        for (i = 0; i < (int)pp->pb_count; ++i) {
-          line_count_left += pp->pb_pointer[i].pe_line_count;
+
+        // if block 1 becomes full the tree is given an extra level
+        // The pointers from block 1 are moved into the new block.
+        // block 1 is updated to point to the new block
+        // then continue to split the new block
+        memmove(pp_new, pp, (size_t)page_size);
+        pp->pb_count = 1;
+        pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
+        pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
+        pp->pb_pointer[0].pe_old_lnum = 1;
+        pp->pb_pointer[0].pe_page_count = 1;
+        mf_put(mfp, hp, true, false);             // release block 1
+        hp = hp_new;                          // new block is to be split
+        pp = pp_new;
+        CHECK(stack_idx != 0, _("stack_idx should be 0"));
+        ip->ip_index = 0;
+        stack_idx++;                  // do block 1 again later
+      }
+      // move the pointers after the current one to the new block
+      // If there are none, the new entry will be in the new block.
+      total_moved = pp->pb_count - pb_idx - 1;
+      if (total_moved) {
+        memmove(&pp_new->pb_pointer[0],
+                &pp->pb_pointer[pb_idx + 1],
+                (size_t)(total_moved) * sizeof(PTR_EN));
+        pp_new->pb_count = total_moved;
+        pp->pb_count -= total_moved - 1;
+        pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
+        pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
+        pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
+        if (lnum_right) {
+          pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
         }
+      } else {
+        pp_new->pb_count = 1;
+        pp_new->pb_pointer[0].pe_bnum = bnum_right;
+        pp_new->pb_pointer[0].pe_line_count = line_count_right;
+        pp_new->pb_pointer[0].pe_page_count = page_count_right;
+        pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
+      }
+      pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
+      pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
+      pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
+      if (lnum_left) {
+        pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
+      }
+      lnum_left = 0;
+      lnum_right = 0;
 
-        bnum_left = hp->bh_bnum;
-        bnum_right = hp_new->bh_bnum;
-        page_count_left = 1;
-        page_count_right = 1;
-        mf_put(mfp, hp, true, false);
-        mf_put(mfp, hp_new, true, false);
+      // recompute line counts
+      line_count_right = 0;
+      for (i = 0; i < (int)pp_new->pb_count; i++) {
+        line_count_right += pp_new->pb_pointer[i].pe_line_count;
       }
+      line_count_left = 0;
+      for (i = 0; i < (int)pp->pb_count; i++) {
+        line_count_left += pp->pb_pointer[i].pe_line_count;
+      }
+
+      bnum_left = hp->bh_bnum;
+      bnum_right = hp_new->bh_bnum;
+      page_count_left = 1;
+      page_count_right = 1;
+      mf_put(mfp, hp, true, false);
+      mf_put(mfp, hp_new, true, false);
     }
 
     /*
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 9fd0e0b222..551bc1fbdf 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5140,43 +5140,43 @@ char *set_option_value(const char *const name, const long number, const char *co
         s = "";
       }
       return set_string_option(opt_idx, s, opt_flags);
-    } else {
-      varp = get_varp_scope(&(options[opt_idx]), opt_flags);
-      if (varp != NULL) {       // hidden option is not changed
-        if (number == 0 && string != NULL) {
-          int idx;
-
-          // Either we are given a string or we are setting option
-          // to zero.
-          for (idx = 0; string[idx] == '0'; idx++) {}
-          if (string[idx] != NUL || idx == 0) {
-            // There's another character after zeros or the string
-            // is empty.  In both cases, we are trying to set a
-            // num option using a string.
-            semsg(_("E521: Number required: &%s = '%s'"),
-                  name, string);
-            return NULL;  // do nothing as we hit an error
-          }
-        }
-        long numval = number;
-        if (opt_flags & OPT_CLEAR) {
-          if ((int *)varp == &curbuf->b_p_ar) {
-            numval = -1;
-          } else if ((long *)varp == &curbuf->b_p_ul) {
-            numval = NO_LOCAL_UNDOLEVEL;
-          } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
-            numval = -1;
-          } else {
-            char *s = NULL;
-            (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
-          }
+    }
+
+    varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+    if (varp != NULL) {       // hidden option is not changed
+      if (number == 0 && string != NULL) {
+        int idx;
+
+        // Either we are given a string or we are setting option
+        // to zero.
+        for (idx = 0; string[idx] == '0'; idx++) {}
+        if (string[idx] != NUL || idx == 0) {
+          // There's another character after zeros or the string
+          // is empty.  In both cases, we are trying to set a
+          // num option using a string.
+          semsg(_("E521: Number required: &%s = '%s'"),
+                name, string);
+          return NULL;  // do nothing as we hit an error
         }
-        if (flags & P_NUM) {
-          return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+      }
+      long numval = number;
+      if (opt_flags & OPT_CLEAR) {
+        if ((int *)varp == &curbuf->b_p_ar) {
+          numval = -1;
+        } else if ((long *)varp == &curbuf->b_p_ul) {
+          numval = NO_LOCAL_UNDOLEVEL;
+        } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
+          numval = -1;
         } else {
-          return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+          char *s = NULL;
+          (void)get_option_value(name, &numval, (char_u **)&s, OPT_GLOBAL);
         }
       }
+      if (flags & P_NUM) {
+        return set_num_option(opt_idx, varp, numval, NULL, 0, opt_flags);
+      } else {
+        return set_bool_option(opt_idx, varp, (int)numval, opt_flags);
+      }
     }
   }
   return NULL;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index a82e8fa103..d884ad704b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -2289,55 +2289,52 @@ static void check_state_ends(void)
         next_match_idx = 0;
         next_match_col = MAXCOL;
         break;
-      } else {
-        // handle next_list, unless at end of line and no "skipnl" or
-        // "skipempty"
-        current_next_list = cur_si->si_next_list;
-        current_next_flags = cur_si->si_flags;
-        if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
-            && syn_getcurline()[current_col] == NUL) {
-          current_next_list = NULL;
-        }
+      }
+
+      // handle next_list, unless at end of line and no "skipnl" or
+      // "skipempty"
+      current_next_list = cur_si->si_next_list;
+      current_next_flags = cur_si->si_flags;
+      if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
+          && syn_getcurline()[current_col] == NUL) {
+        current_next_list = NULL;
+      }
+
+      // When the ended item has "extend", another item with
+      // "keepend" now needs to check for its end.
+      had_extend = (cur_si->si_flags & HL_EXTEND);
 
-        // When the ended item has "extend", another item with
-        // "keepend" now needs to check for its end.
-        had_extend = (cur_si->si_flags & HL_EXTEND);
+      pop_current_state();
 
-        pop_current_state();
+      if (GA_EMPTY(¤t_state)) {
+        break;
+      }
 
+      if (had_extend && keepend_level >= 0) {
+        syn_update_ends(false);
         if (GA_EMPTY(¤t_state)) {
           break;
         }
+      }
 
-        if (had_extend && keepend_level >= 0) {
-          syn_update_ends(false);
-          if (GA_EMPTY(¤t_state)) {
-            break;
-          }
-        }
-
-        cur_si = &CUR_STATE(current_state.ga_len - 1);
+      cur_si = &CUR_STATE(current_state.ga_len - 1);
 
-        /*
-         * Only for a region the search for the end continues after
-         * the end of the contained item.  If the contained match
-         * included the end-of-line, break here, the region continues.
-         * Don't do this when:
-         * - "keepend" is used for the contained item
-         * - not at the end of the line (could be end="x$"me=e-1).
-         * - "excludenl" is used (HL_HAS_EOL won't be set)
-         */
-        if (cur_si->si_idx >= 0
-            && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
-            == SPTYPE_START
-            && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
-          update_si_end(cur_si, (int)current_col, true);
-          check_keepend();
-          if ((current_next_flags & HL_HAS_EOL)
-              && keepend_level < 0
-              && syn_getcurline()[current_col] == NUL) {
-            break;
-          }
+      // Only for a region the search for the end continues after
+      // the end of the contained item.  If the contained match
+      // included the end-of-line, break here, the region continues.
+      // Don't do this when:
+      // - "keepend" is used for the contained item
+      // - not at the end of the line (could be end="x$"me=e-1).
+      // - "excludenl" is used (HL_HAS_EOL won't be set)
+      if (cur_si->si_idx >= 0
+          && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type == SPTYPE_START
+          && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND))) {
+        update_si_end(cur_si, (int)current_col, true);
+        check_keepend();
+        if ((current_next_flags & HL_HAS_EOL)
+            && keepend_level < 0
+            && syn_getcurline()[current_col] == NUL) {
+          break;
         }
       }
     } else {
-- 
cgit 


From 2c7dc648ca796786b4847771c1df71dea20a1774 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 10:45:42 +0800
Subject: vim-patch:8.2.3925: diff mode confused by NUL bytes (#18033)

Problem:    Diff mode confused by NUL bytes.
Solution:   Handle NUL bytes differently. (Christian Brabandt, closes vim/vim#9421,
            closes vim/vim#9418)
https://github.com/vim/vim/commit/06f6095623cfcc72da08748c058d13b465652fd4
---
 src/nvim/diff.c                    | 14 ++++++++++----
 src/nvim/testdir/test_diffmode.vim | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index a6bbe40999..0b55fb877c 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -743,11 +743,16 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
   for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
     for (char_u *s = ml_get_buf(buf, lnum, false); *s != NUL;) {
       if (diff_flags & DIFF_ICASE) {
+        int c;
         char_u cbuf[MB_MAXBYTES + 1];
 
-        // xdiff doesn't support ignoring case, fold-case the text.
-        int c = utf_ptr2char(s);
-        c = utf_fold(c);
+        if (*s == NL) {
+          c = NUL;
+        } else {
+          // xdiff doesn't support ignoring case, fold-case the text.
+          c = utf_ptr2char(s);
+          c = utf_fold(c);
+        }
         const int orig_len = utfc_ptr2len(s);
         if (utf_char2bytes(c, cbuf) != orig_len) {
           // TODO(Bram): handle byte length difference
@@ -759,7 +764,8 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din)
         s += orig_len;
         len += orig_len;
       } else {
-        ptr[len++] = *s++;
+        ptr[len++] = *s == NL ? NUL : *s;
+        s++;
       }
     }
     ptr[len++] = NL;
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 10eb979b45..be9a77ee75 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -1295,4 +1295,41 @@ func Test_diff_filler_cursorcolumn()
 endfunc
 
 
+func Test_diff_binary()
+  CheckScreendump
+
+  let content =<< trim END
+    call setline(1, ['a', 'b', "c\n", 'd', 'e', 'f', 'g'])
+    vnew
+    call setline(1, ['A', 'b', 'c', 'd', 'E', 'f', 'g'])
+    windo diffthis
+    wincmd p
+    norm! gg0
+    redraw!
+  END
+  call writefile(content, 'Xtest_diff_bin')
+  let buf = RunVimInTerminal('-S Xtest_diff_bin', {})
+
+  " Test using internal diff
+  call VerifyScreenDump(buf, 'Test_diff_bin_01', {})
+
+  " Test using internal diff and case folding
+  call term_sendkeys(buf, ":set diffopt+=icase\")
+  call term_sendkeys(buf, "\")
+  call VerifyScreenDump(buf, 'Test_diff_bin_02', {})
+  " Test using external diff
+  call term_sendkeys(buf, ":set diffopt=filler\")
+  call term_sendkeys(buf, "\")
+  call VerifyScreenDump(buf, 'Test_diff_bin_03', {})
+  " Test using external diff and case folding
+  call term_sendkeys(buf, ":set diffopt=filler,icase\")
+  call term_sendkeys(buf, "\")
+  call VerifyScreenDump(buf, 'Test_diff_bin_04', {})
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('Xtest_diff_bin')
+  set diffopt&vim
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
-- 
cgit 


From 38506553f7213cc9df0f222c29127a9f182351e4 Mon Sep 17 00:00:00 2001
From: zeertzjq 
Date: Fri, 8 Apr 2022 19:01:08 +0800
Subject: vim-patch:8.2.4711: when 'insermode' is set :edit from  mapping
 misbehaves

Problem:    When 'insermode' is set :edit from  mapping misbehaves.
Solution:   Don't set "need_start_insertmode" when already in Insert mode.
            (closes vim/vim#10116)
https://github.com/vim/vim/commit/3a56b6d405fc0f1ca928b77382f97d0c552bea64
---
 src/nvim/ex_cmds.c             |  2 +-
 src/nvim/testdir/test_edit.vim | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4e98319723..98aabe89b3 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2875,7 +2875,7 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new
     redraw_curbuf_later(NOT_VALID);     // redraw this buffer later
   }
 
-  if (p_im) {
+  if (p_im && (State & INSERT) == 0) {
     need_start_insertmode = true;
   }
 
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 360b3aaaa0..eea5d190b2 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1627,6 +1627,29 @@ func Test_edit_is_a_directory()
   call delete(dirname, 'rf')
 endfunc
 
+" Using :edit without leaving 'insertmode' should not cause Insert mode to be
+" re-entered immediately after 
+func Test_edit_insertmode_ex_edit()
+  CheckRunVimInTerminal
+
+  let lines =<< trim END
+    set insertmode noruler
+    inoremap  edit Xfoo
+  END
+  call writefile(lines, 'Xtest_edit_insertmode_ex_edit')
+
+  let buf = RunVimInTerminal('-S Xtest_edit_insertmode_ex_edit', #{rows: 6})
+  call TermWait(buf, 50)
+  call assert_match('^-- INSERT --\s*$', term_getline(buf, 6))
+  call term_sendkeys(buf, "\\")
+  call TermWait(buf, 50)
+  call assert_notmatch('^-- INSERT --\s*$', term_getline(buf, 6))
+
+  " clean up
+  call StopVimInTerminal(buf)
+  call delete('Xtest_edit_insertmode_ex_edit')
+endfunc
+
 func Test_edit_browse()
   " in the GUI this opens a file picker, we only test the terminal behavior
   CheckNotGui
-- 
cgit 


From 30bc02c6364f384e437a6f53b057522d585492fc Mon Sep 17 00:00:00 2001
From: Gregory Anders 
Date: Sat, 19 Mar 2022 19:16:19 -0600
Subject: feat(api)!: pass args table to autocommand callbacks

---
 src/nvim/api/autocmd.c | 16 ++++++++++++---
 src/nvim/autocmd.c     | 55 ++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 60 insertions(+), 11 deletions(-)

(limited to 'src')

diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 57f392f98e..ccf4ae3d02 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -366,7 +366,7 @@ cleanup:
 ///   {"CursorHold", "BufPreWrite", "BufPostWrite"}
 /// 
/// -/// @param event (String|Array) The event or events to register this autocommand +/// @param event (string|array) The event or events to register this autocommand /// @param opts Dictionary of autocommand options: /// - group (string|integer) optional: the autocommand group name or /// id to match against. @@ -375,8 +375,18 @@ cleanup: /// - buffer (integer) optional: buffer number for buffer local autocommands /// |autocmd-buflocal|. Cannot be used with {pattern}. /// - desc (string) optional: description of the autocommand. -/// - callback (function|string) optional: Lua function or Vim function (as string) to -/// execute on event. Cannot be used with {command} +/// - callback (function|string) optional: if a string, the name of a Vimscript function +/// to call when this autocommand is triggered. Otherwise, a Lua function which is +/// called when this autocommand is triggered. Cannot be used with {command}. Lua +/// callbacks can return true to delete the autocommand; in addition, they accept a +/// single table argument with the following keys: +/// - id: (number) the autocommand id +/// - event: (string) the name of the event that triggered the autocommand +/// |autocmd-events| +/// - group: (number|nil) the autocommand group id, if it exists +/// - match: (string) the expanded value of || +/// - buf: (number) the expanded value of || +/// - file: (string) the expanded value of || /// - command (string) optional: Vim command to execute on event. Cannot be used with /// {callback} /// - once (boolean) optional: defaults to false. Run the autocommand diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 5fc36a8412..48c1bb740d 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -2005,6 +2005,50 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last) } } +static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc) +{ + bool ret = false; + Callback callback = ac->exec.callable.cb; + if (callback.type == kCallbackLua) { + Dictionary data = ARRAY_DICT_INIT; + PUT(data, "id", INTEGER_OBJ(ac->id)); + PUT(data, "event", CSTR_TO_OBJ(event_nr2name(apc->event))); + PUT(data, "match", CSTR_TO_OBJ((char *)autocmd_match)); + PUT(data, "file", CSTR_TO_OBJ((char *)autocmd_fname)); + PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr)); + + int group = apc->curpat->group; + switch (group) { + case AUGROUP_ERROR: + abort(); // unreachable + case AUGROUP_DEFAULT: + case AUGROUP_ALL: + case AUGROUP_DELETED: + // omit group in these cases + break; + default: + PUT(data, "group", INTEGER_OBJ(group)); + break; + } + + FIXED_TEMP_ARRAY(args, 1); + args.items[0] = DICTIONARY_OBJ(data); + + Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL); + if (result.type == kObjectTypeBoolean) { + ret = result.data.boolean; + } + api_free_dictionary(data); + api_free_object(result); + } else { + typval_T argsin = TV_INITIAL_VALUE; + typval_T rettv = TV_INITIAL_VALUE; + callback_call(&callback, 0, &argsin, &rettv); + } + + return ret; +} + /// Get next autocommand command. /// Called by do_cmdline() to get the next line for ":if". /// @return allocated string, or NULL for end of autocommands. @@ -2069,16 +2113,11 @@ char_u *getnextac(int c, void *cookie, int indent, bool do_concat) current_sctx = ac->script_ctx; if (ac->exec.type == CALLABLE_CB) { - typval_T argsin = TV_INITIAL_VALUE; - typval_T rettv = TV_INITIAL_VALUE; - if (callback_call(&ac->exec.callable.cb, 0, &argsin, &rettv)) { - if (ac->exec.callable.cb.type == kCallbackLua) { - // If a Lua callback returns 'true' then the autocommand is removed - oneshot = true; - } + if (call_autocmd_callback(ac, acp)) { + // If an autocommand callback returns true, delete the autocommand + oneshot = true; } - // TODO(tjdevries): // // Major Hack Alert: -- cgit From fda9adab5d0688fbd94544dc7146d0957c728c69 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 9 Apr 2022 13:31:42 +0800 Subject: vim-patch:partial:8.1.2333: with modifyOtherKeys CTRL-^ doesn't work (#18048) Problem: With modifyOtherKeys CTRL-^ doesn't work. Solution: Handle the exception. https://github.com/vim/vim/commit/828ffd596394f714270a01a55fc3f949a8bd9b35 --- src/nvim/getchar.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 299385cb17..d615255828 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1590,11 +1590,19 @@ int vgetc(void) c = utf_ptr2char(buf); } - if ((mod_mask & MOD_MASK_CTRL) && (c >= '?' && c <= '_')) { - c = Ctrl_chr(c); - mod_mask &= ~MOD_MASK_CTRL; - if (c == 0) { // is - c = K_ZERO; + // A modifier was not used for a mapping, apply it to ASCII + // keys. Shift would already have been applied. + if (mod_mask & MOD_MASK_CTRL) { + if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) { + c &= 0x1f; + mod_mask &= ~MOD_MASK_CTRL; + if (c == 0) { + c = K_ZERO; + } + } else if (c == '6') { + // CTRL-6 is equivalent to CTRL-^ + c = 0x1e; + mod_mask &= ~MOD_MASK_CTRL; } } -- cgit From 8055f9857b8e384634d457533dfdb08618fc36f0 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 9 Apr 2022 11:19:18 +0200 Subject: vim-patch:8.2.4715: Vagrantfile not recognized (#18052) Problem: Vagrantfile not recognized. Solution: Recognize Vagrantfile as ruby. (Julien Voisin, closes vim/vim#10119) https://github.com/vim/vim/commit/5e1792270a072a96157e5d5e1d6a97414e26d0bf --- src/nvim/testdir/test_filetype.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 42271a014d..9feab735e4 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -455,7 +455,7 @@ let s:filename_checks = { \ 'rpl': ['file.rpl'], \ 'rst': ['file.rst'], \ 'rtf': ['file.rtf'], - \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile'], + \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake', 'rakefile', 'Rakefile', 'rantfile', 'Rantfile', 'rakefile-file', 'Rakefile-file', 'Puppetfile', 'Vagrantfile'], \ 'rust': ['file.rs'], \ 'samba': ['smb.conf'], \ 'sas': ['file.sas'], -- cgit From 45f62464d3e1f39e74fca627e27eea106ffe46ef Mon Sep 17 00:00:00 2001 From: Tom Praschan <13141438+tom-anders@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:14:02 +0200 Subject: vim-patch:8.2.4702: C++ scope labels are hard-coded Problem: C++ scope labels are hard-coded. Solution: Add 'cinscopedecls' to define the labels. (Tom Praschan, closes vim/vim#10109) https://github.com/vim/vim/commit/3506cf34c17c5eae6c2d1317db1fcd5a8493c288 --- src/nvim/buffer.c | 1 + src/nvim/buffer_defs.h | 1 + src/nvim/indent_c.c | 33 ++++++++++++++++-------------- src/nvim/option.c | 6 ++++++ src/nvim/option_defs.h | 1 + src/nvim/options.lua | 9 ++++++++ src/nvim/testdir/test_cindent.vim | 43 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 79 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bf592a626d..b65fa77660 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1938,6 +1938,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_cink); clear_string_option(&buf->b_p_cino); clear_string_option(&buf->b_p_cinw); + clear_string_option(&buf->b_p_cinsd); clear_string_option(&buf->b_p_cpt); clear_string_option(&buf->b_p_cfu); clear_string_option(&buf->b_p_ofu); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 422741cc2f..2bb45e9fa0 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -696,6 +696,7 @@ struct file_buffer { char_u *b_p_cino; ///< 'cinoptions' char_u *b_p_cink; ///< 'cinkeys' char_u *b_p_cinw; ///< 'cinwords' + char_u *b_p_cinsd; ///< 'cinscopedecls' char_u *b_p_com; ///< 'comments' char_u *b_p_cms; ///< 'commentstring' char_u *b_p_cpt; ///< 'complete' diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 7f483d02ab..d26a27c1c2 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -512,24 +512,27 @@ static int cin_isdefault(const char_u *s) && s[1] != ':'; } -/* - * Recognize a "public/private/protected" scope declaration label. - */ -bool cin_isscopedecl(const char_u *s) +/// Recognize a scope declaration label set in 'cinscopedecls'. +bool cin_isscopedecl(const char_u *p) { - int i; + const char_u *s = cin_skipcomment(p); - s = cin_skipcomment(s); - if (STRNCMP(s, "public", 6) == 0) { - i = 6; - } else if (STRNCMP(s, "protected", 9) == 0) { - i = 9; - } else if (STRNCMP(s, "private", 7) == 0) { - i = 7; - } else { - return false; + const size_t cinsd_len = STRLEN(curbuf->b_p_cinsd) + 1; + char_u *cinsd_buf = xmalloc(cinsd_len); + + for (char_u *cinsd = curbuf->b_p_cinsd; *cinsd; ) { + const size_t len = copy_option_part(&cinsd, cinsd_buf, cinsd_len, ","); + if (STRNCMP(s, cinsd_buf, len) == 0) { + const char_u *skip = cin_skipcomment(s + len); + if (*skip == ':' && skip[1] != ':') { + return true; + } + } } - return *(s = cin_skipcomment(s + i)) == ':' && s[1] != ':'; + + xfree(cinsd_buf); + + return false; } // Maximum number of lines to search back for a "namespace" line. diff --git a/src/nvim/option.c b/src/nvim/option.c index 9fd0e0b222..6522454aa5 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -133,6 +133,7 @@ static int p_cin; static char_u *p_cink; static char_u *p_cino; static char_u *p_cinw; +static char_u *p_cinsd; static char_u *p_com; static char_u *p_cms; static char_u *p_cpt; @@ -2060,6 +2061,7 @@ void check_buf_options(buf_T *buf) parse_cino(buf); check_string_option(&buf->b_p_ft); check_string_option(&buf->b_p_cinw); + check_string_option(&buf->b_p_cinsd); check_string_option(&buf->b_p_cpt); check_string_option(&buf->b_p_cfu); check_string_option(&buf->b_p_ofu); @@ -6058,6 +6060,8 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curbuf->b_p_cink); case PV_CINO: return (char_u *)&(curbuf->b_p_cino); + case PV_CINSD: + return (char_u *)&(curbuf->b_p_cinsd); case PV_CINW: return (char_u *)&(curbuf->b_p_cinw); case PV_COM: @@ -6505,6 +6509,8 @@ void buf_copy_options(buf_T *buf, int flags) COPY_OPT_SCTX(buf, BV_CINK); buf->b_p_cino = vim_strsave(p_cino); COPY_OPT_SCTX(buf, BV_CINO); + buf->b_p_cinsd = vim_strsave(p_cinsd); + COPY_OPT_SCTX(buf, BV_CINSD); // Don't copy 'filetype', it must be detected buf->b_p_ft = empty_option; buf->b_p_pi = p_pi; diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 6ce95ea690..bf71b63cc8 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -773,6 +773,7 @@ enum { BV_CINK, BV_CINO, BV_CINW, + BV_CINSD, BV_CM, BV_CMS, BV_COM, diff --git a/src/nvim/options.lua b/src/nvim/options.lua index e665ffd346..313cace4b2 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -350,6 +350,15 @@ return { varname='p_cinw', defaults={if_true="if,else,while,do,for,switch"} }, + { + full_name='cinscopedecls', abbreviation='cinsd', + short_desc=N_("words that are recognized by 'cino-g'"), + type='string', list='onecomma', scope={'buffer'}, + deny_duplicates=true, + alloced=true, + varname='p_cinsd', + defaults={if_true="public,protected,private"} + }, { full_name='clipboard', abbreviation='cb', short_desc=N_("use the clipboard as the unnamed register"), diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim index 4b702bf2b8..4d69aed96c 100644 --- a/src/nvim/testdir/test_cindent.vim +++ b/src/nvim/testdir/test_cindent.vim @@ -5311,6 +5311,49 @@ func Test_cindent_case() bwipe! endfunc +func Test_cindent_scopedecls() + new + setl cindent ts=4 sw=4 + setl cino=g0 + setl cinsd+=public\ slots,signals + + let code =<< trim [CODE] + class Foo + { + public: + virtual void foo() = 0; + public slots: + void onBar(); + signals: + void baz(); + private: + int x; + }; + [CODE] + + call append(0, code) + normal gg + normal ]]=][ + + let expected =<< trim [CODE] + class Foo + { + public: + virtual void foo() = 0; + public slots: + void onBar(); + signals: + void baz(); + private: + int x; + }; + + [CODE] + + call assert_equal(expected, getline(1, '$')) + enew! | close +endfunc + func Test_cindent_pragma() new setl cindent ts=4 sw=4 -- cgit From bf39c5fe4fcd54f32610849b5bacd129e449729d Mon Sep 17 00:00:00 2001 From: Tom Praschan <13141438+tom-anders@users.noreply.github.com> Date: Fri, 8 Apr 2022 19:27:48 +0200 Subject: vim-patch:8.2.4703: memory leak in handling 'cinscopedecls' Problem: Memory leak in handling 'cinscopedecls'. Solution: Free the memory before returning. https://github.com/vim/vim/commit/cb49a1d934d38cbc0d445af59b71ffe45cbcae75 --- src/nvim/indent_c.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index d26a27c1c2..f2ae8079d8 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -520,19 +520,22 @@ bool cin_isscopedecl(const char_u *p) const size_t cinsd_len = STRLEN(curbuf->b_p_cinsd) + 1; char_u *cinsd_buf = xmalloc(cinsd_len); + bool found = false; + for (char_u *cinsd = curbuf->b_p_cinsd; *cinsd; ) { const size_t len = copy_option_part(&cinsd, cinsd_buf, cinsd_len, ","); if (STRNCMP(s, cinsd_buf, len) == 0) { const char_u *skip = cin_skipcomment(s + len); if (*skip == ':' && skip[1] != ':') { - return true; + found = true; + break; } } } xfree(cinsd_buf); - return false; + return found; } // Maximum number of lines to search back for a "namespace" line. -- cgit From 3280dc2b60db8d4e36bff6d481e3e873f32040b1 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 9 Apr 2022 17:42:46 +0200 Subject: vim-patch:8.2.4720: ABB Rapid files are not recognized properly (#18057) Problem: ABB Rapid files are not recognized properly. Solution: Add checks for ABB Rapid files. (Patrick Meiser-Knosowski, closes #10104) https://github.com/vim/vim/commit/b09c320039ad49e62d2e2d7f14ba47ee3ca0706a --- src/nvim/testdir/test_filetype.vim | 191 ++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 9feab735e4..1f76063aed 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -99,7 +99,7 @@ let s:filename_checks = { \ 'cdrtoc': ['file.toc'], \ 'cf': ['file.cfm', 'file.cfi', 'file.cfc'], \ 'cfengine': ['cfengine.conf'], - \ 'cfg': ['file.cfg', 'file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'], + \ 'cfg': ['file.hgrc', 'filehgrc', 'hgrc', 'some-hgrc'], \ 'ch': ['file.chf'], \ 'chaiscript': ['file.chai'], \ 'chaskell': ['file.chs'], @@ -150,7 +150,7 @@ let s:filename_checks = { \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS', 'any/etc/DIR_COLORS'], \ 'dnsmasq': ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], \ 'dockerfile': ['Containerfile', 'Dockerfile', 'file.Dockerfile', 'Dockerfile.debian', 'Containerfile.something'], - \ 'dosbatch': ['file.bat', 'file.sys'], + \ 'dosbatch': ['file.bat'], \ 'dosini': ['.editorconfig', '/etc/pacman.conf', '/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/pacman.conf', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap'], \ 'dot': ['file.dot', 'file.gv'], \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'], @@ -615,7 +615,7 @@ let s:filename_checks = { \ } let s:filename_case_checks = { - \ 'modula2': ['file.DEF', 'file.MOD'], + \ 'modula2': ['file.DEF'], \ 'bzl': ['file.BUILD', 'BUILD'], \ } @@ -807,6 +807,36 @@ func Test_bas_file() filetype off endfunc +" Test dist#ft#FTcfg() +func Test_cfg_file() + filetype on + + " *.cfg defaults to cfg + call writefile(['looks like cfg'], 'cfgfile.cfg') + split cfgfile.cfg + call assert_equal('cfg', &filetype) + + let g:filetype_cfg = 'other' + edit + call assert_equal('other', &filetype) + bwipe! + unlet g:filetype_cfg + + " RAPID cfg + let ext = 'cfg' + for i in ['EIO', 'MMC', 'MOC', 'PROC', 'SIO', 'SYS'] + call writefile([i .. ':CFG'], 'cfgfile.' .. ext) + execute "split cfgfile." .. ext + call assert_equal('rapid', &filetype) + bwipe! + call delete('cfgfile.' .. ext) + " check different case of file extension + let ext = substitute(ext, '\(\l\)', '\u\1', '') + endfor + + filetype off +endfunc + func Test_d_file() filetype on @@ -1241,6 +1271,81 @@ func Test_m_file() filetype off endfunc +func Test_mod_file() + filetype on + + " *.mod defaults to Modsim III + call writefile(['locks like Modsim III'], 'modfile.mod') + split modfile.mod + call assert_equal('modsim3', &filetype) + bwipe! + + " Users preference set by g:filetype_mod + let g:filetype_mod = 'lprolog' + split modfile.mod + call assert_equal('lprolog', &filetype) + unlet g:filetype_mod + bwipe! + + " RAPID header start with a line containing only "%%%", + " but is not always present. + call writefile(['%%%'], 'modfile.mod') + split modfile.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 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 assert_equal('rapid', &filetype) + bwipe! + + " Modula-2 MODULE not start of line + call writefile(['IMPLEMENTATION MODULE Module2Mod;'], 'modfile.MOD') + split modfile.MOD + call assert_equal('modula2', &filetype) + bwipe! + + " Modula-2 with comment and empty lines prior MODULE + call writefile(['', '(* with', ' comment *)', '', 'MODULE Module2Mod;'], 'modfile.MOD') + split modfile.MOD + call assert_equal('modula2', &filetype) + bwipe! + call delete('modfile.MOD') + + " LambdaProlog module + call writefile(['module lpromod.'], 'modfile.mod') + split modfile.mod + call assert_equal('lprolog', &filetype) + 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) + bwipe! + call delete('modfile.mod') + + " go.mod + call writefile(['module example.com/M'], 'go.mod') + split go.mod + call assert_equal('gomod', &filetype) + bwipe! + call delete('go.mod') + + filetype off +endfunc + func Test_patch_file() filetype on @@ -1309,6 +1414,50 @@ func Test_pp_file() filetype off endfunc +" Test dist#ft#FTprg() +func Test_prg_file() + filetype on + + " *.prg defaults to clipper + call writefile(['looks like clipper'], 'prgfile.prg') + split prgfile.prg + call assert_equal('clipper', &filetype) + bwipe! + + " Users preference set by g:filetype_prg + let g:filetype_prg = 'eviews' + split prgfile.prg + call assert_equal('eviews', &filetype) + unlet g:filetype_prg + bwipe! + + " RAPID header start with a line containing only "%%%", + " but is not always present. + call writefile(['%%%'], 'prgfile.prg') + split prgfile.prg + call assert_equal('rapid', &filetype) + bwipe! + call delete('prgfile.prg') + + " RAPID supports umlauts in module names, leading spaces, + " the .prg extension is not case sensitive. + call writefile([' module ÜmlautModule'], 'prgfile.Prg') + split prgfile.Prg + call assert_equal('rapid', &filetype) + bwipe! + call delete('prgfile.Prg') + + " RAPID is not case sensitive, embedded spaces, sysmodule, + " file starts with empty line(s). + call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'prgfile.PRG') + split prgfile.PRG + call assert_equal('rapid', &filetype) + bwipe! + call delete('prgfile.PRG') + + filetype off +endfunc + func Test_src_file() filetype on @@ -1333,6 +1482,42 @@ func Test_src_file() filetype off endfunc +func Test_sys_file() + filetype on + + " *.sys defaults to Batch file for MSDOS + call writefile(['looks like dos batch'], 'sysfile.sys') + split sysfile.sys + call assert_equal('bat', &filetype) + bwipe! + + " RAPID header start with a line containing only "%%%", + " but is not always present. + call writefile(['%%%'], 'sysfile.sys') + split sysfile.sys + call assert_equal('rapid', &filetype) + bwipe! + call delete('sysfile.sys') + + " RAPID supports umlauts in module names, leading spaces, + " the .sys extension is not case sensitive. + call writefile([' module ÜmlautModule'], 'sysfile.Sys') + split sysfile.Sys + call assert_equal('rapid', &filetype) + bwipe! + call delete('sysfile.Sys') + + " RAPID is not case sensitive, embedded spaces, sysmodule, + " file starts with empty line(s). + call writefile(['', 'MODULE rapidmödüle (SYSMODULE,NOSTEPIN)'], 'sysfile.SYS') + split sysfile.SYS + call assert_equal('rapid', &filetype) + bwipe! + call delete('sysfile.SYS') + + filetype off +endfunc + func Test_tex_file() filetype on -- cgit From 61bd5426f4ef0d8f45d0972f7da870131ae278ba Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sat, 9 Apr 2022 17:43:33 +0200 Subject: vim-patch:8.2.4721: cooklang files are not recognized (#18058) Problem: Cooklang files are not recognized. Solution: recognize *.cook files. (Goc Dundar, closes vim/vim#10120) https://github.com/vim/vim/commit/36951ed1dab2b2e816dc8959c72b5732f36d9e3b --- src/nvim/testdir/test_filetype.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 1f76063aed..9d31eae17d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -116,6 +116,7 @@ let s:filename_checks = { \ 'conf': ['auto.master'], \ 'config': ['configure.in', 'configure.ac', '/etc/hostname.file'], \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi', 'file.mkxl', 'file.mklx'], + \ 'cook': ['file.cook'], \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], \ 'crm': ['file.crm'], \ 'crontab': ['crontab', 'crontab.file', '/etc/cron.d/file', 'any/etc/cron.d/file'], -- cgit From 2a2c4e191f5c2cb70bafaa213b5697308dc6f850 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 04:50:49 +0800 Subject: vim-patch:8.2.4718: @@@ in the last line sometimes drawn in the wrong place (#18055) Problem: @@@ in the last line sometimes drawn in the wrong place. Solution: Make sure the column is valid. (closes vim/vim#10130) https://github.com/vim/vim/commit/cee9c844f27bceaba90362a3fa27a04d4d06c0fd --- src/nvim/screen.c | 12 +++++++----- src/nvim/testdir/test_display.vim | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index baeb2fcb03..296255ed8c 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1639,16 +1639,18 @@ static void win_update(win_T *wp, DecorProviders *providers) int scr_row = wp->w_grid.Rows - 1; // Last line isn't finished: Display "@@@" in the last screen line. - grid_puts_len(&wp->w_grid, (char_u *)"@@", 2, scr_row, 0, at_attr); + grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.Columns, 2), scr_row, 0, at_attr); grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.Columns, '@', ' ', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline" + int start_col = wp->w_grid.Columns - 3; + // Last line isn't finished: Display "@@@" at the end. grid_fill(&wp->w_grid, wp->w_grid.Rows - 1, wp->w_grid.Rows, - wp->w_grid.Columns - 3, wp->w_grid.Columns, '@', '@', at_attr); + MAX(start_col, 0), wp->w_grid.Columns, '@', '@', at_attr); set_empty_rows(wp, srow); wp->w_botline = lnum; } else { @@ -6001,9 +6003,9 @@ static void end_search_hl(void) } -/// Fill the grid from 'start_row' to 'end_row', from 'start_col' to 'end_col' -/// with character 'c1' in first column followed by 'c2' in the other columns. -/// Use attributes 'attr'. +/// Fill the grid from "start_row" to "end_row" (exclusive), from "start_col" +/// to "end_col" (exclusive) with character "c1" in first column followed by +/// "c2" in the other columns. Use attributes "attr". void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int end_col, int c1, int c2, int attr) { diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index 094283a3a3..6938abbc28 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -325,4 +325,31 @@ func Test_display_linebreak_breakat() let &breakat=_breakat endfunc +func Test_display_lastline() + CheckScreendump + + let lines =<< trim END + call setline(1, ['aaa', 'b'->repeat(100)]) + set display=truncate + vsplit + 100wincmd < + END + call writefile(lines, 'XdispLastline') + let buf = RunVimInTerminal('-S XdispLastline', #{rows: 10}) + call VerifyScreenDump(buf, 'Test_display_lastline_1', {}) + + call term_sendkeys(buf, ":set display=lastline\") + call VerifyScreenDump(buf, 'Test_display_lastline_2', {}) + + call term_sendkeys(buf, ":100wincmd >\") + call VerifyScreenDump(buf, 'Test_display_lastline_3', {}) + + call term_sendkeys(buf, ":set display=truncate\") + call VerifyScreenDump(buf, 'Test_display_lastline_4', {}) + + call StopVimInTerminal(buf) + call delete('XdispLastline') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From 12662ac0c4160b81f7875909d765cf8526bbdef2 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 06:45:27 +0800 Subject: vim-patch:8.2.4722: ending recording with mapping records too much (#18060) Problem: When a recording is ended with a mapped key that key is also recorded. Solution: Remember the previous last_recorded_len. (closes vim/vim#10122) https://github.com/vim/vim/commit/81b46a6ccd818609e1ca8cd410e26a58428c30ba --- src/nvim/getchar.c | 10 +++++++++- src/nvim/testdir/test_registers.vim | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index d615255828..85479b220a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1469,8 +1469,14 @@ int vgetc(void) mouse_row = old_mouse_row; mouse_col = old_mouse_col; } else { + // number of characters recorded from the last vgetc() call + static size_t last_vgetc_recorded_len = 0; + mod_mask = 0; - last_recorded_len = 0; + + // last_recorded_len can be larger than last_vgetc_recorded_len + // if peeking records more + last_recorded_len -= last_vgetc_recorded_len; for (;;) { // this is done twice if there are modifiers bool did_inc = false; @@ -1621,6 +1627,8 @@ int vgetc(void) break; } + + last_vgetc_recorded_len = last_recorded_len; } /* diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim index c623edd5a1..b852cfd22f 100644 --- a/src/nvim/testdir/test_registers.vim +++ b/src/nvim/testdir/test_registers.vim @@ -690,6 +690,27 @@ func Test_record_in_select_mode() bwipe! endfunc +" mapping that ends macro recording should be removed from recorded macro +func Test_end_record_using_mapping() + call setline(1, 'aaa') + nnoremap s q + call feedkeys('safas', 'tx') + call assert_equal('fa', @a) + nunmap s + + nnoremap xx q + call feedkeys('0xxafaxx', 'tx') + call assert_equal('fa', @a) + nunmap xx + + nnoremap xsx q + call feedkeys('0qafaxsx', 'tx') + call assert_equal('fa', @a) + nunmap xsx + + bwipe! +endfunc + func Test_end_reg_executing() nnoremap s let @a = 's' -- cgit From ff726cc569994aab61a42c40270e679dc80cca7c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 07:13:22 +0800 Subject: vim-patch:8.2.4719: ">" marker sometimes not displayed in the jumplist (#18056) Problem: ">" marker sometimes not displayed in the jumplist. Solution: If the buffer no longer exists show "-invalid-". (Christian Brabandt, closes vim/vim#10131, closes vim/vim#10100) https://github.com/vim/vim/commit/a0f659c76e22108880f857b8961422afc5ed8f5d Add a modeline to test_jumplist.vim --- src/nvim/mark.c | 5 +++++ src/nvim/testdir/test_alot.vim | 1 - src/nvim/testdir/test_jumplist.vim | 41 ++++++++++++++++++++++++++++++++++++++ src/nvim/testdir/test_jumps.vim | 11 ---------- 4 files changed, 46 insertions(+), 12 deletions(-) delete mode 100644 src/nvim/testdir/test_jumps.vim (limited to 'src') diff --git a/src/nvim/mark.c b/src/nvim/mark.c index b770fef05c..6790bf8240 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -844,6 +844,11 @@ void ex_jumps(exarg_T *eap) if (curwin->w_jumplist[i].fmark.mark.lnum != 0) { name = fm_getname(&curwin->w_jumplist[i].fmark, 16); + // Make sure to output the current indicator, even when on an wiped + // out buffer. ":filter" may still skip it. + if (name == NULL && i == curwin->w_jumplistidx) { + name = vim_strsave((char_u *)"-invalid-"); + } // apply :filter /pat/ or file name not available if (name == NULL || message_filtered(name)) { xfree(name); diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index bb744f7b41..43a519bc84 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -16,7 +16,6 @@ source test_fnamemodify.vim source test_ga.vim source test_glob2regpat.vim source test_global.vim -source test_jumps.vim source test_lispwords.vim source test_menu.vim source test_move.vim diff --git a/src/nvim/testdir/test_jumplist.vim b/src/nvim/testdir/test_jumplist.vim index 9cfbbe2029..91ad940e18 100644 --- a/src/nvim/testdir/test_jumplist.vim +++ b/src/nvim/testdir/test_jumplist.vim @@ -64,3 +64,44 @@ func Test_getjumplist() call delete("Xtest") endfunc + +func Test_jumplist_invalid() + new + clearjumps + " put some randome text + put ='a' + let prev = bufnr('%') + setl nomodified bufhidden=wipe + e XXJumpListBuffer + let bnr = bufnr('%') + " 1) empty jumplist + let expected = [[ + \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}], 1] + call assert_equal(expected, getjumplist()) + let jumps = execute(':jumps') + call assert_equal('>', jumps[-1:]) + " now jump back + exe ":norm! \" + let expected = [[ + \ {'lnum': 2, 'bufnr': prev, 'col': 0, 'coladd': 0}, + \ {'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}], 0] + call assert_equal(expected, getjumplist()) + let jumps = execute(':jumps') + call assert_match('> 0 2 0 -invalid-', jumps) +endfunc + +" Test for '' mark in an empty buffer + +func Test_empty_buffer() + new + insert +a +b +c +d +. + call assert_equal(1, line("''")) + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_jumps.vim b/src/nvim/testdir/test_jumps.vim deleted file mode 100644 index 5a3717d165..0000000000 --- a/src/nvim/testdir/test_jumps.vim +++ /dev/null @@ -1,11 +0,0 @@ -func Test_empty_buffer() - new - insert -a -b -c -d -. - call assert_equal(1, line("''")) - bwipe! -endfunc -- cgit From 263a7fde35f2341f526a536690122b927300021a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 07:20:35 +0800 Subject: vim-patch:8.2.4723: the ModeChanged autocmd event is inefficient Problem: The ModeChanged autocmd event is inefficient. Solution: Avoid allocating memory. (closes vim/vim#10134) Rename trigger_modechanged() to may_trigger_modechanged(). https://github.com/vim/vim/commit/2bf52dd065495cbf28e28792f2c2d50d44546d9f Make v:event readonly for ModeChanged. --- src/nvim/api/vim.c | 5 +-- src/nvim/autocmd.c | 3 +- src/nvim/edit.c | 14 ++++----- src/nvim/eval/funcs.c | 8 +++-- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 4 +-- src/nvim/globals.h | 6 +++- src/nvim/memory.c | 1 - src/nvim/normal.c | 16 +++++----- src/nvim/state.c | 85 ++++++++++++++++++++++++++------------------------- src/nvim/terminal.c | 2 +- src/nvim/vim.h | 2 -- 12 files changed, 77 insertions(+), 71 deletions(-) (limited to 'src') diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 7c7ada55a2..503a1c9f23 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1549,10 +1549,11 @@ Dictionary nvim_get_mode(void) FUNC_API_SINCE(2) FUNC_API_FAST { Dictionary rv = ARRAY_DICT_INIT; - char *modestr = get_mode(); + char modestr[MODE_MAX_LENGTH]; + get_mode(modestr); bool blocked = input_blocking(); - PUT(rv, "mode", STRING_OBJ(cstr_as_string(modestr))); + PUT(rv, "mode", STRING_OBJ(cstr_to_string(modestr))); PUT(rv, "blocking", BOOLEAN_OBJ(blocked)); return rv; diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 48c1bb740d..f65b5ba983 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1083,8 +1083,7 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro // need to initialize last_mode for the first ModeChanged autocmd if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) { - xfree(last_mode); - last_mode = get_mode(); + get_mode(last_mode); } // If the event is CursorMoved, update the last cursor position diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 3eb4ab9517..e223841326 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -387,7 +387,7 @@ static void insert_enter(InsertState *s) State = INSERT; } - trigger_modechanged(); + may_trigger_modechanged(); stop_insert_mode = false; // need to position cursor again when on a TAB @@ -2097,7 +2097,7 @@ static void ins_ctrl_x(void) ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X; } - trigger_modechanged(); + may_trigger_modechanged(); } // Whether other than default completion has been selected. @@ -2710,7 +2710,7 @@ void set_completion(colnr_T startcol, list_T *list) show_pum(save_w_wrow, save_w_leftcol); } - trigger_modechanged(); + may_trigger_modechanged(); ui_flush(); } @@ -3890,7 +3890,7 @@ static bool ins_compl_prep(int c) ins_apply_autocmds(EVENT_COMPLETEDONE); } - trigger_modechanged(); + may_trigger_modechanged(); /* reset continue_* if we left expansion-mode, if we stay they'll be * (re)set properly in ins_complete() */ @@ -4641,7 +4641,7 @@ static int ins_compl_get_exp(pos_T *ini) compl_curr_match = compl_old_match; } } - trigger_modechanged(); + may_trigger_modechanged(); return i; } @@ -8051,7 +8051,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) State = NORMAL; - trigger_modechanged(); + may_trigger_modechanged(); // need to position cursor again when on a TAB if (gchar_cursor() == TAB) { curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL); @@ -8155,7 +8155,7 @@ static void ins_insert(int replaceState) } else { State = replaceState | (State & LANGMAP); } - trigger_modechanged(); + may_trigger_modechanged(); AppendCharToRedobuff(K_INS); showmode(); ui_cursor_shape(); // may show different cursor shape diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index f7d9f76534..d365e075e6 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -6094,15 +6094,17 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "mode()" function static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *mode = get_mode(); + char buf[MODE_MAX_LENGTH]; + + get_mode(buf); // Clear out the minor mode when the argument is not a non-zero number or // non-empty string. if (!non_zero_arg(&argvars[0])) { - mode[1] = NUL; + buf[1] = NUL; } - rettv->vval.v_string = (char_u *)mode; + rettv->vval.v_string = vim_strsave((char_u *)buf); rettv->v_type = VAR_STRING; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1f17101aca..482bf69f67 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -187,7 +187,7 @@ void do_exmode(void) exmode_active = true; State = NORMAL; - trigger_modechanged(); + may_trigger_modechanged(); // When using ":global /pat/ visual" and then "Q" we return to continue // the :global command. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index dd6e4630d2..a7c0b06050 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -921,7 +921,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init } tl_ret = true; } - trigger_modechanged(); + may_trigger_modechanged(); state_enter(&s->state); @@ -6556,7 +6556,7 @@ static int open_cmdwin(void) cmdmsg_rl = save_cmdmsg_rl; State = save_State; - trigger_modechanged(); + may_trigger_modechanged(); setmouse(); return cmdwin_result; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 4aa49337cf..42a7fcc3ab 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -746,7 +746,11 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or // :bufdo is executing EXTERN bool need_start_insertmode INIT(= false); // start insert mode soon -EXTERN char *last_mode INIT(= NULL); + +#define MODE_MAX_LENGTH 4 // max mode length returned in get_mode() + // including the final NUL character + +EXTERN char last_mode[MODE_MAX_LENGTH] INIT(= "n"); EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline diff --git a/src/nvim/memory.c b/src/nvim/memory.c index c895cc1ec6..14930238e5 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -631,7 +631,6 @@ void free_all_mem(void) clear_sb_text(true); // free any scrollback text // Free some global vars. - xfree(last_mode); xfree(last_cmdline); xfree(new_last_cmdline); set_keep_msg(NULL, 0); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 9cee2de0c5..dcfd73a631 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -481,7 +481,7 @@ static void normal_prepare(NormalState *s) if (finish_op != c) { ui_cursor_shape(); // may show different cursor shape } - trigger_modechanged(); + may_trigger_modechanged(); // When not finishing an operator and no register name typed, reset the count. if (!finish_op && !s->oa.regname) { @@ -920,7 +920,7 @@ normal_end: // Reset finish_op, in case it was set s->c = finish_op; finish_op = false; - trigger_modechanged(); + may_trigger_modechanged(); // Redraw the cursor with another shape, if we were in Operator-pending // mode or did a replace command. if (s->c || s->ca.cmdchar == 'r') { @@ -959,7 +959,7 @@ normal_end: if (restart_VIsual_select == 1) { VIsual_select = true; VIsual_select_reg = 0; - trigger_modechanged(); + may_trigger_modechanged(); showmode(); restart_VIsual_select = 0; } @@ -2299,7 +2299,7 @@ void end_visual_mode(void) may_clear_cmdline(); adjust_cursor_eol(); - trigger_modechanged(); + may_trigger_modechanged(); } /* @@ -4113,7 +4113,7 @@ static void nv_ctrlg(cmdarg_T *cap) { if (VIsual_active) { // toggle Selection/Visual mode VIsual_select = !VIsual_select; - trigger_modechanged(); + may_trigger_modechanged(); showmode(); } else if (!checkclearop(cap->oap)) { // print full name if count given or :cd used @@ -4157,7 +4157,7 @@ static void nv_ctrlo(cmdarg_T *cap) { if (VIsual_active && VIsual_select) { VIsual_select = false; - trigger_modechanged(); + may_trigger_modechanged(); showmode(); restart_VIsual_select = 2; // restart Select mode later } else { @@ -5945,7 +5945,7 @@ static void nv_visual(cmdarg_T *cap) // or char/line mode VIsual_mode = cap->cmdchar; showmode(); - trigger_modechanged(); + may_trigger_modechanged(); } redraw_curbuf_later(INVERTED); // update the inversion } else { // start Visual mode @@ -6056,7 +6056,7 @@ static void n_start_visual_mode(int c) foldAdjustVisual(); - trigger_modechanged(); + may_trigger_modechanged(); setmouse(); // Check for redraw after changing the state. conceal_check_cursor_line(); diff --git a/src/nvim/state.c b/src/nvim/state.c index 34e3ddf654..056828574f 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -156,102 +156,105 @@ int get_real_state(void) return State; } -/// @returns[allocated] mode string -char *get_mode(void) +/// Returns the current mode as a string in "buf[MODE_MAX_LENGTH]", NUL +/// terminated. +/// The first character represents the major mode, the following ones the minor +/// ones. +void get_mode(char *buf) { - char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char)); + int i = 0; if (VIsual_active) { if (VIsual_select) { - buf[0] = (char)(VIsual_mode + 's' - 'v'); + buf[i++] = (char)(VIsual_mode + 's' - 'v'); } else { - buf[0] = (char)VIsual_mode; + buf[i++] = (char)VIsual_mode; if (restart_VIsual_select) { - buf[1] = 's'; + buf[i++] = 's'; } } } else if (State == HITRETURN || State == ASKMORE || State == SETWSIZE || State == CONFIRM) { - buf[0] = 'r'; + buf[i++] = 'r'; if (State == ASKMORE) { - buf[1] = 'm'; + buf[i++] = 'm'; } else if (State == CONFIRM) { - buf[1] = '?'; + buf[i++] = '?'; } } else if (State == EXTERNCMD) { - buf[0] = '!'; + buf[i++] = '!'; } else if (State & INSERT) { if (State & VREPLACE_FLAG) { - buf[0] = 'R'; - buf[1] = 'v'; + buf[i++] = 'R'; + buf[i++] = 'v'; if (ins_compl_active()) { - buf[2] = 'c'; + buf[i++] = 'c'; } else if (ctrl_x_mode_not_defined_yet()) { - buf[2] = 'x'; + buf[i++] = 'x'; } } else { if (State & REPLACE_FLAG) { - buf[0] = 'R'; + buf[i++] = 'R'; } else { - buf[0] = 'i'; + buf[i++] = 'i'; } if (ins_compl_active()) { - buf[1] = 'c'; + buf[i++] = 'c'; } else if (ctrl_x_mode_not_defined_yet()) { - buf[1] = 'x'; + buf[i++] = 'x'; } } } else if ((State & CMDLINE) || exmode_active) { - buf[0] = 'c'; + buf[i++] = 'c'; if (exmode_active) { - buf[1] = 'v'; + buf[i++] = 'v'; } } else if (State & TERM_FOCUS) { - buf[0] = 't'; + buf[i++] = 't'; } else { - buf[0] = 'n'; + buf[i++] = 'n'; if (finish_op) { - buf[1] = 'o'; + buf[i++] = 'o'; // to be able to detect force-linewise/blockwise/charwise operations - buf[2] = (char)motion_force; + buf[i++] = (char)motion_force; } else if (restart_edit == 'I' || restart_edit == 'R' || restart_edit == 'V') { - buf[1] = 'i'; - buf[2] = (char)restart_edit; + buf[i++] = 'i'; + buf[i++] = (char)restart_edit; } else if (curbuf->terminal) { - buf[1] = 't'; + buf[i++] = 't'; } } - return buf; + buf[i] = NUL; } -/// Fires a ModeChanged autocmd. -void trigger_modechanged(void) +/// Fires a ModeChanged autocmd if appropriate. +void may_trigger_modechanged(void) { if (!has_event(EVENT_MODECHANGED)) { return; } - char *mode = get_mode(); - if (STRCMP(mode, last_mode) == 0) { - xfree(mode); + char curr_mode[MODE_MAX_LENGTH]; + char_u pattern_buf[2 * MODE_MAX_LENGTH]; + + get_mode(curr_mode); + if (STRCMP(curr_mode, last_mode) == 0) { return; } save_v_event_T save_v_event; dict_T *v_event = get_v_event(&save_v_event); - tv_dict_add_str(v_event, S_LEN("new_mode"), mode); + tv_dict_add_str(v_event, S_LEN("new_mode"), curr_mode); tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode); + tv_dict_set_keys_readonly(v_event); - char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":"); - char_u *pat = concat_str(pat_pre, (char_u *)mode); - xfree(pat_pre); + // concatenate modes in format "old_mode:new_mode" + vim_snprintf((char *)pattern_buf, sizeof(pattern_buf), "%s:%s", last_mode, curr_mode); - apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf); - xfree(last_mode); - last_mode = mode; + apply_autocmds(EVENT_MODECHANGED, pattern_buf, NULL, false, curbuf); + STRCPY(last_mode, curr_mode); - xfree(pat); restore_v_event(v_event, &save_v_event); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 5c8789ec37..f4baa92f5b 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -422,7 +422,7 @@ void terminal_enter(void) curwin->w_redr_status = true; // For mode() in statusline. #8323 ui_busy_start(); apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf); - trigger_modechanged(); + may_trigger_modechanged(); s->state.execute = terminal_execute; s->state.check = terminal_check; diff --git a/src/nvim/vim.h b/src/nvim/vim.h index f65dd4f0d4..64333e9c3d 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -72,8 +72,6 @@ enum { NUMBUFLEN = 65, }; #define TERM_FOCUS 0x2000 // Terminal focus mode #define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview. -#define MODE_MAX_LENGTH 4 // max mode length returned in mode() - // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) -- cgit From 381f8f86da4bf5b24fb993eac281dffd2a2bcd1e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 18:45:11 +0800 Subject: vim-patch:8.2.4728: no test that v:event cannot be modified Problem: No test that v:event cannot be modified. Solution: Add a test. (closes vim/vim#10139) https://github.com/vim/vim/commit/021996ffaa933d9dc0c3553ca01de93fbf3d522b --- src/nvim/testdir/test_autocmd.vim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index c1a120efa4..e3362cd28f 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -2661,5 +2661,23 @@ func Test_bufwipeout_changes_window() %bwipe! endfunc +func Test_v_event_readonly() + autocmd CompleteChanged * let v:event.width = 0 + call assert_fails("normal! i\\", 'E46:') + au! CompleteChanged + + autocmd DirChangedPre * let v:event.directory = '' + call assert_fails('cd .', 'E46:') + au! DirChangedPre + + autocmd ModeChanged * let v:event.new_mode = '' + call assert_fails('normal! cc', 'E46:') + au! ModeChanged + + autocmd TextYankPost * let v:event.operator = '' + call assert_fails('normal! yy', 'E46:') + au! TextYankPost +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From b438bb434305a89457f1c565363f07326d4c5353 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Sun, 10 Apr 2022 13:53:33 +0200 Subject: vim-patch:8.2.4729: HEEx and Surface templates do not need a separate filetype (#18065) Problem: HEEx and Surface templates do not need a separate filetype. Solution: Use Eelixir for the similar filetypes. (Aaron Tinio, closes vim/vim#10124) https://github.com/vim/vim/commit/fa76a24109f3c3287e4ee17b6270bfd5310c12f3 --- src/nvim/testdir/test_filetype.vim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 9d31eae17d..1f6d2ce198 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -166,7 +166,7 @@ let s:filename_checks = { \ 'edif': ['file.edf', 'file.edif', 'file.edo'], \ 'elinks': ['elinks.conf'], \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'], - \ 'eelixir': ['file.eex', 'file.leex'], + \ 'eelixir': ['file.eex', 'file.heex', 'file.leex', 'file.sface'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], \ 'elvish': ['file.elv'], @@ -237,7 +237,6 @@ let s:filename_checks = { \ 'hb': ['file.hb'], \ 'hcl': ['file.hcl'], \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'], - \ 'heex': ['file.heex'], \ 'hex': ['file.hex', 'file.h32'], \ 'hgcommit': ['hg-editor-file.txt'], \ 'hjson': ['file.hjson'], @@ -516,7 +515,6 @@ let s:filename_checks = { \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], - \ 'surface': ['file.sface'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], \ 'swift': ['file.swift'], -- cgit From 8bdcd832ae225d7d479a7eb6ca89d097f44e80f7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 21:22:22 +0800 Subject: refactor(globals.h): avoid confusing comment placement (#18066) These comments were indented in Vim, but their indent was removed in Nvim, causing them to be placed in a confusing place. --- src/nvim/globals.h | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 42a7fcc3ab..2b85b6a208 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -680,11 +680,8 @@ EXTERN bool cmd_silent INIT(= false); // don't echo the command line #define SEA_QUIT 2 // quit editing the file #define SEA_RECOVER 3 // recover the file -EXTERN int swap_exists_action INIT(= SEA_NONE); -// For dialog when swap file already -// exists. -EXTERN bool swap_exists_did_quit INIT(= false); -// Selected "quit" at the dialog. +EXTERN int swap_exists_action INIT(= SEA_NONE); ///< For dialog when swap file already exists. +EXTERN bool swap_exists_did_quit INIT(= false); ///< Selected "quit" at the dialog. EXTERN char_u IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc. EXTERN char_u NameBuff[MAXPATHL]; ///< Buffer for expanding file names @@ -737,15 +734,11 @@ EXTERN reg_extmatch_T *re_extmatch_in INIT(= NULL); // Set by vim_regexec() to store \z\(...\) matches EXTERN reg_extmatch_T *re_extmatch_out INIT(= NULL); -EXTERN bool did_outofmem_msg INIT(= false); -// set after out of memory msg -EXTERN bool did_swapwrite_msg INIT(= false); -// set after swap write error msg -EXTERN int global_busy INIT(= 0); // set when :global is executing -EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or - // :bufdo is executing -EXTERN bool need_start_insertmode INIT(= false); -// start insert mode soon +EXTERN bool did_outofmem_msg INIT(= false); ///< set after out of memory msg +EXTERN bool did_swapwrite_msg INIT(= false); ///< set after swap write error msg +EXTERN int global_busy INIT(= 0); ///< set when :global is executing +EXTERN bool listcmd_busy INIT(= false); ///< set when :argdo, :windo or :bufdo is executing +EXTERN bool need_start_insertmode INIT(= false); ///< start insert mode soon #define MODE_MAX_LENGTH 4 // max mode length returned in get_mode() // including the final NUL character @@ -770,8 +763,7 @@ EXTERN bool g_tag_at_cursor INIT(= false); // whether the tag command comes EXTERN int replace_offset INIT(= 0); // offset for replace_push() -EXTERN char_u *escape_chars INIT(= (char_u *)" \t\\\"|"); -// need backslash in cmd line +EXTERN char_u *escape_chars INIT(= (char_u *)" \t\\\"|"); // need backslash in cmd line EXTERN int keep_help_flag INIT(= false); // doing :ta from help file -- cgit From b2cb05b53e61d162044f71227e0ffeacbf59a4bb Mon Sep 17 00:00:00 2001 From: Loong Wang Date: Mon, 11 Apr 2022 06:56:08 +0800 Subject: feat(events): support SIGWINCH for Signal event #18029 closes #15411 --- src/nvim/os/signal.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 327ab6bc48..79e042b8a5 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -21,7 +21,7 @@ #include "nvim/os/signal.h" #include "nvim/vim.h" -static SignalWatcher spipe, shup, squit, sterm, susr1; +static SignalWatcher spipe, shup, squit, sterm, susr1, swinch; #ifdef SIGPWR static SignalWatcher spwr; #endif @@ -53,6 +53,9 @@ void signal_init(void) #endif #ifdef SIGUSR1 signal_watcher_init(&main_loop, &susr1, NULL); +#endif +#ifdef SIGWINCH + signal_watcher_init(&main_loop, &swinch, NULL); #endif signal_start(); } @@ -70,6 +73,9 @@ void signal_teardown(void) #ifdef SIGUSR1 signal_watcher_close(&susr1, NULL); #endif +#ifdef SIGWINCH + signal_watcher_close(&swinch, NULL); +#endif } void signal_start(void) @@ -88,6 +94,9 @@ void signal_start(void) #ifdef SIGUSR1 signal_watcher_start(&susr1, on_signal, SIGUSR1); #endif +#ifdef SIGWINCH + signal_watcher_start(&swinch, on_signal, SIGWINCH); +#endif } void signal_stop(void) @@ -106,6 +115,9 @@ void signal_stop(void) #ifdef SIGUSR1 signal_watcher_stop(&susr1); #endif +#ifdef SIGWINCH + signal_watcher_stop(&swinch); +#endif } void signal_reject_deadly(void) @@ -140,6 +152,10 @@ static char *signal_name(int signum) #ifdef SIGUSR1 case SIGUSR1: return "SIGUSR1"; +#endif +#ifdef SIGWINCH + case SIGWINCH: + return "SIGWINCH"; #endif default: return "Unknown"; @@ -197,6 +213,12 @@ static void on_signal(SignalWatcher *handle, int signum, void *data) apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGUSR1", curbuf->b_fname, true, curbuf); break; +#endif +#ifdef SIGWINCH + case SIGWINCH: + apply_autocmds(EVENT_SIGNAL, (char_u *)"SIGWINCH", curbuf->b_fname, true, + curbuf); + break; #endif default: ELOG("invalid signal: %d", signum); -- cgit From 9da0023a666e83e6b9f777871553177473bfa9ce Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 09:54:59 +0800 Subject: feat(keymap): add F38-F63 keys (#17893) --- src/nvim/keymap.c | 29 +++++++++++++++++++++++++++++ src/nvim/keymap.h | 29 +++++++++++++++++++++++++++++ src/nvim/terminal.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) (limited to 'src') diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 9ad9640834..1e305528ba 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -221,6 +221,35 @@ static const struct key_name_entry { { K_F35, "F35" }, { K_F36, "F36" }, { K_F37, "F37" }, + { K_F38, "F38" }, + { K_F39, "F39" }, + { K_F40, "F40" }, + + { K_F41, "F41" }, + { K_F42, "F42" }, + { K_F43, "F43" }, + { K_F44, "F44" }, + { K_F45, "F45" }, + { K_F46, "F46" }, + { K_F47, "F47" }, + { K_F48, "F48" }, + { K_F49, "F49" }, + { K_F50, "F50" }, + + { K_F51, "F51" }, + { K_F52, "F52" }, + { K_F53, "F53" }, + { K_F54, "F54" }, + { K_F55, "F55" }, + { K_F56, "F56" }, + { K_F57, "F57" }, + { K_F58, "F58" }, + { K_F59, "F59" }, + { K_F60, "F60" }, + + { K_F61, "F61" }, + { K_F62, "F62" }, + { K_F63, "F63" }, { K_XF1, "xF1" }, { K_XF2, "xF2" }, diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index ae2ec7835e..9dff8ba333 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -325,6 +325,35 @@ enum key_extra { #define K_F35 TERMCAP2KEY('F', 'P') #define K_F36 TERMCAP2KEY('F', 'Q') #define K_F37 TERMCAP2KEY('F', 'R') +#define K_F38 TERMCAP2KEY('F', 'S') +#define K_F39 TERMCAP2KEY('F', 'T') +#define K_F40 TERMCAP2KEY('F', 'U') + +#define K_F41 TERMCAP2KEY('F', 'V') +#define K_F42 TERMCAP2KEY('F', 'W') +#define K_F43 TERMCAP2KEY('F', 'X') +#define K_F44 TERMCAP2KEY('F', 'Y') +#define K_F45 TERMCAP2KEY('F', 'Z') +#define K_F46 TERMCAP2KEY('F', 'a') +#define K_F47 TERMCAP2KEY('F', 'b') +#define K_F48 TERMCAP2KEY('F', 'c') +#define K_F49 TERMCAP2KEY('F', 'd') +#define K_F50 TERMCAP2KEY('F', 'e') + +#define K_F51 TERMCAP2KEY('F', 'f') +#define K_F52 TERMCAP2KEY('F', 'g') +#define K_F53 TERMCAP2KEY('F', 'h') +#define K_F54 TERMCAP2KEY('F', 'i') +#define K_F55 TERMCAP2KEY('F', 'j') +#define K_F56 TERMCAP2KEY('F', 'k') +#define K_F57 TERMCAP2KEY('F', 'l') +#define K_F58 TERMCAP2KEY('F', 'm') +#define K_F59 TERMCAP2KEY('F', 'n') +#define K_F60 TERMCAP2KEY('F', 'o') + +#define K_F61 TERMCAP2KEY('F', 'p') +#define K_F62 TERMCAP2KEY('F', 'q') +#define K_F63 TERMCAP2KEY('F', 'r') // extra set of shifted function keys F1-F4, for vt100 compatible xterm #define K_S_XF1 TERMCAP2KEY(KS_EXTRA, KE_S_XF1) diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index f4baa92f5b..59e0d4da6c 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -1205,6 +1205,58 @@ static VTermKey convert_key(int key, VTermModifier *statep) return VTERM_KEY_FUNCTION(36); case K_F37: return VTERM_KEY_FUNCTION(37); + case K_F38: + return VTERM_KEY_FUNCTION(38); + case K_F39: + return VTERM_KEY_FUNCTION(39); + case K_F40: + return VTERM_KEY_FUNCTION(40); + case K_F41: + return VTERM_KEY_FUNCTION(41); + case K_F42: + return VTERM_KEY_FUNCTION(42); + case K_F43: + return VTERM_KEY_FUNCTION(43); + case K_F44: + return VTERM_KEY_FUNCTION(44); + case K_F45: + return VTERM_KEY_FUNCTION(45); + case K_F46: + return VTERM_KEY_FUNCTION(46); + case K_F47: + return VTERM_KEY_FUNCTION(47); + case K_F48: + return VTERM_KEY_FUNCTION(48); + case K_F49: + return VTERM_KEY_FUNCTION(49); + case K_F50: + return VTERM_KEY_FUNCTION(50); + case K_F51: + return VTERM_KEY_FUNCTION(51); + case K_F52: + return VTERM_KEY_FUNCTION(52); + case K_F53: + return VTERM_KEY_FUNCTION(53); + case K_F54: + return VTERM_KEY_FUNCTION(54); + case K_F55: + return VTERM_KEY_FUNCTION(55); + case K_F56: + return VTERM_KEY_FUNCTION(56); + case K_F57: + return VTERM_KEY_FUNCTION(57); + case K_F58: + return VTERM_KEY_FUNCTION(58); + case K_F59: + return VTERM_KEY_FUNCTION(59); + case K_F60: + return VTERM_KEY_FUNCTION(60); + case K_F61: + return VTERM_KEY_FUNCTION(61); + case K_F62: + return VTERM_KEY_FUNCTION(62); + case K_F63: + return VTERM_KEY_FUNCTION(63); default: return VTERM_KEY_NONE; -- cgit From f94f75dc0512def7fbfdfe100eea2dab3352d61f Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Sun, 10 Apr 2022 19:12:41 -0600 Subject: refactor!: rename nvim_add_user_command to nvim_create_user_command --- src/nvim/api/buffer.c | 10 +++++----- src/nvim/api/private/helpers.c | 3 ++- src/nvim/api/vim.c | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 18f7177489..6d5803a5fe 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -1377,9 +1377,9 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) /// /// @param buffer Buffer handle, or 0 for current buffer. /// @param[out] err Error details, if any. -/// @see nvim_add_user_command -void nvim_buf_add_user_command(Buffer buffer, String name, Object command, Dict(user_command) *opts, - Error *err) +/// @see nvim_create_user_command +void nvim_buf_create_user_command(Buffer buffer, String name, Object command, + Dict(user_command) *opts, Error *err) FUNC_API_SINCE(9) { buf_T *target_buf = find_buffer_by_handle(buffer, err); @@ -1389,14 +1389,14 @@ void nvim_buf_add_user_command(Buffer buffer, String name, Object command, Dict( buf_T *save_curbuf = curbuf; curbuf = target_buf; - add_user_command(name, command, opts, UC_BUFFER, err); + create_user_command(name, command, opts, UC_BUFFER, err); curbuf = save_curbuf; } /// Delete a buffer-local user-defined command. /// /// Only commands created with |:command-buffer| or -/// |nvim_buf_add_user_command()| can be deleted with this function. +/// |nvim_buf_create_user_command()| can be deleted with this function. /// /// @param buffer Buffer handle, or 0 for current buffer. /// @param name Name of the command to delete. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 88954a1aa2..5ba4700f62 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1416,7 +1416,8 @@ const char *get_default_stl_hl(win_T *wp) } } -void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err) +void create_user_command(String name, Object command, Dict(user_command) *opts, int flags, + Error *err) { uint32_t argt = 0; long def = -1; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 503a1c9f23..626f7dc3eb 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -2408,7 +2408,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * /// /// Example: ///
-///    :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
+///    :call nvim_create_user_command('SayHello', 'echo "Hello world!"', {})
 ///    :SayHello
 ///    Hello world!
 /// 
@@ -2436,10 +2436,10 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * /// {command}. /// - force: (boolean, default true) Override any previous definition. /// @param[out] err Error details, if any. -void nvim_add_user_command(String name, Object command, Dict(user_command) *opts, Error *err) +void nvim_create_user_command(String name, Object command, Dict(user_command) *opts, Error *err) FUNC_API_SINCE(9) { - add_user_command(name, command, opts, 0, err); + create_user_command(name, command, opts, 0, err); } /// Delete a user-defined command. -- cgit From a2f157233f274599739941e39673ca4d3b0291c3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 10:23:33 +0800 Subject: fix(tui)!: remove `ESC NUL` forced escape (#17198) This make Nvim recognize `ESC NUL` as , as many terminal emulators (including libvterm) send as `ESC NUL`. There is already another unambiguous way to encode a `ESC` key supported by libtermkey: `ESC [ 2 7 u`, which is a `CSI u` sequence. If one still wants to use `ESC NUL` as `ESC`, they can just map to . --- src/nvim/tui/input.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src') diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 24958ce041..17656c5ddc 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -439,22 +439,6 @@ static HandleState handle_bracketed_paste(TermInput *input) return kNotApplicable; } -// ESC NUL => -static bool handle_forced_escape(TermInput *input) -{ - if (rbuffer_size(input->read_stream.buffer) > 1 - && !rbuffer_cmp(input->read_stream.buffer, "\x1b\x00", 2)) { - // skip the ESC and NUL and push one to the input buffer - size_t rcnt; - termkey_push_bytes(input->tk, rbuffer_read_ptr(input->read_stream.buffer, - &rcnt), 1); - rbuffer_consumed(input->read_stream.buffer, 2); - tk_getkeys(input, true); - return true; - } - return false; -} - static void set_bg_deferred(void **argv) { char *bgvalue = argv[0]; @@ -583,7 +567,6 @@ static void handle_raw_buffer(TermInput *input, bool force) if (!force && (handle_focus_event(input) || (is_paste = handle_bracketed_paste(input)) != kNotApplicable - || handle_forced_escape(input) || (is_bc = handle_background_color(input)) != kNotApplicable)) { if (is_paste == kIncomplete || is_bc == kIncomplete) { // Wait for the next input, leaving it in the raw buffer due to an -- cgit From f89ca7194f484108529cbb93ef61ce45a9a277b5 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 11:32:15 +0800 Subject: fix(events): make v:event readonly in more events (#18070) This makes v:event readonly in these four events: - ChanInfo - ChanOpen - RecordingLeave - TermClose --- src/nvim/channel.c | 1 + src/nvim/ops.c | 1 + src/nvim/terminal.c | 1 + 3 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/nvim/channel.c b/src/nvim/channel.c index f87b3a2f8f..ac6c9cd110 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -836,6 +836,7 @@ static void set_info_event(void **argv) typval_T retval; (void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL); tv_dict_add_dict(dict, S_LEN("info"), retval.vval.v_dict); + tv_dict_set_keys_readonly(dict); apply_autocmds(event, NULL, NULL, false, curbuf); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index dab6d237bf..badc00fb39 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -944,6 +944,7 @@ int do_record(int c) buf[0] = (char)regname; buf[1] = NUL; (void)tv_dict_add_str(dict, S_LEN("regname"), buf); + tv_dict_set_keys_readonly(dict); // Get the recorded key hits. K_SPECIAL will be escaped, this // needs to be removed again to put it in a register. exec_reg then diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 59e0d4da6c..fd870361c7 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -336,6 +336,7 @@ void terminal_close(Terminal *term, int status) save_v_event_T save_v_event; dict_T *dict = get_v_event(&save_v_event); tv_dict_add_nr(dict, S_LEN("status"), status); + tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf); restore_v_event(dict, &save_v_event); } -- cgit From 18ed556bbd95a7d3d925432e71c33dcfc6bf47b8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 10 Apr 2022 21:32:33 +0800 Subject: vim-patch:8.2.4714: using g:filetype_dat and g:filetype_src not tested Problem: Using g:filetype_dat and g:filetype_src not tested. Solution: Add a test. (Patrick Meiser-Knosowski, closes vim/vim#10117) https://github.com/vim/vim/commit/a8034a4886843fbf10bd59a6f55ec723da515b8e --- src/nvim/testdir/test_filetype.vim | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 1f6d2ce198..fbf02989ea 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -875,22 +875,32 @@ endfunc func Test_dat_file() filetype on + " KRL header start with "&WORD", but is not always present. call writefile(['&ACCESS'], 'datfile.dat') split datfile.dat call assert_equal('krl', &filetype) bwipe! call delete('datfile.dat') + " KRL defdat with leading spaces, for KRL file extension is not case + " sensitive. call writefile([' DEFDAT datfile'], 'datfile.Dat') split datfile.Dat call assert_equal('krl', &filetype) bwipe! call delete('datfile.Dat') - call writefile(['', 'defdat datfile'], 'datfile.DAT') + " KRL defdat with embedded spaces, file starts with empty line(s). + call writefile(['', 'defdat datfile public'], 'datfile.DAT') split datfile.DAT call assert_equal('krl', &filetype) bwipe! + + " User may overrule file inspection + let g:filetype_dat = 'dat' + split datfile.DAT + call assert_equal('dat', &filetype) + bwipe! call delete('datfile.DAT') filetype off @@ -1460,22 +1470,31 @@ endfunc func Test_src_file() filetype on + " KRL header start with "&WORD", but is not always present. call writefile(['&ACCESS'], 'srcfile.src') split srcfile.src call assert_equal('krl', &filetype) bwipe! call delete('srcfile.src') + " KRL def with leading spaces, for KRL file extension is not case sensitive. call writefile([' DEF srcfile()'], 'srcfile.Src') split srcfile.Src call assert_equal('krl', &filetype) bwipe! call delete('srcfile.Src') + " KRL global def with embedded spaces, file starts with empty line(s). call writefile(['', 'global def srcfile()'], 'srcfile.SRC') split srcfile.SRC call assert_equal('krl', &filetype) bwipe! + + " User may overrule file inspection + let g:filetype_src = 'src' + split srcfile.SRC + call assert_equal('src', &filetype) + bwipe! call delete('srcfile.SRC') filetype off -- cgit From 2dc86ef3b2328e5ee7b2d814cda05da17ec25eaa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 18:38:54 +0800 Subject: vim-patch:8.2.4733: HEEx and Surface do need a separate filetype Problem: HEEx and Surface do need a separate filetype. Solution: Revert 8.2.4729. (closes vim/vim#10147) https://github.com/vim/vim/commit/4232dff815db1a727cb1aea26783267d8bd731c3 --- src/nvim/testdir/test_filetype.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index fbf02989ea..aeb6e12ead 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -166,7 +166,7 @@ let s:filename_checks = { \ 'edif': ['file.edf', 'file.edif', 'file.edo'], \ 'elinks': ['elinks.conf'], \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'], - \ 'eelixir': ['file.eex', 'file.heex', 'file.leex', 'file.sface'], + \ 'eelixir': ['file.eex', 'file.leex'], \ 'elm': ['file.elm'], \ 'elmfilt': ['filter-rules'], \ 'elvish': ['file.elv'], @@ -237,6 +237,7 @@ let s:filename_checks = { \ 'hb': ['file.hb'], \ 'hcl': ['file.hcl'], \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'], + \ 'heex': ['file.heex'], \ 'hex': ['file.hex', 'file.h32'], \ 'hgcommit': ['hg-editor-file.txt'], \ 'hjson': ['file.hjson'], @@ -515,6 +516,7 @@ let s:filename_checks = { \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], + \ 'surface': ['file.sface'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], \ 'swift': ['file.swift'], @@ -902,6 +904,7 @@ func Test_dat_file() call assert_equal('dat', &filetype) bwipe! call delete('datfile.DAT') + unlet g:filetype_dat filetype off endfunc @@ -1496,6 +1499,7 @@ func Test_src_file() call assert_equal('src', &filetype) bwipe! call delete('srcfile.SRC') + unlet g:filetype_src filetype off endfunc -- cgit From 356cff78ece597059133e33eceb955f72286a319 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 21:29:18 +0800 Subject: vim-patch:8.2.4734: getcharpos() may change a mark position (#18077) Problem: getcharpos() may change a mark position. Solution: Copy the mark position. (closes vim/vim#10148) https://github.com/vim/vim/commit/3caf1cce2b85a8f24195d057f0ad63082543e99e --- src/nvim/eval.c | 31 ++++++++++++++----------------- src/nvim/testdir/test_cursor_func.vim | 10 ++++++++-- 2 files changed, 22 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c1905b31d0..3e855ece15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7739,7 +7739,6 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { static pos_T pos; - pos_T *pp; // Argument can be [lnum, col, coladd]. if (tv->v_type == VAR_LIST) { @@ -7799,33 +7798,31 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (name == NULL) { return NULL; } - if (name[0] == '.') { // Cursor. + + pos.lnum = 0; + if (name[0] == '.') { + // cursor pos = curwin->w_cursor; - if (charcol) { - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); - } - return &pos; - } - if (name[0] == 'v' && name[1] == NUL) { // Visual start. + } else if (name[0] == 'v' && name[1] == NUL) { + // Visual start if (VIsual_active) { pos = VIsual; } else { pos = curwin->w_cursor; } - if (charcol) { - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); - } - return &pos; - } - if (name[0] == '\'') { // Mark. - pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); + } else if (name[0] == '\'') { + // mark + const pos_T *const pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } + pos = *pp; + } + if (pos.lnum != 0) { if (charcol) { - pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col); + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); } - return pp; + return &pos; } pos.coladd = 0; diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 57825b4551..9ba82e3b70 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -140,12 +140,12 @@ func Test_getcharpos() call assert_fails('call getcharpos({})', 'E731:') call assert_equal([0, 0, 0, 0], getcharpos(0)) new - call setline(1, ['', "01\tà4è678", 'â…¥', '012345678']) + call setline(1, ['', "01\tà4è678", 'â…¥', '012345678', ' │ x']) " Test for '.' and '$' normal 1G call assert_equal([0, 1, 1, 0], getcharpos('.')) - call assert_equal([0, 4, 1, 0], getcharpos('$')) + call assert_equal([0, 5, 1, 0], getcharpos('$')) normal 2G6l call assert_equal([0, 2, 7, 0], getcharpos('.')) normal 3G$ @@ -159,6 +159,12 @@ func Test_getcharpos() delmarks m call assert_equal([0, 0, 0, 0], getcharpos("'m")) + " Check mark does not move + normal 5Gfxma + call assert_equal([0, 5, 5, 0], getcharpos("'a")) + call assert_equal([0, 5, 5, 0], getcharpos("'a")) + call assert_equal([0, 5, 5, 0], getcharpos("'a")) + " Test for the visual start column vnoremap SaveVisualStartCharPos() let g:VisualStartPos = [] -- cgit From 85b33fc0426049b5cf3e65bb76aa600272785109 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Mon, 11 Apr 2022 22:50:17 +0200 Subject: vim-patch:8.2.4737: // in JavaScript string recognized as comment (#18083) Problem: // in JavaScript string recognized as comment. Solution: Only check for linecomment if 'cindent' is set. (closes vim/vim#10151) https://github.com/vim/vim/commit/1655619717ff109ea8bf1002883636d5af345e48 --- src/nvim/change.c | 2 +- src/nvim/testdir/test_textformat.vim | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/change.c b/src/nvim/change.c index 44abd69733..024969415d 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1186,7 +1186,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) end_comment_pending = NUL; if (flags & OPENLINE_DO_COM) { lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true); - if (lead_len == 0 && do_cindent && dir == FORWARD) { + if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD) { // Check for a line comment after code. comment_start = check_linecomment(saved_line); if (comment_start != MAXCOL) { diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim index e9f846af7b..b6be3d8861 100644 --- a/src/nvim/testdir/test_textformat.vim +++ b/src/nvim/testdir/test_textformat.vim @@ -278,12 +278,25 @@ func Test_format_c_comment() // END call assert_equal(expected, getline(1, '$')) + + " using 'indentexpr' instead of 'cindent' does not repeat a comment + setl nocindent indentexpr=2 + 3delete + normal 2Gox + let expected =<< trim END + nop; + val = val; // This is a comment + x + END + call assert_equal(expected, getline(1, '$')) + setl cindent indentexpr= + 3delete + normal 2GO let expected =<< trim END nop; val = val; // This is a comment - // END call assert_equal(expected, getline(1, '$')) -- cgit From 10b40440ddbdc979a47335790988966b4e9fb6f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Apr 2022 04:56:15 +0800 Subject: test(old): fix mistakes in porting Vim patches 8.1.1362 and 8.1.1585 (#18080) --- src/nvim/testdir/test_quickfix.vim | 82 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 5457223677..29722ef09b 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1038,21 +1038,20 @@ func s:dir_stack_tests(cchar) let save_efm=&efm set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' - let lines =<< trim [DATA] - Entering dir 'dir1/a' - habits2.txt:1:Nine Healthy Habits - Entering dir 'b' - habits3.txt:2:0 Hours of television - habits2.txt:7:5 Small meals - Entering dir 'dir1/c' - habits4.txt:3:1 Hour of exercise - Leaving dir 'dir1/c' - Leaving dir 'dir1/a' - habits1.txt:4:2 Liters of water - Entering dir 'dir2' - habits5.txt:5:3 Cups of hot green tea - Leaving dir 'dir2 - [DATA] + let lines = ["Entering dir 'dir1/a'", + \ 'habits2.txt:1:Nine Healthy Habits', + \ "Entering dir 'b'", + \ 'habits3.txt:2:0 Hours of television', + \ 'habits2.txt:7:5 Small meals', + \ "Entering dir 'dir1/c'", + \ 'habits4.txt:3:1 Hour of exercise', + \ "Leaving dir 'dir1/c'", + \ "Leaving dir 'dir1/a'", + \ 'habits1.txt:4:2 Liters of water', + \ "Entering dir 'dir2'", + \ 'habits5.txt:5:3 Cups of hot green tea', + \ "Leaving dir 'dir2'" + \] Xexpr "" for l in lines @@ -1086,19 +1085,18 @@ func Test_efm_dirstack() call mkdir('dir1/c') call mkdir('dir2') - let lines =<< trim [DATA] - Nine Healthy Habits, - 0 Hours of television, - 1 Hour of exercise, - 2 Liters of water, - 3 Cups of hot green tea, - 4 Short mental breaks, - 5 Small meals, - 6 AM wake up time, - 7 Minutes of laughter, - 8 Hours of sleep (at least), - 9 PM end of the day and off to bed - [DATA] + let lines = ["Nine Healthy Habits", + \ "0 Hours of television", + \ "1 Hour of exercise", + \ "2 Liters of water", + \ "3 Cups of hot green tea", + \ "4 Short mental breaks", + \ "5 Small meals", + \ "6 AM wake up time", + \ "7 Minutes of laughter", + \ "8 Hours of sleep (at least)", + \ "9 PM end of the day and off to bed" + \ ] call writefile(lines, 'habits1.txt') call writefile(lines, 'dir1/a/habits2.txt') @@ -1220,6 +1218,7 @@ func Test_efm2() (67,3) warning: 's' already defined -- [DATA] + set efm=%+P[%f]%r,(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%+Q--%r " To exercise the push/pop file functionality in quickfix, the test files " need to be created. @@ -1280,20 +1279,21 @@ func Test_efm2() " Test for %A, %C and other formats let lines =<< trim [DATA] - ============================================================== - FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest) - -------------------------------------------------------------- - Traceback (most recent call last): - File "unittests/dbfacadeTest.py", line 89, in testFoo - self.assertEquals(34, dtid) - File "/usr/lib/python2.2/unittest.py", line 286, in - failUnlessEqual - raise self.failureException, \\ - W:AssertionError: 34 != 33 - - -------------------------------------------------------------- - Ran 27 tests in 0.063s + ============================================================== + FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest) + -------------------------------------------------------------- + Traceback (most recent call last): + File "unittests/dbfacadeTest.py", line 89, in testFoo + self.assertEquals(34, dtid) + File "/usr/lib/python2.2/unittest.py", line 286, in + failUnlessEqual + raise self.failureException, \\ + W:AssertionError: 34 != 33 + + -------------------------------------------------------------- + Ran 27 tests in 0.063s [DATA] + set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%t:%m cgetexpr lines let l = getqflist() -- cgit From 53668a5815099f432a5ecebad1d2982ae6813fe6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 Apr 2022 16:24:15 +0800 Subject: vim-patch:8.2.4713: plugins cannot track text scrolling Problem: Plugins cannot track text scrolling. Solution: Add the WinScrolled event. (closes vim/vim#10102) https://github.com/vim/vim/commit/0937182d49fa8db50cec42785f22f1031760a0bd Skip User event in autocmd.txt, not needed unless #10689 is reverted. --- src/nvim/auevents.lua | 1 - src/nvim/autocmd.c | 11 +++++++- src/nvim/buffer_defs.h | 11 ++++---- src/nvim/edit.c | 7 +++-- src/nvim/normal.c | 12 ++++----- src/nvim/testdir/test_autocmd.vim | 55 +++++++++++++++++++++++++++++++++++++++ src/nvim/window.c | 40 ++++++++++++++++------------ 7 files changed, 102 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 518d0b52b2..9e3eb06752 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -141,6 +141,5 @@ return { TermOpen=true, UIEnter=true, UILeave=true, - WinScrolled=true, }, } diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index f65b5ba983..1b146f82c6 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1092,6 +1092,15 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro curwin->w_last_cursormoved = curwin->w_cursor; } + // Initialize the fields checked by the WinScrolled trigger to + // stop it from firing right after the first autocmd is defined. + if (event == EVENT_WINSCROLLED && !has_event(EVENT_WINSCROLLED)) { + curwin->w_last_topline = curwin->w_topline; + curwin->w_last_leftcol = curwin->w_leftcol; + curwin->w_last_width = curwin->w_width; + curwin->w_last_height = curwin->w_height; + } + ap->cmds = NULL; *prev_ap = ap; last_autopat[(int)event] = ap; @@ -1718,7 +1727,7 @@ bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, bool f || event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX || event == EVENT_SIGNAL || event == EVENT_TABCLOSED || event == EVENT_USER - || event == EVENT_WINCLOSED) { + || event == EVENT_WINCLOSED || event == EVENT_WINSCROLLED) { fname = vim_strsave(fname); } else { fname = (char_u *)FullName_save((char *)fname, false); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 2bb45e9fa0..375bebc5ac 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1269,12 +1269,11 @@ struct window_S { colnr_T w_skipcol; // starting column when a single line // doesn't fit in the window - // "w_last_topline" and "w_last_leftcol" are used to determine if - // a Scroll autocommand should be emitted. - linenr_T w_last_topline; ///< last known value for topline - colnr_T w_last_leftcol; ///< last known value for leftcol - int w_last_width; ///< last known value for width - int w_last_height; ///< last known value for height + // four fields that are only used when there is a WinScrolled autocommand + linenr_T w_last_topline; ///< last known value for w_topline + colnr_T w_last_leftcol; ///< last known value for w_leftcol + int w_last_width; ///< last known value for w_width + int w_last_height; ///< last known value for w_height // // Layout of the window in the screen. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index e223841326..8b25378531 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1543,10 +1543,9 @@ static void ins_redraw(bool ready) } } - // Trigger Scroll if viewport changed. - if (ready && has_event(EVENT_WINSCROLLED) - && win_did_scroll(curwin)) { - do_autocmd_winscrolled(curwin); + if (ready) { + // Trigger Scroll if viewport changed. + may_trigger_winscrolled(curwin); } // Trigger BufModified if b_changed_invalid is set. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index dcfd73a631..5b3f27529c 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1223,10 +1223,9 @@ static void normal_check_interrupt(NormalState *s) static void normal_check_window_scrolled(NormalState *s) { - // Trigger Scroll if the viewport changed. - if (!finish_op && has_event(EVENT_WINSCROLLED) - && win_did_scroll(curwin)) { - do_autocmd_winscrolled(curwin); + if (!finish_op) { + // Trigger Scroll if the viewport changed. + may_trigger_winscrolled(curwin); } } @@ -1353,9 +1352,10 @@ static int normal_check(VimState *state) if (skip_redraw || exmode_active) { skip_redraw = false; } else if (do_redraw || stuff_empty()) { - // Need to make sure w_topline and w_leftcol are correct before - // normal_check_window_scrolled() is called. + // Ensure curwin->w_topline and curwin->w_leftcol are up to date + // before triggering a WinScrolled autocommand. update_topline(curwin); + validate_cursor(); normal_check_cursor_moved(s); normal_check_text_changed(s); diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index e3362cd28f..4b4f6ad3d3 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -3,6 +3,7 @@ source shared.vim source check.vim source term_util.vim +source screendump.vim func s:cleanup_buffers() abort for bnr in range(1, bufnr('$')) @@ -260,6 +261,60 @@ func Test_win_tab_autocmd() unlet g:record endfunc +func Test_WinScrolled() + CheckRunVimInTerminal + + let lines =<< trim END + set nowrap scrolloff=0 + for ii in range(1, 18) + call setline(ii, repeat(nr2char(96 + ii), ii * 2)) + endfor + let win_id = win_getid() + let g:matched = v:false + execute 'au WinScrolled' win_id 'let g:matched = v:true' + let g:scrolled = 0 + au WinScrolled * let g:scrolled += 1 + au WinScrolled * let g:amatch = str2nr(expand('')) + au WinScrolled * let g:afile = str2nr(expand('')) + END + call writefile(lines, 'Xtest_winscrolled') + let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) + + call term_sendkeys(buf, ":echo g:scrolled\") + call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000) + + " Scroll left/right in Normal mode. + call term_sendkeys(buf, "zlzh:echo g:scrolled\") + call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) + + " Scroll up/down in Normal mode. + call term_sendkeys(buf, "\\:echo g:scrolled\") + call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000) + + " Scroll up/down in Insert mode. + call term_sendkeys(buf, "Mi\\\i\\\") + call term_sendkeys(buf, ":echo g:scrolled\") + call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000) + + " Scroll the window horizontally to focus the last letter of the third line + " containing only six characters. Moving to the previous and shorter lines + " should trigger another autocommand as Vim has to make them visible. + call term_sendkeys(buf, "5zl2k") + call term_sendkeys(buf, ":echo g:scrolled\") + call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000) + + " Ensure the command was triggered for the specified window ID. + call term_sendkeys(buf, ":echo g:matched\") + call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) + + " Ensure the expansion of and matches the window ID. + call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\") + call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) + + call StopVimInTerminal(buf) + call delete('Xtest_winscrolled') +endfunc + func Test_WinClosed() " Test that the pattern is matched against the closed window's ID, and both " and are set to it. diff --git a/src/nvim/window.c b/src/nvim/window.c index 9aa2f947cb..08cdfa4ebe 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2851,7 +2851,7 @@ static void do_autocmd_winclosed(win_T *win) } recursive = true; char_u winid[NUMBUFLEN]; - vim_snprintf((char *)winid, sizeof(winid), "%i", win->handle); + vim_snprintf((char *)winid, sizeof(winid), "%d", win->handle); apply_autocmds(EVENT_WINCLOSED, winid, winid, false, win->w_buffer); recursive = false; } @@ -5246,25 +5246,31 @@ void shell_new_columns(void) win_reconfig_floats(); // The size of floats might change } -/// Check if "wp" has scrolled since last time it was checked -/// @param wp the window to check -bool win_did_scroll(win_T *wp) +/// Trigger WinScrolled autocmd if window has scrolled. +void may_trigger_winscrolled(win_T *wp) { - return (curwin->w_last_topline != curwin->w_topline - || curwin->w_last_leftcol != curwin->w_leftcol - || curwin->w_last_width != curwin->w_width - || curwin->w_last_height != curwin->w_height); -} + static bool recursive = false; -/// Trigger WinScrolled autocmd -void do_autocmd_winscrolled(win_T *wp) -{ - apply_autocmds(EVENT_WINSCROLLED, NULL, NULL, false, curbuf); + if (recursive || !has_event(EVENT_WINSCROLLED)) { + return; + } + + if (wp->w_last_topline != wp->w_topline + || wp->w_last_leftcol != wp->w_leftcol + || wp->w_last_width != wp->w_width + || wp->w_last_height != wp->w_height) { + char_u winid[NUMBUFLEN]; + vim_snprintf((char *)winid, sizeof(winid), "%d", wp->handle); - wp->w_last_topline = wp->w_topline; - wp->w_last_leftcol = wp->w_leftcol; - wp->w_last_width = wp->w_width; - wp->w_last_height = wp->w_height; + recursive = true; + apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer); + recursive = false; + + wp->w_last_topline = wp->w_topline; + wp->w_last_leftcol = wp->w_leftcol; + wp->w_last_width = wp->w_width; + wp->w_last_height = wp->w_height; + } } /* -- cgit From 7e1e906738ae8cf2d38ded4be3bef50d72b7c2a7 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Apr 2022 19:16:47 +0800 Subject: vim-patch:8.2.4739: accessing freed memory after WinScrolled autocmd event (#18090) Problem: Accessing freed memory after WinScrolled autocmd event. Solution: Check the window pointer is still valid. (closes vim/vim#10156) Remove the argument from may_trigger_winscrolled(). https://github.com/vim/vim/commit/d58862d18f091d3c14fa3647e724ef7eea1ecefa --- src/nvim/edit.c | 2 +- src/nvim/normal.c | 2 +- src/nvim/testdir/test_autocmd.vim | 46 +++++++++++++++++++++++++++++---------- src/nvim/window.c | 16 +++++++++----- 4 files changed, 47 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 8b25378531..313e23fd3b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1545,7 +1545,7 @@ static void ins_redraw(bool ready) if (ready) { // Trigger Scroll if viewport changed. - may_trigger_winscrolled(curwin); + may_trigger_winscrolled(); } // Trigger BufModified if b_changed_invalid is set. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 5b3f27529c..a8769c4c80 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1225,7 +1225,7 @@ static void normal_check_window_scrolled(NormalState *s) { if (!finish_op) { // Trigger Scroll if the viewport changed. - may_trigger_winscrolled(curwin); + may_trigger_winscrolled(); } } diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 4b4f6ad3d3..228145ec4d 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -265,17 +265,17 @@ func Test_WinScrolled() CheckRunVimInTerminal let lines =<< trim END - set nowrap scrolloff=0 - for ii in range(1, 18) - call setline(ii, repeat(nr2char(96 + ii), ii * 2)) - endfor - let win_id = win_getid() - let g:matched = v:false - execute 'au WinScrolled' win_id 'let g:matched = v:true' - let g:scrolled = 0 - au WinScrolled * let g:scrolled += 1 - au WinScrolled * let g:amatch = str2nr(expand('')) - au WinScrolled * let g:afile = str2nr(expand('')) + set nowrap scrolloff=0 + for ii in range(1, 18) + call setline(ii, repeat(nr2char(96 + ii), ii * 2)) + endfor + let win_id = win_getid() + let g:matched = v:false + execute 'au WinScrolled' win_id 'let g:matched = v:true' + let g:scrolled = 0 + au WinScrolled * let g:scrolled += 1 + au WinScrolled * let g:amatch = str2nr(expand('')) + au WinScrolled * let g:afile = str2nr(expand('')) END call writefile(lines, 'Xtest_winscrolled') let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) @@ -315,6 +315,30 @@ func Test_WinScrolled() call delete('Xtest_winscrolled') endfunc +func Test_WinScrolled_close_curwin() + CheckRunVimInTerminal + + let lines =<< trim END + set nowrap scrolloff=0 + call setline(1, ['aaa', 'bbb']) + vsplit + au WinScrolled * close + au VimLeave * call writefile(['123456'], 'Xtestout') + END + call writefile(lines, 'Xtest_winscrolled_close_curwin') + let buf = RunVimInTerminal('-S Xtest_winscrolled_close_curwin', {'rows': 6}) + + " This was using freed memory + call term_sendkeys(buf, "\") + call TermWait(buf) + call StopVimInTerminal(buf) + + call assert_equal(['123456'], readfile('Xtestout')) + + call delete('Xtest_winscrolled_close_curwin') + call delete('Xtestout') +endfunc + func Test_WinClosed() " Test that the pattern is matched against the closed window's ID, and both " and are set to it. diff --git a/src/nvim/window.c b/src/nvim/window.c index 08cdfa4ebe..2ca5128445 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5246,8 +5246,8 @@ void shell_new_columns(void) win_reconfig_floats(); // The size of floats might change } -/// Trigger WinScrolled autocmd if window has scrolled. -void may_trigger_winscrolled(win_T *wp) +/// Trigger WinScrolled for "curwin" if needed. +void may_trigger_winscrolled(void) { static bool recursive = false; @@ -5255,6 +5255,7 @@ void may_trigger_winscrolled(win_T *wp) return; } + win_T *wp = curwin; if (wp->w_last_topline != wp->w_topline || wp->w_last_leftcol != wp->w_leftcol || wp->w_last_width != wp->w_width @@ -5266,10 +5267,13 @@ void may_trigger_winscrolled(win_T *wp) apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer); recursive = false; - wp->w_last_topline = wp->w_topline; - wp->w_last_leftcol = wp->w_leftcol; - wp->w_last_width = wp->w_width; - wp->w_last_height = wp->w_height; + // an autocmd may close the window, "wp" may be invalid now + if (win_valid_any_tab(wp)) { + wp->w_last_topline = wp->w_topline; + wp->w_last_leftcol = wp->w_leftcol; + wp->w_last_width = wp->w_width; + wp->w_last_height = wp->w_height; + } } } -- cgit From cbc54cf484d1082e8c09b955e86aff4579ad8f8a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 15:23:17 +0800 Subject: vim-patch:8.2.3184: cannot add a digraph with a leading space Problem: Cannot add a digraph with a leading space. It is not easy to list existing digraphs. Solution: Add setdigraph(), setdigraphlist(), getdigraph() and getdigraphlist(). (closes vim/vim#8580) https://github.com/vim/vim/commit/6106504e9edc8500131f7a36e59bc146f90180fa Use GA_APPEND_VIA_PTR in registerdigraph(). Use tv_list_append_*() in getdigraphlist_appendpair(). Put the error messages in digraph.c. E196 is N/A. Remove mentions about 'encoding' being non-Unicode. Nvim doesn't support setting encoding=japan, so skip a test. --- src/nvim/digraph.c | 257 ++++++++++++++++++++++++++++++++++---- src/nvim/digraph.h | 1 + src/nvim/eval.lua | 4 + src/nvim/eval/funcs.c | 1 + src/nvim/testdir/test_digraph.vim | 80 +++++++++++- 5 files changed, 317 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 6e5389c979..4046869871 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -12,6 +12,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/digraph.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -34,6 +35,12 @@ typedef struct digraph { result_T result; } digr_T; +static char e_digraph_must_be_just_two_characters_str[] + = N_("E1214: Digraph must be just two characters: %s"); +static char e_digraph_argument_must_be_one_character_str[] + = N_("E1215: Digraph must be one character: %s"); +static char e_setdigraphlist_argument_must_be_list_of_lists_with_two_items[] + = N_("E1216: setdigraphlist() argument must be a list of lists with two items"); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "digraph.c.generated.h" @@ -1607,6 +1614,44 @@ int getdigraph(int char1, int char2, bool meta_char) return retval; } +/// Add a digraph to the digraph table. +static void registerdigraph(int char1, int char2, int n) +{ + // If the digraph already exists, replace "result". + digr_T *dp = (digr_T *)user_digraphs.ga_data; + for (int i = 0; i < user_digraphs.ga_len; i++) { + if ((int)dp->char1 == char1 && (int)dp->char2 == char2) { + dp->result = n; + return; + } + dp++; + } + + // Add a new digraph to the table. + dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs); + dp->char1 = (char_u)char1; + dp->char2 = (char_u)char2; + dp->result = n; +} + +/// Check the characters are valid for a digraph. +/// If they are valid, returns true; otherwise, give an error message and +/// returns false. +bool check_digraph_chars_valid(int char1, int char2) +{ + if (char2 == 0) { + char_u msg[MB_MAXBYTES + 1]; + msg[utf_char2bytes(char1, msg)] = NUL; + semsg(_(e_digraph_must_be_just_two_characters_str), msg); + return false; + } + if (char1 == ESC || char2 == ESC) { + emsg(_("E104: Escape not allowed in digraph")); + return false; + } + return true; +} + /// Add the digraphs in the argument to the digraph table. /// format: {c1}{c2} char {c1}{c2} char ... /// @@ -1622,15 +1667,10 @@ void putdigraph(char_u *str) char_u char1 = *str++; char_u char2 = *str++; - if (char2 == 0) { - emsg(_(e_invarg)); + if (!check_digraph_chars_valid(char1, char2)) { return; } - if ((char1 == ESC) || (char2 == ESC)) { - emsg(_("E104: Escape not allowed in digraph")); - return; - } str = skipwhite(str); if (!ascii_isdigit(*str)) { @@ -1639,25 +1679,7 @@ void putdigraph(char_u *str) } int n = getdigits_int(&str, true, 0); - // If the digraph already exists, replace the result. - digr_T *dp = (digr_T *)user_digraphs.ga_data; - - int i; - for (i = 0; i < user_digraphs.ga_len; i++) { - if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { - dp->result = n; - break; - } - dp++; - } - - // Add a new digraph to the table. - if (i == user_digraphs.ga_len) { - dp = GA_APPEND_VIA_PTR(digr_T, &user_digraphs); - dp->char1 = char1; - dp->char2 = char2; - dp->result = n; - } + registerdigraph(char1, char2, n); } } @@ -1707,6 +1729,50 @@ void listdigraphs(bool use_headers) } } +static void getdigraphlist_appendpair(digr_T *dp, list_T *l) +{ + list_T *l2 = tv_list_alloc(2); + tv_list_append_list(l, l2); + + char_u buf[30]; + buf[0] = dp->char1; + buf[1] = dp->char2; + buf[2] = NUL; + tv_list_append_string(l2, (char *)buf, -1); + + char_u *p = buf; + p += utf_char2bytes(dp->result, p); + *p = NUL; + tv_list_append_string(l2, (char *)buf, -1); +} + +void getdigraphlist_common(bool list_all, typval_T *rettv) +{ + tv_list_alloc_ret(rettv, (int)sizeof(digraphdefault) + user_digraphs.ga_len); + + digr_T *dp; + + if (list_all) { + dp = digraphdefault; + for (int i = 0; dp->char1 != NUL && !got_int; i++) { + digr_T tmp; + tmp.char1 = dp->char1; + tmp.char2 = dp->char2; + tmp.result = getexactdigraph(tmp.char1, tmp.char2, false); + if (tmp.result != 0 && tmp.result != tmp.char2) { + getdigraphlist_appendpair(&tmp, rettv->vval.v_list); + } + dp++; + } + } + + dp = (digr_T *)user_digraphs.ga_data; + for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) { + getdigraphlist_appendpair(dp, rettv->vval.v_list); + dp++; + } +} + struct dg_header_entry { int dg_start; const char *dg_header; @@ -1797,6 +1863,147 @@ static void printdigraph(const digr_T *dp, result_T *previous) } } +/// Get the two digraph characters from a typval. +/// @return OK or FAIL. +static int get_digraph_chars(typval_T *arg, int *char1, int *char2) +{ + char buf_chars[NUMBUFLEN]; + const char *chars = tv_get_string_buf_chk(arg, buf_chars); + const char_u *p = (const char_u *)chars; + + if (p != NULL) { + if (*p != NUL) { + *char1 = mb_cptr2char_adv(&p); + if (*p != NUL) { + *char2 = mb_cptr2char_adv(&p); + if (*p == NUL) { + if (check_digraph_chars_valid(*char1, *char2)) { + return OK; + } + return FAIL; + } + } + } + } + semsg(_(e_digraph_must_be_just_two_characters_str), chars); + return FAIL; +} + +static bool setdigraph_common(typval_T *argchars, typval_T *argdigraph) +{ + int char1, char2; + if (get_digraph_chars(argchars, &char1, &char2) == FAIL) { + return false; + } + + char buf_digraph[NUMBUFLEN]; + const char *digraph = tv_get_string_buf_chk(argdigraph, buf_digraph); + if (digraph == NULL) { + return false; + } + const char_u *p = (const char_u *)digraph; + int n = mb_cptr2char_adv(&p); + if (*p != NUL) { + semsg(_(e_digraph_argument_must_be_one_character_str), digraph); + return false; + } + + registerdigraph(char1, char2, n); + return true; +} + +/// "getdigraph()" function +void f_getdigraph(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; // Return empty string for failure + const char *digraphs = tv_get_string_chk(&argvars[0]); + + if (digraphs == NULL) { + return; + } + if (STRLEN(digraphs) != 2) { + semsg(_(e_digraph_must_be_just_two_characters_str), digraphs); + return; + } + int code = getdigraph(digraphs[0], digraphs[1], false); + + char_u buf[NUMBUFLEN]; + buf[utf_char2bytes(code, buf)] = NUL; + rettv->vval.v_string = vim_strsave(buf); +} + +/// "getdigraphlist()" function +void f_getdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + bool flag_list_all; + + if (argvars[0].v_type == VAR_UNKNOWN) { + flag_list_all = false; + } else { + bool error = false; + varnumber_T flag = tv_get_number_chk(&argvars[0], &error); + if (error) { + return; + } + flag_list_all = flag != 0; + } + + getdigraphlist_common(flag_list_all, rettv); +} + +/// "setdigraph()" function +void f_setdigraph(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_BOOL; + rettv->vval.v_bool = kBoolVarFalse; + + if (!setdigraph_common(&argvars[0], &argvars[1])) { + return; + } + + rettv->vval.v_bool = kBoolVarTrue; +} + +/// "setdigraphlist()" function +void f_setdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_BOOL; + rettv->vval.v_bool = kBoolVarFalse; + + if (argvars[0].v_type != VAR_LIST) { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + list_T *pl = argvars[0].vval.v_list; + if (pl == NULL) { + // Empty list always results in success. + rettv->vval.v_bool = kBoolVarTrue; + return; + } + + TV_LIST_ITER_CONST(pl, pli, { + if (TV_LIST_ITEM_TV(pli)->v_type != VAR_LIST) { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + list_T *l = TV_LIST_ITEM_TV(pli)->vval.v_list; + if (l == NULL || tv_list_len(l) != 2) { + emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + return; + } + + if (!setdigraph_common(TV_LIST_ITEM_TV(tv_list_first(l)), + TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))))) { + return; + } + }); + + rettv->vval.v_bool = kBoolVarTrue; +} + /// structure used for b_kmap_ga.ga_data typedef struct { char_u *from; diff --git a/src/nvim/digraph.h b/src/nvim/digraph.h index 71330ae9b1..80b4148213 100644 --- a/src/nvim/digraph.h +++ b/src/nvim/digraph.h @@ -2,6 +2,7 @@ #define NVIM_DIGRAPH_H #include "nvim/ex_cmds_defs.h" +#include "nvim/eval/funcs.h" #include "nvim/types.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 05e91a658f..0951cd239d 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -156,6 +156,8 @@ return { getcurpos={args={0, 1}, base=1}, getcursorcharpos={args={0, 1}, base=1}, getcwd={args={0, 2}, base=1}, + getdigraph={args=1, base=1}, + getdigraphlist={args={0, 1}, base=1}, getenv={args=1, base=1}, getfontname={args={0, 1}}, getfperm={args=1, base=1}, @@ -322,6 +324,8 @@ return { setcharsearch={args=1, base=1}, setcmdpos={args=1, base=1}, setcursorcharpos={args={1, 3}, base=1}, + setdigraph={args=2, base=1}, + setdigraphlist={args=1, base=1}, setenv={args=2, base=2}, setfperm={args=2, base=1}, setline={args=2, base=2}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index d365e075e6..609db3990b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -15,6 +15,7 @@ #include "nvim/charset.h" #include "nvim/context.h" #include "nvim/cursor.h" +#include "nvim/digraph.h" #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 5965ee48ef..e9073db649 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -212,7 +212,7 @@ func Test_digraphs() call Put_Dig("el") call assert_equal(['â€', 'ü', '∞', 'l'], getline(line('.')-3,line('.'))) call assert_fails('digraph xy z', 'E39:') - call assert_fails('digraph x', 'E474:') + call assert_fails('digraph x', 'E1214:') bw! endfunc @@ -505,4 +505,82 @@ func Test_entering_digraph() call StopVimInTerminal(buf) endfunc +func Test_setdigraph_function() + new + call setdigraph('aa', 'ã‚') + call Put_Dig('aa') + call assert_equal('ã‚', getline('$')) + call setdigraph(' i', 'ã„') + call Put_Dig(' i') + call assert_equal('ã„', getline('$')) + call setdigraph(' ', 'ã†') + call Put_Dig(' ') + call assert_equal('ã†', getline('$')) + + eval 'aa'->setdigraph('ãˆ') + call Put_Dig('aa') + call assert_equal('ãˆ', getline('$')) + + call assert_fails('call setdigraph("aaa", "ã‚")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call setdigraph("b", "ã‚")', 'E1214: Digraph must be just two characters: b') + call assert_fails('call setdigraph("ã‚", "ã‚")', 'E1214: Digraph must be just two characters: ã‚') + call assert_fails('call setdigraph("aa", "ã‚ã‚")', 'E1215: Digraph must be one character: ã‚ã‚') + call assert_fails('call setdigraph("aa", "ã‹" .. nr2char(0x3099))', 'E1215: Digraph must be one character: ã‹' .. nr2char(0x3099)) + bwipe! +endfunc + +func Test_getdigraph_function() + " Built-in digraphs + call assert_equal('∞', getdigraph('00')) + + " User-defined digraphs + call setdigraph('aa', 'ã‚') + call setdigraph(' i', 'ã„') + call setdigraph(' ', 'ã†') + call assert_equal('ã‚', getdigraph('aa')) + call assert_equal('ã‚', 'aa'->getdigraph()) + call assert_equal('ã„', getdigraph(' i')) + call assert_equal('ã†', getdigraph(' ')) + call assert_fails('call getdigraph("aaa")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call getdigraph("b")', 'E1214: Digraph must be just two characters: b') +endfunc + +func Test_getdigraph_function_encode() + throw 'Skipped: Nvim does not support setting encoding=japan' + CheckFeature iconv + let testcases = { + \'00': '∞', + \'aa': 'ã‚', + \} + for [key, ch] in items(testcases) + call setdigraph(key, ch) + set encoding=japan + call assert_equal(iconv(ch, 'utf-8', 'japan'), getdigraph(key)) + set encoding& + endfor +endfunc + +func Test_setdigraphlist_function() + call setdigraphlist([['aa', 'ã'], ['bb', 'ã']]) + call assert_equal('ã', getdigraph('aa')) + call assert_equal('ã', getdigraph('bb')) + + call assert_fails('call setdigraphlist([[]])', 'E1216:') + call assert_fails('call setdigraphlist([["aa", "b", "cc"]])', '1216:') + call assert_fails('call setdigraphlist([["ã‚", "ã‚"]])', 'E1214: Digraph must be just two characters: ã‚') +endfunc + +func Test_getdigraphlist_function() + " Make sure user-defined digraphs are defined + call setdigraphlist([['aa', 'ã'], ['bb', 'ã']]) + + for pair in getdigraphlist(1) + call assert_equal(getdigraph(pair[0]), pair[1]) + endfor + + " We don't know how many digraphs are registered before, so check the number + " of digraphs returned. + call assert_equal(getdigraphlist()->len(), getdigraphlist(0)->len()) + call assert_notequal((getdigraphlist()->len()), getdigraphlist(1)->len()) +endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 3b0bcb8ad0816e363cdaa7565f0d9ed0702d1d4e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 18:42:48 +0800 Subject: vim-patch:8.2.3226: new digraph functions use old naming scheme Problem: New digraph functions use old naming scheme. Solution: Use the digraph_ prefix. (Hirohito Higashi, closes vim/vim#8580) https://github.com/vim/vim/commit/29b857150c111a455f1a38a8f748243524f692e1 --- src/nvim/digraph.c | 52 +++++++++++++-------------- src/nvim/edit.c | 2 +- src/nvim/eval.lua | 8 ++--- src/nvim/testdir/test_digraph.vim | 74 +++++++++++++++++++-------------------- 4 files changed, 68 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 4046869871..aaf3ace861 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -39,8 +39,8 @@ static char e_digraph_must_be_just_two_characters_str[] = N_("E1214: Digraph must be just two characters: %s"); static char e_digraph_argument_must_be_one_character_str[] = N_("E1215: Digraph must be one character: %s"); -static char e_setdigraphlist_argument_must_be_list_of_lists_with_two_items[] - = N_("E1216: setdigraphlist() argument must be a list of lists with two items"); +static char e_digraph_setlist_argument_must_be_list_of_lists_with_two_items[] + = N_("E1216: digraph_setlist() argument must be a list of lists with two items"); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "digraph.c.generated.h" @@ -1465,7 +1465,7 @@ int do_digraph(int c) backspaced = -1; } else if (p_dg) { if (backspaced >= 0) { - c = getdigraph(backspaced, c, false); + c = digraph_get(backspaced, c, false); } backspaced = -1; @@ -1537,7 +1537,7 @@ int get_digraph(bool cmdline) if (cc != ESC) { // ESC cancels CTRL-K - return getdigraph(c, cc, true); + return digraph_get(c, cc, true); } } return NUL; @@ -1601,7 +1601,7 @@ static int getexactdigraph(int char1, int char2, bool meta_char) /// @param meta_char /// /// @return The digraph. -int getdigraph(int char1, int char2, bool meta_char) +int digraph_get(int char1, int char2, bool meta_char) { int retval; @@ -1729,7 +1729,7 @@ void listdigraphs(bool use_headers) } } -static void getdigraphlist_appendpair(digr_T *dp, list_T *l) +static void digraph_getlist_appendpair(digr_T *dp, list_T *l) { list_T *l2 = tv_list_alloc(2); tv_list_append_list(l, l2); @@ -1746,7 +1746,7 @@ static void getdigraphlist_appendpair(digr_T *dp, list_T *l) tv_list_append_string(l2, (char *)buf, -1); } -void getdigraphlist_common(bool list_all, typval_T *rettv) +void digraph_getlist_common(bool list_all, typval_T *rettv) { tv_list_alloc_ret(rettv, (int)sizeof(digraphdefault) + user_digraphs.ga_len); @@ -1760,7 +1760,7 @@ void getdigraphlist_common(bool list_all, typval_T *rettv) tmp.char2 = dp->char2; tmp.result = getexactdigraph(tmp.char1, tmp.char2, false); if (tmp.result != 0 && tmp.result != tmp.char2) { - getdigraphlist_appendpair(&tmp, rettv->vval.v_list); + digraph_getlist_appendpair(&tmp, rettv->vval.v_list); } dp++; } @@ -1768,7 +1768,7 @@ void getdigraphlist_common(bool list_all, typval_T *rettv) dp = (digr_T *)user_digraphs.ga_data; for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) { - getdigraphlist_appendpair(dp, rettv->vval.v_list); + digraph_getlist_appendpair(dp, rettv->vval.v_list); dp++; } } @@ -1889,7 +1889,7 @@ static int get_digraph_chars(typval_T *arg, int *char1, int *char2) return FAIL; } -static bool setdigraph_common(typval_T *argchars, typval_T *argdigraph) +static bool digraph_set_common(typval_T *argchars, typval_T *argdigraph) { int char1, char2; if (get_digraph_chars(argchars, &char1, &char2) == FAIL) { @@ -1912,8 +1912,8 @@ static bool setdigraph_common(typval_T *argchars, typval_T *argdigraph) return true; } -/// "getdigraph()" function -void f_getdigraph(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "digraph_get()" function +void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; // Return empty string for failure @@ -1926,15 +1926,15 @@ void f_getdigraph(typval_T *argvars, typval_T *rettv, FunPtr fptr) semsg(_(e_digraph_must_be_just_two_characters_str), digraphs); return; } - int code = getdigraph(digraphs[0], digraphs[1], false); + int code = digraph_get(digraphs[0], digraphs[1], false); char_u buf[NUMBUFLEN]; buf[utf_char2bytes(code, buf)] = NUL; rettv->vval.v_string = vim_strsave(buf); } -/// "getdigraphlist()" function -void f_getdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "digraph_getlist()" function +void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool flag_list_all; @@ -1949,30 +1949,30 @@ void f_getdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) flag_list_all = flag != 0; } - getdigraphlist_common(flag_list_all, rettv); + digraph_getlist_common(flag_list_all, rettv); } -/// "setdigraph()" function -void f_setdigraph(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "digraph_set()" function +void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_BOOL; rettv->vval.v_bool = kBoolVarFalse; - if (!setdigraph_common(&argvars[0], &argvars[1])) { + if (!digraph_set_common(&argvars[0], &argvars[1])) { return; } rettv->vval.v_bool = kBoolVarTrue; } -/// "setdigraphlist()" function -void f_setdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "digraph_setlist()" function +void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_BOOL; rettv->vval.v_bool = kBoolVarFalse; if (argvars[0].v_type != VAR_LIST) { - emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items)); return; } @@ -1985,18 +1985,18 @@ void f_setdigraphlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) TV_LIST_ITER_CONST(pl, pli, { if (TV_LIST_ITEM_TV(pli)->v_type != VAR_LIST) { - emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items)); return; } list_T *l = TV_LIST_ITEM_TV(pli)->vval.v_list; if (l == NULL || tv_list_len(l) != 2) { - emsg(_(e_setdigraphlist_argument_must_be_list_of_lists_with_two_items)); + emsg(_(e_digraph_setlist_argument_must_be_list_of_lists_with_two_items)); return; } - if (!setdigraph_common(TV_LIST_ITEM_TV(tv_list_first(l)), - TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))))) { + if (!digraph_set_common(TV_LIST_ITEM_TV(tv_list_first(l)), + TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))))) { return; } }); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 313e23fd3b..47d491033b 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -9260,7 +9260,7 @@ static int ins_digraph(void) } if (cc != ESC) { AppendToRedobuff(CTRL_V_STR); - c = getdigraph(c, cc, true); + c = digraph_get(c, cc, true); clear_showcmd(); return c; } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0951cd239d..698cffe2fa 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -102,6 +102,10 @@ return { did_filetype={}, diff_filler={args=1, base=1}, diff_hlID={args=2, base=1}, + digraph_get={args=1, base=1}, + digraph_getlist={args={0, 1}, base=1}, + digraph_set={args=2, base=1}, + digraph_setlist={args=1, base=1}, empty={args=1, base=1}, environ={}, escape={args=2, base=1}, @@ -156,8 +160,6 @@ return { getcurpos={args={0, 1}, base=1}, getcursorcharpos={args={0, 1}, base=1}, getcwd={args={0, 2}, base=1}, - getdigraph={args=1, base=1}, - getdigraphlist={args={0, 1}, base=1}, getenv={args=1, base=1}, getfontname={args={0, 1}}, getfperm={args=1, base=1}, @@ -324,8 +326,6 @@ return { setcharsearch={args=1, base=1}, setcmdpos={args=1, base=1}, setcursorcharpos={args={1, 3}, base=1}, - setdigraph={args=2, base=1}, - setdigraphlist={args=1, base=1}, setenv={args=2, base=2}, setfperm={args=2, base=1}, setline={args=2, base=2}, diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index e9073db649..9fecedfd5b 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -505,47 +505,47 @@ func Test_entering_digraph() call StopVimInTerminal(buf) endfunc -func Test_setdigraph_function() +func Test_digraph_set_function() new - call setdigraph('aa', 'ã‚') + call digraph_set('aa', 'ã‚') call Put_Dig('aa') call assert_equal('ã‚', getline('$')) - call setdigraph(' i', 'ã„') + call digraph_set(' i', 'ã„') call Put_Dig(' i') call assert_equal('ã„', getline('$')) - call setdigraph(' ', 'ã†') + call digraph_set(' ', 'ã†') call Put_Dig(' ') call assert_equal('ã†', getline('$')) - eval 'aa'->setdigraph('ãˆ') + eval 'aa'->digraph_set('ãˆ') call Put_Dig('aa') call assert_equal('ãˆ', getline('$')) - call assert_fails('call setdigraph("aaa", "ã‚")', 'E1214: Digraph must be just two characters: aaa') - call assert_fails('call setdigraph("b", "ã‚")', 'E1214: Digraph must be just two characters: b') - call assert_fails('call setdigraph("ã‚", "ã‚")', 'E1214: Digraph must be just two characters: ã‚') - call assert_fails('call setdigraph("aa", "ã‚ã‚")', 'E1215: Digraph must be one character: ã‚ã‚') - call assert_fails('call setdigraph("aa", "ã‹" .. nr2char(0x3099))', 'E1215: Digraph must be one character: ã‹' .. nr2char(0x3099)) + call assert_fails('call digraph_set("aaa", "ã‚")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call digraph_set("b", "ã‚")', 'E1214: Digraph must be just two characters: b') + call assert_fails('call digraph_set("ã‚", "ã‚")', 'E1214: Digraph must be just two characters: ã‚') + call assert_fails('call digraph_set("aa", "ã‚ã‚")', 'E1215: Digraph must be one character: ã‚ã‚') + call assert_fails('call digraph_set("aa", "ã‹" .. nr2char(0x3099))', 'E1215: Digraph must be one character: ã‹' .. nr2char(0x3099)) bwipe! endfunc -func Test_getdigraph_function() +func Test_digraph_get_function() " Built-in digraphs - call assert_equal('∞', getdigraph('00')) + call assert_equal('∞', digraph_get('00')) " User-defined digraphs - call setdigraph('aa', 'ã‚') - call setdigraph(' i', 'ã„') - call setdigraph(' ', 'ã†') - call assert_equal('ã‚', getdigraph('aa')) - call assert_equal('ã‚', 'aa'->getdigraph()) - call assert_equal('ã„', getdigraph(' i')) - call assert_equal('ã†', getdigraph(' ')) - call assert_fails('call getdigraph("aaa")', 'E1214: Digraph must be just two characters: aaa') - call assert_fails('call getdigraph("b")', 'E1214: Digraph must be just two characters: b') + call digraph_set('aa', 'ã‚') + call digraph_set(' i', 'ã„') + call digraph_set(' ', 'ã†') + call assert_equal('ã‚', digraph_get('aa')) + call assert_equal('ã‚', 'aa'->digraph_get()) + call assert_equal('ã„', digraph_get(' i')) + call assert_equal('ã†', digraph_get(' ')) + call assert_fails('call digraph_get("aaa")', 'E1214: Digraph must be just two characters: aaa') + call assert_fails('call digraph_get("b")', 'E1214: Digraph must be just two characters: b') endfunc -func Test_getdigraph_function_encode() +func Test_digraph_get_function_encode() throw 'Skipped: Nvim does not support setting encoding=japan' CheckFeature iconv let testcases = { @@ -553,34 +553,34 @@ func Test_getdigraph_function_encode() \'aa': 'ã‚', \} for [key, ch] in items(testcases) - call setdigraph(key, ch) + call digraph_set(key, ch) set encoding=japan - call assert_equal(iconv(ch, 'utf-8', 'japan'), getdigraph(key)) + call assert_equal(iconv(ch, 'utf-8', 'japan'), digraph_get(key)) set encoding& endfor endfunc -func Test_setdigraphlist_function() - call setdigraphlist([['aa', 'ã'], ['bb', 'ã']]) - call assert_equal('ã', getdigraph('aa')) - call assert_equal('ã', getdigraph('bb')) +func Test_digraph_setlist_function() + call digraph_setlist([['aa', 'ã'], ['bb', 'ã']]) + call assert_equal('ã', digraph_get('aa')) + call assert_equal('ã', digraph_get('bb')) - call assert_fails('call setdigraphlist([[]])', 'E1216:') - call assert_fails('call setdigraphlist([["aa", "b", "cc"]])', '1216:') - call assert_fails('call setdigraphlist([["ã‚", "ã‚"]])', 'E1214: Digraph must be just two characters: ã‚') + call assert_fails('call digraph_setlist([[]])', 'E1216:') + call assert_fails('call digraph_setlist([["aa", "b", "cc"]])', '1216:') + call assert_fails('call digraph_setlist([["ã‚", "ã‚"]])', 'E1214: Digraph must be just two characters: ã‚') endfunc -func Test_getdigraphlist_function() +func Test_digraph_getlist_function() " Make sure user-defined digraphs are defined - call setdigraphlist([['aa', 'ã'], ['bb', 'ã']]) + call digraph_setlist([['aa', 'ã'], ['bb', 'ã']]) - for pair in getdigraphlist(1) - call assert_equal(getdigraph(pair[0]), pair[1]) + for pair in digraph_getlist(1) + call assert_equal(digraph_get(pair[0]), pair[1]) endfor " We don't know how many digraphs are registered before, so check the number " of digraphs returned. - call assert_equal(getdigraphlist()->len(), getdigraphlist(0)->len()) - call assert_notequal((getdigraphlist()->len()), getdigraphlist(1)->len()) + call assert_equal(digraph_getlist()->len(), digraph_getlist(0)->len()) + call assert_notequal((digraph_getlist()->len()), digraph_getlist(1)->len()) endfunc " vim: shiftwidth=2 sts=2 expandtab -- cgit From 0a813ae2914bee748dd55370929214699c97805f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 17 Feb 2022 19:00:15 +0800 Subject: vim-patch:8.2.3325: digraph test fails when LC_ALL is set to "C" Problem: Digraph test fails when LC_ALL is set to "C". Solution: When restoring 'encoding' set it to "utf-8". (closes vim/vim#8742) https://github.com/vim/vim/commit/52eb372a04dfc5d5afef238c1b3c4a8e92020837 --- src/nvim/testdir/test_digraph.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index 9fecedfd5b..7b6bc940d3 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -548,6 +548,7 @@ endfunc func Test_digraph_get_function_encode() throw 'Skipped: Nvim does not support setting encoding=japan' CheckFeature iconv + let testcases = { \'00': '∞', \'aa': 'ã‚', @@ -556,7 +557,7 @@ func Test_digraph_get_function_encode() call digraph_set(key, ch) set encoding=japan call assert_equal(iconv(ch, 'utf-8', 'japan'), digraph_get(key)) - set encoding& + set encoding=utf-8 endfor endfunc @@ -583,4 +584,6 @@ func Test_digraph_getlist_function() call assert_equal(digraph_getlist()->len(), digraph_getlist(0)->len()) call assert_notequal((digraph_getlist()->len()), digraph_getlist(1)->len()) endfunc + + " vim: shiftwidth=2 sts=2 expandtab -- cgit From c9d1fcd85064120a722f17cdf28f3e3d89b456b9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 12 Apr 2022 20:36:43 +0800 Subject: refactor(digraph.c): add more const qualifiers --- src/nvim/digraph.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index aaf3ace861..083c868607 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1482,17 +1482,16 @@ int do_digraph(int c) char_u *get_digraph_for_char(int val_arg) { const int val = val_arg; - digr_T *dp; + const digr_T *dp; static char_u r[3]; for (int use_defaults = 0; use_defaults <= 1; use_defaults++) { if (use_defaults == 0) { - dp = (digr_T *)user_digraphs.ga_data; + dp = (const digr_T *)user_digraphs.ga_data; } else { dp = digraphdefault; } - for (int i = 0; - use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) { + for (int i = 0; use_defaults ? dp->char1 != NUL : i < user_digraphs.ga_len; i++) { if (dp->result == val) { r[0] = dp->char1; r[1] = dp->char2; @@ -1560,7 +1559,7 @@ static int getexactdigraph(int char1, int char2, bool meta_char) } // Search user digraphs first. - digr_T *dp = (digr_T *)user_digraphs.ga_data; + const digr_T *dp = (const digr_T *)user_digraphs.ga_data; for (int i = 0; i < user_digraphs.ga_len; i++) { if (((int)dp->char1 == char1) && ((int)dp->char2 == char2)) { retval = dp->result; @@ -1699,7 +1698,7 @@ void listdigraphs(bool use_headers) msg_putchar('\n'); - digr_T *dp = digraphdefault; + const digr_T *dp = digraphdefault; for (int i = 0; dp->char1 != NUL && !got_int; i++) { digr_T tmp; @@ -1709,15 +1708,14 @@ void listdigraphs(bool use_headers) tmp.char2 = dp->char2; tmp.result = getexactdigraph(tmp.char1, tmp.char2, false); - if ((tmp.result != 0) - && (tmp.result != tmp.char2)) { + if (tmp.result != 0 && tmp.result != tmp.char2) { printdigraph(&tmp, use_headers ? &previous : NULL); } dp++; fast_breakcheck(); } - dp = (digr_T *)user_digraphs.ga_data; + dp = (const digr_T *)user_digraphs.ga_data; for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) { if (previous >= 0 && use_headers) { digraph_header(_("Custom")); @@ -1729,7 +1727,7 @@ void listdigraphs(bool use_headers) } } -static void digraph_getlist_appendpair(digr_T *dp, list_T *l) +static void digraph_getlist_appendpair(const digr_T *dp, list_T *l) { list_T *l2 = tv_list_alloc(2); tv_list_append_list(l, l2); @@ -1750,7 +1748,7 @@ void digraph_getlist_common(bool list_all, typval_T *rettv) { tv_list_alloc_ret(rettv, (int)sizeof(digraphdefault) + user_digraphs.ga_len); - digr_T *dp; + const digr_T *dp; if (list_all) { dp = digraphdefault; @@ -1766,7 +1764,7 @@ void digraph_getlist_common(bool list_all, typval_T *rettv) } } - dp = (digr_T *)user_digraphs.ga_data; + dp = (const digr_T *)user_digraphs.ga_data; for (int i = 0; i < user_digraphs.ga_len && !got_int; i++) { digraph_getlist_appendpair(dp, rettv->vval.v_list); dp++; @@ -1865,7 +1863,7 @@ static void printdigraph(const digr_T *dp, result_T *previous) /// Get the two digraph characters from a typval. /// @return OK or FAIL. -static int get_digraph_chars(typval_T *arg, int *char1, int *char2) +static int get_digraph_chars(const typval_T *arg, int *char1, int *char2) { char buf_chars[NUMBUFLEN]; const char *chars = tv_get_string_buf_chk(arg, buf_chars); @@ -1889,7 +1887,7 @@ static int get_digraph_chars(typval_T *arg, int *char1, int *char2) return FAIL; } -static bool digraph_set_common(typval_T *argchars, typval_T *argdigraph) +static bool digraph_set_common(const typval_T *argchars, const typval_T *argdigraph) { int char1, char2; if (get_digraph_chars(argchars, &char1, &char2) == FAIL) { -- cgit From 4dc09f38ee709cadc745ac44a1cc24c4c526ecaf Mon Sep 17 00:00:00 2001 From: Anatolii Sakhnik Date: Wed, 13 Apr 2022 11:25:59 +0300 Subject: fix(translation): po file for Ukrainian (#18100) --- src/nvim/po/uk.po | 871 ++++++++++++++++++++++++------------------------------ 1 file changed, 394 insertions(+), 477 deletions(-) (limited to 'src') diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index f0ae154648..7f0fe6a197 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -14,7 +14,7 @@ msgid "" msgstr "" "Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-18 17:46+0200\n" +"POT-Creation-Date: 2022-04-13 10:28+0300\n" "PO-Revision-Date: 2020-08-23 20:19+0300\n" "Last-Translator: Ðнатолій Сахнік \n" "Language-Team: Ukrainian\n" @@ -40,18 +40,6 @@ msgstr "E936: Ðе вдалоÑÑ Ð·Ð½Ð¸Ñ‰Ð¸Ñ‚Ð¸ цю групу" msgid "W19: Deleting augroup that is still in use" msgstr "W19: Ð—Ð½Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð³Ñ€ÑƒÐ¿Ð¸, Ñка вÑе ще викориÑтовуєтьÑÑ" -#, c-format -msgid "E215: Illegal character after *: %s" -msgstr "E215: Ðедозволений Ñимвол піÑÐ»Ñ *: %s" - -#, c-format -msgid "E216: No such event: %s" -msgstr "E216: Ðемає такої події: %s" - -#, c-format -msgid "E216: No such group or event: %s" -msgstr "E216: Ðемає такої групи чи події: %s" - msgid "" "\n" "--- Autocommands ---" @@ -84,6 +72,18 @@ msgstr "ВиконуєтьÑÑ %s" msgid "autocommand %s" msgstr "автокоманда %s" +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: Ðедозволений Ñимвол піÑÐ»Ñ *: %s" + +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: Ðемає такої події: %s" + +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: Ðемає такої групи чи події: %s" + msgid "[Location List]" msgstr "[СпиÑок міÑць]" @@ -111,27 +111,6 @@ msgstr "E516: Жоден з буферів не знищено" msgid "E517: No buffers were wiped out" msgstr "E517: Жоден з буферів не витерто" -msgid "1 buffer unloaded" -msgstr "Вивантажено один буфер" - -#, c-format -msgid "%d buffers unloaded" -msgstr "Вивантажено %d буфери(ів)" - -msgid "1 buffer deleted" -msgstr "Знищено один буфер" - -#, c-format -msgid "%d buffers deleted" -msgstr "Знищено %d буфери(ів)" - -msgid "1 buffer wiped out" -msgstr "Витерто один буфер" - -#, c-format -msgid "%d buffers wiped out" -msgstr "Витерто %d буфери(ів)" - msgid "E90: Cannot unload last buffer" msgstr "E90: Ðе можу вивантажити оÑтанній буфер" @@ -147,15 +126,15 @@ msgstr "E87: Це вже оÑтанній буфер" msgid "E88: Cannot go before first buffer" msgstr "E88: Це вже найперший буфер" -#, c-format -msgid "E89: %s will be killed (add ! to override)" -msgstr "E89: «%s» буде вбито (! щоб не зважати)" - #, c-format msgid "" "E89: No write since last change for buffer % (add ! to override)" msgstr "E89: Буфер % має зміни (! щоб не зважати)" +#, c-format +msgid "E89: %s will be killed (add ! to override)" +msgstr "E89: «%s» буде вбито (! щоб не зважати)" + msgid "E948: Job still running (add ! to end the job)" msgstr "E948: Задача вÑе ще виконуєтьÑÑ (! щоб закінчити)" @@ -205,14 +184,6 @@ msgstr "[RO]" msgid "[readonly]" msgstr "[лише читати]" -#, c-format -msgid "1 line --%d%%--" -msgstr "один Ñ€Ñдок --%d%%--" - -#, c-format -msgid "% lines --%d%%--" -msgstr "% Ñ€Ñдки(ів) --%d%%--" - #, c-format msgid "line % of % --%d%%-- col " msgstr "Ñ€Ñдок % з % --%d%%-- колонка " @@ -277,6 +248,51 @@ msgstr "E548: Потрібна цифра" msgid "E549: Illegal percentage" msgstr "E549: Ðеправильний відÑоток" +msgid "Entering Debug mode. Type \"cont\" to continue." +msgstr "Режим налагодженнÑ. Щоб продовжити введіть «cont»." + +#, c-format +msgid "Oldval = \"%s\"" +msgstr "Oldval = «%s»" + +#, c-format +msgid "Newval = \"%s\"" +msgstr "Newval = «%s»" + +#, c-format +msgid "line %: %s" +msgstr "Ñ€Ñдок %: %s" + +#, c-format +msgid "cmd: %s" +msgstr "команда: %s" + +msgid "frame is zero" +msgstr "кадр нульовий" + +#, c-format +msgid "frame at highest level: %d" +msgstr "кадр на найвищому рівні: %d" + +#, c-format +msgid "Breakpoint in \"%s%s\" line %" +msgstr "Точка зупинки в «%s%s» Ñ€Ñдок %" + +#, c-format +msgid "E161: Breakpoint not found: %s" +msgstr "E161: Точку зупинки не знайдено: %s" + +msgid "No breakpoints defined" +msgstr "Ðе визначено жодної точки зупинки" + +#, c-format +msgid "%3d %s %s line %" +msgstr "%3d %s %s Ñ€Ñдок %" + +#, c-format +msgid "%3d expr %s" +msgstr "%3d вираз %s" + #, c-format msgid "E96: Cannot diff more than % buffers" msgstr "E96: Ðе можна порівнювати понад % буфери(ів)" @@ -325,6 +341,19 @@ msgstr "E103: Буфер «%s» не в режимі порівнÑннÑ" msgid "E787: Buffer changed unexpectedly" msgstr "E787: Буфер неÑподівано змінивÑÑ" +#, c-format +msgid "E1214: Digraph must be just two characters: %s" +msgstr "E1214: Диграф має бути з двох Ñимволів: %s" + +#, c-format +msgid "E1215: Digraph must be one character: %s" +msgstr "E1215: Диграф має бути одним Ñимволом: %s" + +msgid "" +"E1216: digraph_setlist() argument must be a list of lists with two items" +msgstr "" +"E1216: аргумент digraph_setlist() має бути ÑпиÑком ÑпиÑків з двох елементів" + msgid "E104: Escape not allowed in digraph" msgstr "E104: У диграфах не може міÑтитиÑÑ escape" @@ -528,13 +557,20 @@ msgstr "E461: ÐеприпуÑтима назва змінної: %s" msgid "E995: Cannot modify existing variable" msgstr "E995: Ðеможливо змінити наÑвну змінну" -msgid "E957: Invalid window number" -msgstr "E957: Ðекоректний номер вікна" +msgid "E274: No white space allowed before parenthesis" +msgstr "E274: Перед дужками не має бути пробілу" #, c-format msgid "E940: Cannot lock or unlock variable %s" msgstr "E940: Ðеможливо заблокувати чи розблокувати змінну %s" +#, c-format +msgid "E80: Error while writing: %s" +msgstr "E80: Помилка під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñу: %s" + +msgid "E1098: String, List or Blob required" +msgstr "E1098: Потрібен String, List чи Blob" + #, c-format msgid "E734: Wrong variable type for %s=" msgstr "E734: Ðеправильний тип змінної Ð´Ð»Ñ %s=" @@ -564,8 +600,8 @@ msgstr "E687: Цілей менше, ніж елементів ÑпиÑку" msgid "E688: More targets than List items" msgstr "E688: Цілей більше, ніж елементів ÑпиÑку" -msgid "Double ; in list of variables" -msgstr "Друга ; у ÑпиÑку змінних" +msgid "E452: Double ; in list of variables" +msgstr "E452: Друга ; у ÑпиÑку змінних" #, c-format msgid "E738: Can't list variables for %s" @@ -584,8 +620,8 @@ msgstr "E996: Ðеможливо заблокувати регіÑтр" msgid "E121: Undefined variable: %.*s" msgstr "E121: Ðевизначена змінна: %.*s" -msgid "E689: Can only index a List or Dictionary" -msgstr "E689: ІндекÑний доÑтуп може бути тільки до ÑпиÑку чи Ñловника" +msgid "E689: Can only index a List, Dictionary or Blob" +msgstr "E689: ІндекÑний доÑтуп може бути тільки до List, Dictionary чи Blob" msgid "E708: [:] must come last" msgstr "E708: [:] має бути оÑтанньою" @@ -593,8 +629,11 @@ msgstr "E708: [:] має бути оÑтанньою" msgid "E713: Cannot use empty key after ." msgstr "E713: Ðеможливо вжити порожній ключ піÑÐ»Ñ ." -msgid "E709: [:] requires a List value" -msgstr "E709: [:] вимагає ÑпиÑок" +msgid "E709: [:] requires a List or Blob value" +msgstr "E709: [:] вимагає List чи Blob" + +msgid "E972: Blob value does not have the right number of bytes" +msgstr "E972: неправильна кількіÑть байтів у значенні Blob" msgid "E996: Cannot lock a range" msgstr "E996: Ðеможливо заблокувати діапазон" @@ -618,27 +657,18 @@ msgstr "E108: Змінної немає: «%s»" msgid "E109: Missing ':' after '?'" msgstr "E109: Бракує ':' піÑÐ»Ñ '?'" -msgid "E691: Can only compare List with List" -msgstr "E691: СпиÑок можна порівнÑти тільки зі ÑпиÑком" - -msgid "E692: Invalid operation for List" -msgstr "E692: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ ÑпиÑком" - -msgid "E735: Can only compare Dictionary with Dictionary" -msgstr "E735: Словник можна порівнÑти тільки із Ñловником" - -msgid "E736: Invalid operation for Dictionary" -msgstr "E736: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ Ñловником" - -msgid "E694: Invalid operation for Funcrefs" -msgstr "E694: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ функцією" - msgid "E804: Cannot use '%' with Float" msgstr "E804: Ðе можна виконати '%' над Float" +msgid "E973: Blob literal should have an even number of hex characters" +msgstr "E973: Ð—Ð°Ð¿Ð¸Ñ Blob повинен мати парну кількіÑть шіÑтнадцÑткових Ñимволів" + msgid "E110: Missing ')'" msgstr "E110: Пропущено ')'" +msgid "E260: Missing name after ->" +msgstr "E260: ПіÑÐ»Ñ -> бракує імені" + msgid "E695: Cannot index a Funcref" msgstr "E695: Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ Ð½Ðµ має індекÑації" @@ -715,10 +745,6 @@ msgstr "ВиконуєтьÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°: «%s»" msgid "E921: Invalid callback argument" msgstr "E921: Ðекоректний аргумент функції зворотнього виклику" -#, c-format -msgid "E80: Error while writing: %s" -msgstr "E80: Помилка під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñу: %s" - #, c-format msgid "E963: setting %s to value with wrong type" msgstr "E963: вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %s до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· неправильним типом" @@ -759,6 +785,24 @@ msgstr "E5009: Ðекоректна $VIMRUNTIME: %s" msgid "E5009: Invalid 'runtimepath'" msgstr "E5009: Ðекоректний 'runtimepath'" +msgid "E977: Can only compare Blob with Blob" +msgstr "E977: Блоб можна порівнÑти тільки із блобом" + +msgid "E691: Can only compare List with List" +msgstr "E691: СпиÑок можна порівнÑти тільки зі ÑпиÑком" + +msgid "E692: Invalid operation for List" +msgstr "E692: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ ÑпиÑком" + +msgid "E735: Can only compare Dictionary with Dictionary" +msgstr "E735: Словник можна порівнÑти тільки із Ñловником" + +msgid "E736: Invalid operation for Dictionary" +msgstr "E736: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ Ñловником" + +msgid "E694: Invalid operation for Funcrefs" +msgstr "E694: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ функцією" + #, c-format msgid "E474: Expected comma before list item: %s" msgstr "E474: ОчікуєтьÑÑ ÐºÐ¾Ð¼Ð° перед елементом ÑпиÑка: %s" @@ -1014,8 +1058,15 @@ msgid "E684: list index out of range: %" msgstr "E684: Ð†Ð½Ð´ÐµÐºÑ ÑпиÑку поза межами: %" #, c-format -msgid "E686: Argument of %s must be a List" -msgstr "E686: Ðргумент у %s має бути ÑпиÑком" +msgid "E899: Argument of %s must be a List or Blob" +msgstr "E899: Ðргумент у %s має бути ÑпиÑком чи блобом" + +msgid "E957: Invalid window number" +msgstr "E957: Ðекоректний номер вікна" + +#, c-format +msgid "E998: Reduce of an empty %s with no initial value" +msgstr "E998: Ð¡ÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½ÑŒÐ¾Ð³Ð¾ %s без початкового значеннÑ" #, c-format msgid "Error converting the call result: %s" @@ -1081,14 +1132,6 @@ msgstr "E474: Ðе вдалоÑÑ Ñ€Ð¾Ð·Ñ–Ð±Ñ€Ð°Ñ‚Ð¸ %.*s" msgid "E701: Invalid type for len()" msgstr "E701: Ðекоректний тип Ð´Ð»Ñ len()" -#, c-format -msgid "E798: ID is reserved for \":match\": %" -msgstr "E798: ID зарезервовано Ð´Ð»Ñ \":match\": %" - -#, c-format -msgid "E798: ID is reserved for \"match\": %" -msgstr "E798: ID зарезервовано Ð´Ð»Ñ \"match\": %" - #, c-format msgid "msgpackdump() argument, index %i" msgstr "аргумент msgpackdump(), Ñ–Ð½Ð´ÐµÐºÑ %i" @@ -1126,14 +1169,6 @@ msgstr "E5010: Елемент ÑпиÑку %d другого аргументу msgid "E927: Invalid action: '%s'" msgstr "E927: Ðеправильна діÑ: «%s»" -#, c-format -msgid "E474: List item %d is either not a dictionary or an empty one" -msgstr "E474: Елемент ÑпиÑку %d або не Ñловник або порожній" - -#, c-format -msgid "E474: List item %d is missing one of the required keys" -msgstr "E474: Елемент ÑпиÑку %d немає одного з обов’Ñзкових ключів" - #, c-format msgid "E962: Invalid action: '%s'" msgstr "E962: Ðеправильна діÑ: «%s»" @@ -1168,6 +1203,9 @@ msgstr "E935: неправильний номер групи Ñпівпадін msgid "Can only call this function in an unmodified buffer" msgstr "Цю функцію можна викликати тільки у незміненому буфері" +msgid "writefile() first argument must be a List or a Blob" +msgstr "перший аргумент writefile() має бути List або Blob" + #, c-format msgid "E5060: Unknown flag: %s" msgstr "E5060: Ðевідомий прапорець: %s" @@ -1228,6 +1266,9 @@ msgstr "E745: ОчікуєтьÑÑ Number чи String, трапивÑÑ List" msgid "E728: Expected a Number or a String, Dictionary found" msgstr "E728: ОчікуєтьÑÑ Number чи String, трапивÑÑ Dictionary" +msgid "E974: Expected a Number or a String, Blob found" +msgstr "E974: ОчікуєтьÑÑ Number чи String, трапивÑÑ Blob" + msgid "E5299: Expected a Number or a String, Boolean found" msgstr "E5299: ОчікуєтьÑÑ Number чи String, трапивÑÑ Boolean" @@ -1243,6 +1284,9 @@ msgstr "E728: Dictionary вжито Ñк Number" msgid "E805: Using a Float as a Number" msgstr "E805: Float вжито Ñк Number" +msgid "E974: Using a Blob as a Number" +msgstr "E974: Blob вжито Ñк Number" + msgid "E685: using an invalid value as a Number" msgstr "E685: некоректне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¶Ð¸Ñ‚Ð¾ Ñк Number" @@ -1252,6 +1296,9 @@ msgstr "E730: List вжито Ñк String" msgid "E731: using Dictionary as a String" msgstr "E731: Dictionary вжито Ñк String" +msgid "E976: using Blob as a String" +msgstr "E976: Blob вжито Ñк String" + msgid "E908: using an invalid value as a String" msgstr "E908: некоректне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¶Ð¸Ñ‚Ð¾ Ñк String" @@ -1273,6 +1320,9 @@ msgstr "E362: ВикориÑтано логічне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñк Float" msgid "E907: Using a special value as a Float" msgstr "E907: ВикориÑтано Ñпеціальне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñк Float" +msgid "E975: Using a Blob as a Float" +msgstr "E975: Blob вжито Ñк Float" + msgid "E808: Number or Float required" msgstr "E808: Треба вказати Number чи Float" @@ -1298,6 +1348,9 @@ msgstr "E125: Ðедозволений аргумент: %s" msgid "E853: Duplicate argument name: %s" msgstr "E853: Ðазва аргументу повторюєтьÑÑ: %s" +msgid "E989: Non-default argument follows default argument" +msgstr "E989: Ðргумент без домовленого Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ñ–ÑÐ»Ñ Ð°Ñ€Ð³ÑƒÐ¼ÐµÐ½Ñ‚Ñƒ з домовленим значеннÑм" + #, c-format msgid "E740: Too many arguments for function %s" msgstr "E740: Забагато аргументів Ð´Ð»Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— %s" @@ -1336,6 +1389,10 @@ msgstr "E699: Забагато аргументів" msgid "E117: Unknown function: %s" msgstr "E117: Ðевідома функціÑ: %s" +#, c-format +msgid "E276: Cannot use function as a method: %s" +msgstr "E276: Ðе можна вжити функцію Ñк метод: %s" + #, c-format msgid "E933: Function was deleted: %s" msgstr "E933: Функцію було видалено: %s" @@ -1408,10 +1465,6 @@ msgstr "Ðе вдалоÑÑ Ð·Ð½Ð¸Ñ‰Ð¸Ñ‚Ð¸ функцію %s: Вона вико msgid "E133: :return not inside a function" msgstr "E133: :return поза межами функції" -#, c-format -msgid "E107: Missing parentheses: %s" -msgstr "E107: Пропущено дужки: %s" - msgid "tcp address must be host:port" msgstr "адреÑа tcp має бути вузол:порт" @@ -1448,13 +1501,6 @@ msgstr "> %d, шіÑÑ‚ %08x, Ð²Ñ–Ñ %o" msgid "E134: Cannot move a range of lines into itself" msgstr "E134: Ðеможливо переміÑтити діапазон Ñ€Ñдків Ñам у Ñебе" -msgid "1 line moved" -msgstr "Переміщено один Ñ€Ñдок" - -#, c-format -msgid "% lines moved" -msgstr "Переміщено % Ñ€Ñдки(ів)" - #, c-format msgid "E482: Can't create file %s" msgstr "E482: Ðе вдалоÑÑ Ñтворити файл %s" @@ -1533,27 +1579,6 @@ msgstr "Замінити на %s (y/n/a/q/l/^E/^Y)?" msgid "(Interrupted) " msgstr "(Перервано) " -msgid "1 match" -msgstr "Один збіг" - -msgid "1 substitution" -msgstr "Одна заміна" - -#, c-format -msgid "% matches" -msgstr "% збіги(ів)" - -#, c-format -msgid "% substitutions" -msgstr "% замін(и)" - -msgid " on 1 line" -msgstr " в одному Ñ€Ñдку" - -#, c-format -msgid " on % lines" -msgstr " в % Ñ€Ñдках" - msgid "E147: Cannot do :global recursive with a range" msgstr "E147: :global не можна рекурÑивно з діапазоном" @@ -1610,39 +1635,6 @@ msgstr "E150: Ðе Ñ” каталогом: %s" msgid "No old files" msgstr "Жодного Ñтарого файлу" -msgid "Entering Debug mode. Type \"cont\" to continue." -msgstr "Режим налагодженнÑ. Щоб продовжити введіть «cont»." - -#, c-format -msgid "line %: %s" -msgstr "Ñ€Ñдок %: %s" - -#, c-format -msgid "cmd: %s" -msgstr "команда: %s" - -msgid "frame is zero" -msgstr "кадр нульовий" - -#, c-format -msgid "frame at highest level: %d" -msgstr "кадр на найвищому рівні: %d" - -#, c-format -msgid "Breakpoint in \"%s%s\" line %" -msgstr "Точка зупинки в «%s%s» Ñ€Ñдок %" - -#, c-format -msgid "E161: Breakpoint not found: %s" -msgstr "E161: Точку зупинки не знайдено: %s" - -msgid "No breakpoints defined" -msgstr "Ðе визначено жодної точки зупинки" - -#, c-format -msgid "%3d %s %s line %" -msgstr "%3d %s %s Ñ€Ñдок %" - msgid "E750: First use \":profile start {fname}\"" msgstr "E750: Спочатку зробіть «:profile start {файл}»" @@ -1682,10 +1674,6 @@ msgstr "E610: Ðемає аргументів Ð´Ð»Ñ Ð·Ð½Ð¸Ñ‰ÐµÐ½Ð½Ñ" msgid "E666: compiler not supported: %s" msgstr "E666: КомпілÑтор не підтримуєтьÑÑ: %s" -#, c-format -msgid ":source error parsing command %s" -msgstr ":source помилка розбору команди %s" - #, c-format msgid "Cannot source a directory: \"%s\"" msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ каталог: «%s»" @@ -1725,6 +1713,9 @@ msgstr "змінна оточеннÑ" msgid "error handler" msgstr "обробник помилки" +msgid "changed window size" +msgstr "змінено розмір вікна" + msgid "Lua" msgstr "Lua" @@ -1735,6 +1726,10 @@ msgstr "Клієнт API (канал «%»)" msgid "anonymous :source" msgstr "анонімний :source" +#, c-format +msgid "anonymous :source (script id %d)" +msgstr "анонімний :source (ід. Ñкрипта %d)" + msgid "W15: Warning: Wrong line separator, ^M may be missing" msgstr "W15: ЗаÑтереженнÑ: Ðеправильний роздільник Ñ€Ñдків, можливо, бракує ^M" @@ -1752,6 +1747,14 @@ msgstr "Мова (%s): «%s»" msgid "E197: Cannot set language to \"%s\"" msgstr "E197: Ðе вдалоÑÑ Ð²Ñтановити мову «%s»" +#, c-format +msgid "E184: No such user-defined command: %s" +msgstr "E184: Команду кориÑтувача не знайдено: %s" + +#, c-format +msgid "E1237: No such user-defined command in current buffer: %s" +msgstr "E1237: Ðемає такої команди кориÑтувача у цьому буфері: %s" + msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." msgstr "Режим Ex. Ð”Ð»Ñ Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Ð´Ð¾ нормального режиму виконайте «visual»" @@ -1796,7 +1799,8 @@ msgstr "E494: Спробуйте w або w>>" msgid "" "INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX" msgstr "" -"Ð’ÐУТРІШÐЄ: Ðе можна вживати EX_DFLALL з ADDR_NONE, ADDR_UNSIGNED чи ADDR_QUICKFIX" +"Ð’ÐУТРІШÐЄ: Ðе можна вживати EX_DFLALL з ADDR_NONE, ADDR_UNSIGNED чи " +"ADDR_QUICKFIX" msgid "E943: Command table needs to be updated, run 'make'" msgstr "E943: Потрібно поновити таблицю команд, запуÑтіть 'make'" @@ -1804,20 +1808,6 @@ msgstr "E943: Потрібно поновити таблицю команд, з msgid "E319: The command is not available in this version" msgstr "E319: Вибачте, цієї команди немає у цій верÑÑ–Ñ—" -msgid "1 more file to edit. Quit anyway?" -msgstr "ЗалишилоÑÑ Ð²Ñ–Ð´Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ñ‚Ð¸ ще один файл. Ð’Ñе одно вийти?" - -#, c-format -msgid "%d more files to edit. Quit anyway?" -msgstr "Ще Ñ” %d не редагованих файлів. Ð’Ñе одно вийти?" - -msgid "E173: 1 more file to edit" -msgstr "E173: ЗалишилоÑÑ Ð²Ñ–Ð´Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ñ‚Ð¸ ще один файл" - -#, c-format -msgid "E173: % more files to edit" -msgstr "E173: ЗалишилоÑÑ % не редагованих файлів" - #, c-format msgid "E174: Command already exists: add ! to replace it: %s" msgstr "E174: Команда вже Ñ–Ñнує, ! щоб замінити Ñ—Ñ—: %s" @@ -1854,6 +1844,9 @@ msgstr "E179: Ð´Ð»Ñ -addr потрібний аргумент" msgid "E181: Invalid attribute: %s" msgstr "E181: Ðеправильний атрибут: %s" +msgid "E1208: -complete used without -nargs" +msgstr "E1208: -complete вжито без without -nargs" + msgid "E182: Invalid command name" msgstr "E182: Ðеправильна назва команди" @@ -1864,10 +1857,6 @@ msgid "E841: Reserved name, cannot be used for user defined command" msgstr "" "E841: Зарезервована назва, не можна викориÑтати Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувацької команди" -#, c-format -msgid "E184: No such user-defined command: %s" -msgstr "E184: Команду кориÑтувача не знайдено: %s" - #, c-format msgid "E180: Invalid address type value: %s" msgstr "E180: Ðеправильне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð¸Ð¿Ñƒ адреÑи: %s" @@ -1949,7 +1938,7 @@ msgstr "E842: немає номера Ñ€Ñдка, щоб викориÑтати msgid "E961: no line number to use for \"\"" msgstr "E961: немає номера Ñ€Ñдка, щоб викориÑтати з «»" -#, c-format +#, no-c-format msgid "E499: Empty file name for '%' or '#', only works with \":p:h\"" msgstr "E499: Ðазва файлу Ð´Ð»Ñ '%' чи '#' порожнÑ, працює лише з «:p:h»" @@ -2106,6 +2095,9 @@ msgstr " тип файлу\n" msgid "'history' option is zero" msgstr "ÐžÐ¿Ñ†Ñ–Ñ 'history' порожнÑ" +msgid "[Command Line]" +msgstr "[РÑдок Команд]" + msgid "E199: Active window or buffer deleted" msgstr "E199: Ðктивне вікно або буфер було знищено" @@ -2228,6 +2220,10 @@ msgstr "Ðе придатний Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу" msgid "is read-only (add ! to override)" msgstr "лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ (! щоб не зважати)" +#, c-format +msgid "E303: Unable to create directory \"%s\" for backup file: %s" +msgstr "E303: Ðе вдалоÑÑ Ñтворити каталог «%s» Ð´Ð»Ñ Ñ€ÐµÐ·ÐµÑ€Ð²Ð½Ð¾Ð³Ð¾ файлу: %s" + msgid "E506: Can't write to backup file (add ! to override)" msgstr "E506: Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати резервний файл (! щоб не зважати)" @@ -2322,20 +2318,6 @@ msgstr "[формат unix]" msgid "[unix]" msgstr "[unix]" -msgid "1 line, " -msgstr "один Ñ€Ñдок, " - -#, c-format -msgid "% lines, " -msgstr "% Ñ€Ñдків, " - -msgid "1 character" -msgstr "один Ñимвол" - -#, c-format -msgid "% characters" -msgstr "% Ñимволів" - msgid "[Incomplete last line]" msgstr "[Ðеповний оÑтанній Ñ€Ñдок]" @@ -2399,10 +2381,12 @@ msgstr "ЗаÑтереженнÑ" msgid "" "&OK\n" -"&Load File" +"&Load File\n" +"Load File &and Options" msgstr "" -"&O:Гаразд\n" -"&L:Завантажити" +"[&O]Гаразд\n" +"[&L]Завантажити файл\n" +"[&a]Завантажити файл Ñ– опції" #, c-format msgid "E462: Could not prepare for reloading \"%s\"" @@ -2550,6 +2534,9 @@ msgstr "E476: Ðекоректна команда" msgid "E17: \"%s\" is a directory" msgstr "E17: «%s» — це каталог" +msgid "E756: Spell checking is not possible" +msgstr "E756: Перевірка орфографії неможлива" + msgid "E900: Invalid channel id" msgstr "E900: Ðекоректний канал" @@ -2718,7 +2705,14 @@ msgid "E928: String required" msgstr "E928: Потрібен String" msgid "E715: Dictionary required" -msgstr "E715: Потрібен Ñловник" +msgstr "E715: Потрібен Dictionary" + +#, c-format +msgid "E979: Blob index out of range: %" +msgstr "E979: Ð†Ð½Ð´ÐµÐºÑ Blob поза межами: %" + +msgid "E978: Invalid operation for Blob" +msgstr "E978: Ðекоректна Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ð°Ð´ Blob" #, c-format msgid "E118: Too many arguments for function: %s" @@ -2731,10 +2725,17 @@ msgstr "E716: Ðемає такого ключа у Ñловнику: «%s»" msgid "E714: List required" msgstr "E714: Потрібен ÑпиÑок" +msgid "E897: List or Blob required" +msgstr "E897: Потрібен List або Blob" + #, c-format msgid "E712: Argument of %s must be a List or Dictionary" msgstr "E712: Ðргумент у %s має бути ÑпиÑком чи Ñловником" +#, c-format +msgid "E896: Argument of %s must be a List, Dictionary or Blob" +msgstr "E896: Ðргумент у %s має бути List, Dictionary чи Blob" + msgid "E47: Error while reading errorfile" msgstr "E47: Помилка Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ помилок" @@ -2802,6 +2803,10 @@ msgstr "E939: Потрібна додана кількіÑть" msgid "E81: Using not in a script context" msgstr "E81: викориÑтовуєтьÑÑ Ð½Ðµ в контекÑті Ñкрипту" +#, c-format +msgid "E107: Missing parentheses: %s" +msgstr "E107: Пропущено дужки: %s" + msgid "E363: pattern uses more memory than 'maxmempattern'" msgstr "E363: Зразок викориÑтовує більше, ніж 'maxmempattern', пам'Ñті" @@ -2832,6 +2837,13 @@ msgstr "E919: Каталог не знайдено у '%s': «%s»" msgid "E952: Autocommand caused recursive behavior" msgstr "E952: Ðвтокоманди призвели до рекурÑÑ–Ñ—" +msgid "E813: Cannot close autocmd window" +msgstr "E813: Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ вікно autocmd" + +#, c-format +msgid "E686: Argument of %s must be a List" +msgstr "E686: Ðргумент у %s має бути ÑпиÑком" + msgid "E519: Option not supported" msgstr "E519: ÐžÐ¿Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ" @@ -2869,6 +2881,21 @@ msgstr "E5601: Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ вікно, Ð·Ð°Ð»Ð¸ÑˆÐ¸Ð»Ð¾Ñ msgid "E5602: Cannot exchange or rotate float" msgstr "E5602: Ðе можна обмінÑти чи покрутити плавуче вікно" +msgid "E1142: Non-empty string required" +msgstr "E1142: Потрібен непорожній String" + +msgid "E1155: Cannot define autocommands for ALL events" +msgstr "E1155: Ðе можу визначити автокоманди Ð´Ð»Ñ Ð£Ð¡Ð†Ð¥ подій" + +msgid "E1240: Resulting text too long" +msgstr "E1240: ТекÑÑ‚ результату задовгий" + +msgid "E1247: Line number out of range" +msgstr "E1247: Ðомер Ñ€Ñдка вийшов поза межами" + +msgid "E1249: Highlight group name too long" +msgstr "E1249: Ðазва групи підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð¾Ð²Ð³Ð°" + msgid "search hit TOP, continuing at BOTTOM" msgstr "Пошук дійшов до ПОЧÐТКУ, продовжуєтьÑÑ Ð· КІÐЦЯ" @@ -2949,31 +2976,85 @@ msgstr "E324: Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл PostScript Ð´Ð»Ñ Ð² msgid "E456: Can't open file \"%s\"" msgstr "E456: Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл «%s»" -msgid "E456: Can't find PostScript resource file \"prolog.ps\"" -msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «prolog.ps»" +msgid "E456: Can't find PostScript resource file \"prolog.ps\"" +msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «prolog.ps»" + +msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" +msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «cidfont.ps»" + +#, c-format +msgid "E456: Can't find PostScript resource file \"%s.ps\"" +msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «%s.ps»" + +#, c-format +msgid "E620: Unable to convert to print encoding \"%s\"" +msgstr "E620: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ до ÐºÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐºÑƒ «%s»" + +msgid "Sending to printer..." +msgstr "ВідÑилаєтьÑÑ Ð½Ð° принтер..." + +msgid "E365: Failed to print PostScript file" +msgstr "E365: Ðе вдалоÑÑ Ð½Ð°Ð´Ñ€ÑƒÐºÑƒÐ²Ð°Ñ‚Ð¸ файл PostScript" + +msgid "Print job sent." +msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐºÑƒ відіÑлано." + +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: ВикориÑтано забагато різних атрибутів кольору" + +#, c-format +msgid "E411: highlight group not found: %s" +msgstr "E411: Групу підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ знайдено: %s" + +#, c-format +msgid "E412: Not enough arguments: \":highlight link %s\"" +msgstr "E412: ÐедоÑтатньо аргументів: «:highlight link %s»" + +#, c-format +msgid "E413: Too many arguments: \":highlight link %s\"" +msgstr "E413: Забагато аргументів: «:highlight link %s»" + +msgid "E414: group has settings, highlight link ignored" +msgstr "E414: Грума має settings, highlight link проігноровано" + +#, c-format +msgid "E415: unexpected equal sign: %s" +msgstr "E415: ÐеÑподіваний знак рівноÑті: %s" + +#, c-format +msgid "E416: missing equal sign: %s" +msgstr "E416: Пропущено знак рівноÑті: %s" + +#, c-format +msgid "E417: missing argument: %s" +msgstr "E417: Пропущено аргумент: %s" + +#, c-format +msgid "E418: Illegal value: %s" +msgstr "E418: Ðеправильне значеннÑ: %s" + +msgid "E419: FG color unknown" +msgstr "E419: Ðевідомий колір текÑту" -msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" -msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «cidfont.ps»" +msgid "E420: BG color unknown" +msgstr "E420: Ðевідомий колір фону" #, c-format -msgid "E456: Can't find PostScript resource file \"%s.ps\"" -msgstr "E456: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ файл реÑурÑів PostScript «%s.ps»" +msgid "E421: Color name or number not recognized: %s" +msgstr "E421: Ðерозпізнана назва або номер кольору: %s" #, c-format -msgid "E620: Unable to convert to print encoding \"%s\"" -msgstr "E620: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ до ÐºÐ¾Ð´ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐºÑƒ «%s»" - -msgid "Sending to printer..." -msgstr "ВідÑилаєтьÑÑ Ð½Ð° принтер..." +msgid "E423: Illegal argument: %s" +msgstr "E423: Ðеправильний аргумент: %s" -msgid "E365: Failed to print PostScript file" -msgstr "E365: Ðе вдалоÑÑ Ð½Ð°Ð´Ñ€ÑƒÐºÑƒÐ²Ð°Ñ‚Ð¸ файл PostScript" +msgid "E669: Unprintable character in group name" +msgstr "E669: Ðедруковний Ñимвол у назві групи" -msgid "Print job sent." -msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐºÑƒ відіÑлано." +msgid "W18: Invalid character in group name" +msgstr "W18: Ðекоректний Ñимвол у назві групи" -msgid "E424: Too many different highlighting attributes in use" -msgstr "E424: ВикориÑтано забагато різних атрибутів кольору" +msgid "E849: Too many highlight and syntax groups" +msgstr "E849: Забагато груп підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– ÑинтакÑиÑу" msgid "Add a new database" msgstr "Додати нову базу даних" @@ -3129,6 +3210,12 @@ msgstr "Жодного з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· cscope\n" msgid " # pid database name prepend path\n" msgstr " # pid назва бази даних шлÑÑ…\n" +msgid "Type number and or click with the mouse (q or empty cancels): " +msgstr "Ðаберіть чиÑло й чи клацніть мишкою (q чи порожнє ÑкаÑовує): " + +msgid "Type number and (q or empty cancels): " +msgstr "Ðаберіть чиÑло й (q чи порожнє ÑкаÑовує): " + #, c-format msgid "E1502: Lua failed to grow stack to %i" msgstr "E1502: Lua не вдалоÑÑ Ð·Ð±Ñ–Ð»ÑŒÑˆÐ¸Ñ‚Ð¸ Ñтек до %i" @@ -3151,20 +3238,11 @@ msgstr "E5102: Lua не вдалоÑÑ Ð·Ð±Ñ–Ð»ÑŒÑˆÐ¸Ñ‚Ð¸ Ñтек до %i" msgid "Error executing vim.schedule lua callback: %.*s" msgstr "Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° lua vim.schedule: %.*s" -#, c-format -msgid "E5106: Error while creating shared module: %.*s" -msgstr "E5106: Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»ÑŽÐ²Ð°Ð½Ð¾Ð³Ð¾ модулÑ: %.*s" - -#, c-format -msgid "E5106: Error while creating inspect module: %.*s" -msgstr "E5106: Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ inspect: %.*s" - -#, c-format -msgid "E5106: Error while creating vim module: %.*s" -msgstr "E5106: Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ vim: %.*s" +msgid "E970: Failed to initialize lua interpreter\n" +msgstr "E970: Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ інтерпретатор lua\n" -msgid "E970: Failed to initialize lua interpreter" -msgstr "E970: Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ інтерпретатор lua" +msgid "E970: Failed to initialize builtin lua modules\n" +msgstr "E970: Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ інтерпретатор lua\n" #, c-format msgid "E5114: Error while converting print argument #%i: %.*s" @@ -3178,6 +3256,10 @@ msgstr "E5115: Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ: msgid "E5116: Error while calling debug string: %.*s" msgstr "E5116: Помилка виклику налагодженнÑ: %.*s" +#, c-format +msgid "E5108: Error executing Lua function: %.*s" +msgstr "E5108: Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— lua: %.*s" + #, c-format msgid "E5107: Error loading lua %.*s" msgstr "E5107: Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ lua %.*s" @@ -3214,8 +3296,16 @@ msgid "E5113: Error while calling lua chunk: %.*s" msgstr "E5113: Помилка виклику шматку lua: %.*s" #, c-format -msgid "Error executing vim.log_keystroke lua callback: %.*s" -msgstr "Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° lua vim.log_keystroke: %.*s" +msgid "Error executing vim._expand_pat: %.*s" +msgstr "Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ vim._expand_pat: %.*s" + +#, c-format +msgid "Error executing vim.on_key Lua callback: %.*s" +msgstr "Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° Lua vim.on_key: %.*s" + +#, c-format +msgid "Error executing Lua callback: %.*s" +msgstr "Помилка Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¾Ð±Ñ€Ð¾Ð±Ð½Ð¸ÐºÐ° Lua: %.*s" msgid "Argument missing after" msgstr "Пропущено аргумент піÑлÑ" @@ -3254,8 +3344,8 @@ msgid "pre-vimrc command line" msgstr "команди перед vimrc" #, c-format -msgid "Conflicting configs: \"%s\" \"%s\"" -msgstr "Суперечливі конфігурації: «%s» «%s»" +msgid "E5422: Conflicting configs: \"%s\" \"%s\"" +msgstr "E5422: Суперечливі конфігурації: «%s» «%s»" #, c-format msgid "E282: Cannot read from \"%s\"" @@ -3387,6 +3477,12 @@ msgstr " --listen <адреÑа> ОбÑлуговувати RPC API за ц msgid " --noplugin Don't load plugins\n" msgstr " --noplugin Ðе завантажувати доповненнÑ\n" +msgid " --remote[-subcommand] Execute commands remotely on a server\n" +msgstr " --remote[-subcommand] Виконати команди на віддаленому Ñервері\n" + +msgid " --server
Specify RPC server to send commands to\n" +msgstr " --server <адреÑа> Задати Ñервер RPC, куди Ñлати команди\n" + msgid " --startuptime Write startup timing messages to \n" msgstr " --startuptime <файл> ЗапиÑати профіль запуÑку до <файлу>\n" @@ -3425,6 +3521,46 @@ msgstr "" "\n" "змінити Ñ€Ñд. Ñтовп. текÑÑ‚" +#, c-format +msgid "E799: Invalid ID: % (must be greater than or equal to 1)" +msgstr "E799: Ðеправильний ID: % (має бути не меншим 1)" + +#, c-format +msgid "E801: ID already taken: %" +msgstr "E801: ID вже зайнÑто: %" + +#, c-format +msgid "E5030: Empty list at position %d" +msgstr "E5030: Порожній ÑпиÑок у позиції %d" + +#, c-format +msgid "E5031: List or number required at position %d" +msgstr "E5031: ОчікуєтьÑÑ ÑпиÑок чи чиÑло у позиції %d" + +#, c-format +msgid "E802: Invalid ID: % (must be greater than or equal to 1)" +msgstr "E802: Ðеправильний ID: % (має бути не меншим 1)" + +#, c-format +msgid "E803: ID not found: %" +msgstr "E803: ID не знайдено: %" + +#, c-format +msgid "E474: List item %d is either not a dictionary or an empty one" +msgstr "E474: Елемент ÑпиÑку %d або не Ñловник або порожній" + +#, c-format +msgid "E474: List item %d is missing one of the required keys" +msgstr "E474: Елемент ÑпиÑку %d немає одного з обов’Ñзкових ключів" + +#, c-format +msgid "E798: ID is reserved for \":match\": %" +msgstr "E798: ID зарезервовано Ð´Ð»Ñ \":match\": %" + +#, c-format +msgid "E798: ID is reserved for \"match\": %" +msgstr "E798: ID зарезервовано Ð´Ð»Ñ \"match\": %" + msgid "E293: block was not locked" msgstr "E293: Блок не було зафікÑовано" @@ -3910,6 +4046,9 @@ msgstr "Перервано: " msgid "Press ENTER or type command to continue" msgstr "ÐатиÑніть ENTER або введіть команду Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ" +msgid " (Interrupted)" +msgstr " (Перервано)" + #, c-format msgid "%s line %" msgstr "%s Ñ€Ñдок %" @@ -3952,38 +4091,9 @@ msgstr "" "&D:Жодного\n" "&C:СкаÑувати" -msgid "Type number and or click with mouse (empty cancels): " -msgstr "Ðаберіть чиÑло й чи клацніть мишкою (порожнє ÑкаÑовує): " - -msgid "Type number and (empty cancels): " -msgstr "Ðаберіть чиÑло й (порожнє ÑкаÑовує): " - -msgid "1 more line" -msgstr "додано один Ñ€Ñдок" - -msgid "1 line less" -msgstr "знищено один Ñ€Ñдок" - -#, c-format -msgid "% more lines" -msgstr "додано Ñ€Ñдків: %" - -#, c-format -msgid "% fewer lines" -msgstr "знищено Ñ€Ñдків: %" - -msgid " (Interrupted)" -msgstr " (Перервано)" - -msgid "Beep!" -msgstr "Дзень!" - msgid "E349: No identifier under cursor" msgstr "E349: Ðемає ідентифікатора над курÑором" -msgid "E774: 'operatorfunc' is empty" -msgstr "E774: 'operatorfunc' порожнÑ" - msgid "E348: No string under cursor" msgstr "E348: Ðемає Ñ€Ñдка на курÑорі" @@ -4006,63 +4116,17 @@ msgstr "" msgid "Type :qa and press to exit Nvim" msgstr "Введіть :qa Ñ– натиÑніÑть щоб вийти з Nvim" -#, c-format -msgid "1 line %sed 1 time" -msgstr "Один Ñ€Ñдок %s-но" - -#, c-format -msgid "1 line %sed %d times" -msgstr "Один Ñ€Ñдок %s-но %d разів" - -#, c-format -msgid "% lines %sed 1 time" -msgstr "% Ñ€Ñдків %s-но" - -#, c-format -msgid "% lines %sed %d times" -msgstr "% Ñ€Ñдків %s-но %d разів" - #, c-format msgid "% lines to indent... " msgstr "ЗалишилоÑÑ Ð²Ð¸Ñ€Ñ–Ð²Ð½Ñти % Ñ€Ñдків..." -msgid "1 line indented " -msgstr "ВирівнÑно один Ñ€Ñдок" - -#, c-format -msgid "% lines indented " -msgstr "ВирівнÑно Ñ€Ñдків: %" - msgid "E748: No previously used register" msgstr "E748: РегіÑтри перед цим не вживалиÑÑŒ" -msgid "1 line changed" -msgstr "Один Ñ€Ñдок змінено" - -#, c-format -msgid "% lines changed" -msgstr "Змінено Ñ€Ñдків: %" - #, c-format msgid " into \"%c" msgstr " у \"%c" -#, c-format -msgid "block of 1 line yanked%s" -msgstr "Запам'Ñтав блок з одного Ñ€Ñдка%s" - -#, c-format -msgid "1 line yanked%s" -msgstr "Запам'Ñтав один Ñ€Ñдок%s" - -#, c-format -msgid "block of % lines yanked%s" -msgstr "Запам'Ñтав блок із % Ñ€Ñдків%s" - -#, c-format -msgid "% lines yanked%s" -msgstr "Запам'Ñтав Ñ€Ñдків: %%s" - #, c-format msgid "E353: Nothing in register %s" msgstr "E353: У регіÑтрі %s нічого немає" @@ -4120,6 +4184,9 @@ msgstr "" msgid "(+% for BOM)" msgstr "(+% Ð´Ð»Ñ BOM)" +msgid "E774: 'operatorfunc' is empty" +msgstr "E774: 'operatorfunc' порожнÑ" + msgid "E518: Unknown option" msgstr "E518: Ðевідома опціÑ" @@ -4168,8 +4235,8 @@ msgstr "E527: Бракує коми" msgid "E528: Must specify a ' value" msgstr "E528: Потрібно вказати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ '" -msgid "E595: contains unprintable or wide character" -msgstr "E595: МіÑтить недруковні або розширені Ñимволи" +msgid "E595: 'showbreak' contains unprintable or wide character" +msgstr "E595: 'showbreak' міÑтить недруковні або розширені Ñимволи" #, c-format msgid "E535: Illegal character after <%c>" @@ -4399,67 +4466,18 @@ msgstr "E70: %s%%[] порожній" msgid "E956: Cannot use pattern recursively" msgstr "E956: Ðе можна рекурÑивно викориÑтати шаблон" -msgid "E65: Illegal back reference" -msgstr "E65: Ðекоректне зворотнє поÑиланнÑ" - -msgid "E339: Pattern too long" -msgstr "E339: Зразок занадто довгий" - -msgid "E50: Too many \\z(" -msgstr "E50: Забагато \\z(" - -#, c-format -msgid "E51: Too many %s(" -msgstr "E51: Забагато %s(" - -msgid "E52: Unmatched \\z(" -msgstr "E52: Ðемає пари \\z(" - -#, c-format -msgid "E59: invalid character after %s@" -msgstr "E59: Ðедозволений Ñимвол піÑÐ»Ñ %s@" - -#, c-format -msgid "E60: Too many complex %s{...}s" -msgstr "E60: Забагато Ñкладних %s{...}" - -#, c-format -msgid "E61: Nested %s*" -msgstr "E61: Вкладені %s*" - #, c-format -msgid "E62: Nested %s%c" -msgstr "E62: Вкладені %s%c" - -msgid "E63: invalid use of \\_" -msgstr "E63: Ðекоректно вжито \\_" - -#, c-format -msgid "E64: %s%c follows nothing" -msgstr "E64: ПіÑÐ»Ñ %s%c нічого немає" - -msgid "E68: Invalid character after \\z" -msgstr "E68: Ðеправильний Ñимвол піÑÐ»Ñ \\z" +msgid "E1204: No Number allowed after .: '\\%%%c'" +msgstr "E1204: Number не можна піÑÐ»Ñ .: '\\%%%c'" #, c-format -msgid "E678: Invalid character after %s%%[dxouU]" -msgstr "E678: Ðедозволений Ñимвол піÑÐ»Ñ %s%%[dxouU]" - -#, c-format -msgid "E71: Invalid character after %s%%" -msgstr "E71: Ðедозволений Ñимвол піÑÐ»Ñ %s%%" +msgid "E554: Syntax error in %s{...}" +msgstr "E554: СинтакÑична помилка в %s{...}" #, c-format msgid "E888: (NFA regexp) cannot repeat %s" msgstr "E888: (NFA regexp) неможливо повторити %s" -#, c-format -msgid "E554: Syntax error in %s{...}" -msgstr "E554: СинтакÑична помилка в %s{...}" - -msgid "External submatches:\n" -msgstr "Зовнішні під-збіги:\n" - msgid "" "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " "used " @@ -4482,6 +4500,14 @@ msgstr "Пошук «%s»" msgid "not found in '%s': \"%s\"" msgstr "не знайдено в '%s': «%s»" +#, c-format +msgid "Searching for \"%s\" in runtime path" +msgstr "Пошук «%s» в шлÑху виконаннÑ" + +#, c-format +msgid "not found in runtime path: \"%s\"" +msgstr "не знайдено в шлÑху виконаннÑ: «%s»" + msgid " TERMINAL" msgstr " ТЕРМІÐÐЛ" @@ -4850,9 +4876,6 @@ msgstr " (не підтримуєтьÑÑ)" msgid "E759: Format error in spell file" msgstr "E759: Помилка формату у файлі орфографії" -msgid "E756: Spell checking is not enabled" -msgstr "E756: Перевірка орфографії не дозволена" - #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" msgstr "" @@ -5193,6 +5216,9 @@ msgstr "E765: 'spellfile' не міÑтить % елементів" msgid "Word '%.*s' removed from %s" msgstr "Слово '%.*s' знищено з %s" +msgid "Seek error in spellfile" +msgstr "Помилка зміни позиції у файлі орфографії" + #, c-format msgid "Word '%.*s' added to %s" msgstr "Слово '%.*s' додано до %s" @@ -5222,36 +5248,6 @@ msgstr "Ð”Ð»Ñ Ð±ÑƒÑ„ÐµÑ€Ð° не визначено елементів Ñинт msgid "'redrawtime' exceeded, syntax highlighting disabled" msgstr "'redrawtime' перевищено, підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ ÑинтакÑиÑу вимкнено" -msgid "syntax conceal on" -msgstr "ÑинтакÑичне Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼Ðº" - -msgid "syntax conceal off" -msgstr "ÑинтакÑичне Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ðº" - -msgid "syntax case ignore" -msgstr "ÑинтакÑÐ¸Ñ Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ñ‚Ð¸ регіÑтр" - -msgid "syntax case match" -msgstr "ÑинтакÑÐ¸Ñ Ð´Ð¾Ñ‚Ñ€Ð¸Ð¼ÑƒÐ²Ð°Ñ‚Ð¸ÑÑ Ñ€ÐµÐ³Ñ–Ñтру" - -msgid "syntax foldlevel start" -msgstr "рівень згортки ÑинтакÑиÑу початок" - -msgid "syntax foldlevel minimum" -msgstr "рівень згортки ÑинтакÑиÑу мінімум" - -msgid "syntax spell toplevel" -msgstr "ÑинтакÑÐ¸Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ñти вÑюди" - -msgid "syntax spell notoplevel" -msgstr "ÑинтакÑÐ¸Ñ Ð½Ðµ перевірÑти" - -msgid "syntax spell default" -msgstr "ÑинтакÑÐ¸Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÐ¾Ð²Ð¾" - -msgid "syntax iskeyword " -msgstr "ÑинтакÑÐ¸Ñ iskeyword " - msgid "syntax iskeyword not set" msgstr "не вÑтановлено ÑинтакÑÐ¸Ñ iskeyword" @@ -5400,63 +5396,6 @@ msgid "" msgstr "" " ВСЬОГО К-ТЬ СПІВП. ÐÐЙПОВІЛ. СЕРЕДÐ. ÐÐЗВРШÐБЛОÐ" -msgid "E679: recursive loop loading syncolor.vim" -msgstr "E679: РекурÑивний цикл Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ syncolor.vim" - -#, c-format -msgid "E411: highlight group not found: %s" -msgstr "E411: Групу підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ðµ знайдено: %s" - -#, c-format -msgid "E412: Not enough arguments: \":highlight link %s\"" -msgstr "E412: ÐедоÑтатньо аргументів: «:highlight link %s»" - -#, c-format -msgid "E413: Too many arguments: \":highlight link %s\"" -msgstr "E413: Забагато аргументів: «:highlight link %s»" - -msgid "E414: group has settings, highlight link ignored" -msgstr "E414: Грума має settings, highlight link проігноровано" - -#, c-format -msgid "E415: unexpected equal sign: %s" -msgstr "E415: ÐеÑподіваний знак рівноÑті: %s" - -#, c-format -msgid "E416: missing equal sign: %s" -msgstr "E416: Пропущено знак рівноÑті: %s" - -#, c-format -msgid "E417: missing argument: %s" -msgstr "E417: Пропущено аргумент: %s" - -#, c-format -msgid "E418: Illegal value: %s" -msgstr "E418: Ðеправильне значеннÑ: %s" - -msgid "E419: FG color unknown" -msgstr "E419: Ðевідомий колір текÑту" - -msgid "E420: BG color unknown" -msgstr "E420: Ðевідомий колір фону" - -#, c-format -msgid "E421: Color name or number not recognized: %s" -msgstr "E421: Ðерозпізнана назва або номер кольору: %s" - -#, c-format -msgid "E423: Illegal argument: %s" -msgstr "E423: Ðеправильний аргумент: %s" - -msgid "E669: Unprintable character in group name" -msgstr "E669: Ðедруковний Ñимвол у назві групи" - -msgid "W18: Invalid character in group name" -msgstr "W18: Ðекоректний Ñимвол у назві групи" - -msgid "E849: Too many highlight and syntax groups" -msgstr "E849: Забагато груп підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– ÑинтакÑиÑу" - msgid "E555: at bottom of tag stack" msgstr "E555: Кінець Ñтеку міток" @@ -5542,6 +5481,9 @@ msgstr "E435: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ мітку, тільки прип msgid "Duplicate field name: %s" msgstr "Ðазва Ð¿Ð¾Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÑŽÑ”Ñ‚ÑŒÑÑ: %s" +msgid "Beep!" +msgstr "Дзень!" + msgid "E881: Line count changed unexpectedly" msgstr "E881: КількіÑть Ñ€Ñдків неÑподівано змінилаÑÑ" @@ -5911,9 +5853,6 @@ msgstr "E443: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñтити вікно, заваж msgid "E444: Cannot close last window" msgstr "E444: Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ оÑтаннє вікно" -msgid "E813: Cannot close autocmd window" -msgstr "E813: Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ вікно autocmd" - msgid "E814: Cannot close window, only autocmd window would remain" msgstr "E814: Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ вікно, залишилоÑÑ Ð± тільки вікно autocmd" @@ -5923,26 +5862,4 @@ msgstr "E445: У іншому вікні Ñ” зміни" msgid "E446: No file name under cursor" msgstr "E446: Ðемає назви файлу над курÑором" -#, c-format -msgid "E799: Invalid ID: % (must be greater than or equal to 1)" -msgstr "E799: Ðеправильний ID: % (має бути не меншим 1)" - -#, c-format -msgid "E801: ID already taken: %" -msgstr "E801: ID вже зайнÑто: %" - -#, c-format -msgid "E5030: Empty list at position %d" -msgstr "E5030: Порожній ÑпиÑок у позиції %d" - -#, c-format -msgid "E5031: List or number required at position %d" -msgstr "E5031: ОчікуєтьÑÑ ÑпиÑок чи чиÑло у позиції %d" - -#, c-format -msgid "E802: Invalid ID: % (must be greater than or equal to 1)" -msgstr "E802: Ðеправильний ID: % (має бути не меншим 1)" -#, c-format -msgid "E803: ID not found: %" -msgstr "E803: ID не знайдено: %" -- cgit From b6026337f25cc708e932b21a9e4e64a174a1d9da Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 01:25:11 +0100 Subject: vim-patch:8.2.3416: second error is reported while exception is being thrown Problem: Second error is reported while exception is being thrown. Solution: Do not check for trailing characters when already aborting. (closes vim/vim#8842) https://github.com/vim/vim/commit/36f691f5f1d0676f080cc97d697d742ed5cc8251 --- src/nvim/eval/userfunc.c | 2 +- src/nvim/testdir/test_trycatch.vim | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index eb5c6e503a..972bd81117 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3018,7 +3018,7 @@ void ex_call(exarg_T *eap) } // When inside :try we need to check for following "| catch". - if (!failed || eap->cstack->cs_trylevel > 0) { + if (!aborting() && (!failed || eap->cstack->cs_trylevel > 0)) { // Check for trailing illegal characters and a following command. if (!ends_excmd(*arg)) { if (!failed) { diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index adc1745b39..04423fa988 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -1996,5 +1996,29 @@ func Test_reload_in_try_catch() call delete('Xreload') endfunc +" Test for using throw in a called function with following error {{{1 +func Test_user_command_throw_in_function_call() + let lines =<< trim END + function s:get_dict() abort + throw 'my_error' + endfunction + + try + call s:get_dict().foo() + catch /my_error/ + let caught = 'yes' + catch + let caught = 'no' + endtry + call assert_equal('yes', caught) + END + call writefile(lines, 'XtestThrow') + source XtestThrow + + call delete('XtestThrow') + unlet g:caught +endfunc + + " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From 93c72d866b3a41c429dd9d278cda7059ebd4afba Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 01:34:18 +0100 Subject: vim-patch:8.2.3448: :endtry after function call that throws not found Problem: :endtry after function call that throws not found. Solution: Do check for following :endtry if an exception is being thrown. (closes vim/vim#8889) https://github.com/vim/vim/commit/1d34189ecb99fa76363c06e1aa815c1075675a1c Nvim obsoleted did_throw; check current_exception is not NULL instead. --- src/nvim/eval/userfunc.c | 7 ++++--- src/nvim/testdir/test_trycatch.vim | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 972bd81117..0fadc0d220 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3017,11 +3017,12 @@ void ex_call(exarg_T *eap) } } - // When inside :try we need to check for following "| catch". - if (!aborting() && (!failed || eap->cstack->cs_trylevel > 0)) { + // When inside :try we need to check for following "| catch" or "| endtry". + // Not when there was an error, but do check if an exception was thrown. + if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) { // Check for trailing illegal characters and a following command. if (!ends_excmd(*arg)) { - if (!failed) { + if (!failed && !aborting()) { emsg_severe = true; emsg(_(e_trailing)); } diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 04423fa988..e7b163c94b 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2008,7 +2008,7 @@ func Test_user_command_throw_in_function_call() catch /my_error/ let caught = 'yes' catch - let caught = 'no' + let caught = v:exception endtry call assert_equal('yes', caught) END @@ -2019,6 +2019,32 @@ func Test_user_command_throw_in_function_call() unlet g:caught endfunc +" Test for using throw in a called function with following endtry {{{1 +func Test_user_command_function_call_with_endtry() + let lines =<< trim END + funct s:throw(msg) abort + throw a:msg + endfunc + func s:main() abort + try + try + throw 'err1' + catch + call s:throw('err2') | endtry + catch + let s:caught = 'yes' + endtry + endfunc + + call s:main() + call assert_equal('yes', s:caught) + END + call writefile(lines, 'XtestThrow') + source XtestThrow + + call delete('XtestThrow') +endfunc + " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From a25c35d6e409398a8d8304210784e020c262aec5 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 02:13:49 +0100 Subject: vim-patch:8.2.3470: crash with error in :catch and also in :finally Problem: Crash with error in :catch and also in :finally. Solution: Only discard an exception if there is one. (closes vim/vim#8954) https://github.com/vim/vim/commit/a684a684096ecef3fbaee39c573b47423235d6b1 --- src/nvim/ex_eval.c | 2 +- src/nvim/testdir/test_trycatch.vim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 3c7c635d98..d9c14f143e 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1913,7 +1913,7 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) default: if (cstack->cs_flags[idx] & CSF_FINALLY) { - if (cstack->cs_pending[idx] & CSTP_THROW) { + if ((cstack->cs_pending[idx] & CSTP_THROW) && cstack->cs_exception[idx] != NULL) { // Cancel the pending exception. This is in the // finally clause, so that the stack of the // caught exceptions is not involved. diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index e7b163c94b..f625f52906 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2045,6 +2045,18 @@ func Test_user_command_function_call_with_endtry() call delete('XtestThrow') endfunc +func ThisWillFail() + try + if x | endif + catch + for l in [] + finally +endfunc + +func Test_error_in_catch_and_finally() + call assert_fails('call ThisWillFail()', ['E121:', 'E600:']) +endfunc + " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From 64150517967fbe7fb111320c3dc1d16528aa547b Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 02:52:21 +0100 Subject: refactor(ex_eval): cherry-pick CSF flags changes from v8.2.3099 https://github.com/vim/vim/commit/4197828dc666f2d258594f7f9461534d23cc50e4 Cherry-pick the changes to existing flags values. Required for v8.2.3478. That patch mostly relates to Vim9script, but I'm careful not to mark it N/A in case the flags have some use outside of Vim9 in the future. Excludes CSF_FUNC_DEF (flag introduced in v8.2.1870 for Vim9's block scopes). --- src/nvim/ex_eval.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 15da4b2d60..07247ea7d5 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -14,9 +14,9 @@ #define CSF_TRY 0x0100 // is a ":try" #define CSF_FINALLY 0x0200 // ":finally" has been passed -#define CSF_THROWN 0x0400 // exception thrown to this try conditional -#define CSF_CAUGHT 0x0800 // exception caught by this try conditional -#define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" +#define CSF_THROWN 0x0800 // exception thrown to this try conditional +#define CSF_CAUGHT 0x1000 // exception caught by this try conditional +#define CSF_SILENT 0x2000 // "emsg_silent" reset by ":try" // Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset // (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. -- cgit From 5feb8cdbb4d1ac01288058112702279c476f5aea Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 02:17:45 +0100 Subject: vim-patch:8.2.3478: still crash with error in :catch and also in :finally Problem: Still crash with error in :catch and also in :finally. Solution: Only call finish_exception() once. (closes vim/vim#8954) https://github.com/vim/vim/commit/f67d3fb7363ebc9454f9bb582de3978609a4fd6b Exclude CSF_FUNC_DEF change (Vim9script). --- src/nvim/ex_eval.c | 3 ++- src/nvim/ex_eval.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index d9c14f143e..b5456e8982 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1934,8 +1934,9 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) */ if (!(cstack->cs_flags[idx] & CSF_FINALLY)) { if ((cstack->cs_flags[idx] & CSF_ACTIVE) - && (cstack->cs_flags[idx] & CSF_CAUGHT)) { + && (cstack->cs_flags[idx] & CSF_CAUGHT) && !(cstack->cs_flags[idx] & CSF_FINISHED)) { finish_exception((except_T *)cstack->cs_exception[idx]); + cstack->cs_flags[idx] |= CSF_FINISHED; } // Stop at this try conditional - except the try block never // got active (because of an inactive surrounding conditional diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 07247ea7d5..1cd0a47d7a 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -16,7 +16,8 @@ #define CSF_FINALLY 0x0200 // ":finally" has been passed #define CSF_THROWN 0x0800 // exception thrown to this try conditional #define CSF_CAUGHT 0x1000 // exception caught by this try conditional -#define CSF_SILENT 0x2000 // "emsg_silent" reset by ":try" +#define CSF_FINISHED 0x2000 // CSF_CAUGHT was handled by finish_exception() +#define CSF_SILENT 0x4000 // "emsg_silent" reset by ":try" // Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset // (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. -- cgit From 789558d365a3134a33cdf4f1f3cc5e77efcad687 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 9 Oct 2021 02:20:07 +0100 Subject: vim-patch:8.2.3480: test does not fail without the fix for a crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Test does not fail without the fix for a crash. Solution: Write the bad code in a file and source it. (Dominique Pellé, closes vim/vim#8961) https://github.com/vim/vim/commit/949de97da32d4fff28c569387e2ba8b3e311e64d --- src/nvim/testdir/test_trycatch.vim | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index f625f52906..34c402fc73 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2046,17 +2046,26 @@ func Test_user_command_function_call_with_endtry() endfunc func ThisWillFail() - try - if x | endif - catch - for l in [] - finally + endfunc +" This was crashing prior to the fix in 8.2.3478. func Test_error_in_catch_and_finally() - call assert_fails('call ThisWillFail()', ['E121:', 'E600:']) -endfunc + let lines =<< trim END + try + echo x + catch + for l in [] + finally + END + call writefile(lines, 'XtestCatchAndFinally') + try + source XtestCatchAndFinally + catch /E600:/ + endtry + call delete('XtestCatchAndFinally') +endfunc " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From fc954d0a61ca8952bdcd05f66fe07ae2a4ccb712 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 8 Oct 2021 21:05:38 +0100 Subject: vim-patch:8.2.3486: illegal memory access with invalid sequence of commands Problem: Illegal memory access with invalid sequence of commands. Solution: Do not call leave_block() when not in a try block. (closes vim/vim#8966) Reset did_emsg so that exception is shown as an error. https://github.com/vim/vim/commit/cce81e9673fe8d056e8eef310d9919620eccb2f2 Vim9script is N/A, which includes leave_block. --- src/nvim/ex_eval.c | 12 +++++++++--- src/nvim/testdir/test_trycatch.vim | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index b5456e8982..f4aaab5c43 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1083,7 +1083,7 @@ void ex_endwhile(exarg_T *eap) if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = err; } else { - fl = cstack->cs_flags[cstack->cs_idx]; + fl = cstack->cs_flags[cstack->cs_idx]; if (!(fl & csf)) { // If we are in a ":while" or ":for" but used the wrong endloop // command, do not rewind to the next enclosing ":for"/":while". @@ -1575,6 +1575,7 @@ void ex_endtry(exarg_T *eap) if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); + // Find the matching ":try" and report what's missing. idx = cstack->cs_idx; do { @@ -1594,6 +1595,9 @@ void ex_endtry(exarg_T *eap) if (current_exception) { discard_current_exception(); } + + // report eap->errmsg, also when there already was an error + did_emsg = false; } else { idx = cstack->cs_idx; @@ -1664,8 +1668,10 @@ void ex_endtry(exarg_T *eap) */ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); - --cstack->cs_idx; - --cstack->cs_trylevel; + if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { + cstack->cs_idx--; + } + cstack->cs_trylevel--; if (!skip) { report_resume_pending(pending, diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index 34c402fc73..205ed095ea 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -2067,5 +2067,26 @@ func Test_error_in_catch_and_finally() call delete('XtestCatchAndFinally') endfunc +" This was causing an illegal memory access +func Test_leave_block_in_endtry_not_called() + let lines =<< trim END + " vim9script + " try # + try " + for x in [] + if + endwhile + if + endtry + END + call writefile(lines, 'XtestEndtry') + try + source XtestEndtry + catch /E171:/ + endtry + + call delete('XtestEndtry') +endfunc + " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker -- cgit From 76e6b81b23c59ee119d6cc34eed0ef580f15db07 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 8 Oct 2021 20:44:58 +0100 Subject: vim-patch:8.2.3487: illegal memory access if buffer name is very long Problem: Illegal memory access if buffer name is very long. Solution: Make sure not to go over the end of the buffer. https://github.com/vim/vim/commit/826bfe4bbd7594188e3d74d2539d9707b1c6a14b Adjust the test to use :noswapfile, as Nvim fails to create the swap file on Windows due to the file name's length (E303). We don't have this behaviour on Linux as we get "[Permission denied]" from readfile(), so there is no attempt to create the swap file. However, Vim doesn't try to create the swap file on Windows either for a different reason: MAXPATHL in Vim for Windows is only 1024 (compared to Nvim's 4096 on the Windows CI), so readfile() gives "Illegal file name" instead, thus not needing :noswapfile for both cases. --- src/nvim/screen.c | 10 +++++----- src/nvim/testdir/test_statusline.vim | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 296255ed8c..5a33ebfb37 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5173,19 +5173,19 @@ static void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - STRCPY(p + len, _("[Help]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]")); len += (int)STRLEN(p + len); } if (wp->w_p_pvw) { - STRCPY(p + len, _("[Preview]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]")); len += (int)STRLEN(p + len); } if (bufIsChanged(wp->w_buffer)) { - STRCPY(p + len, "[+]"); - len += 3; + snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]"); + len += (int)STRLEN(p + len); } if (wp->w_buffer->b_p_ro) { - STRCPY(p + len, _("[RO]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]")); // len += (int)STRLEN(p + len); // dead assignment } diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index fad13e3340..492d09c645 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -523,4 +523,16 @@ func Test_statusline_mbyte_fillchar() %bw! endfunc +" Used to write beyond allocated memory. This assumes MAXPATHL is 4096 bytes. +func Test_statusline_verylong_filename() + let fname = repeat('x', 4090) + " Nvim's swap file creation fails on Windows (E303) due to fname's length + " exe "new " .. fname + exe "noswapfile new " .. fname + set buftype=help + set previewwindow + redraw + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab -- cgit From e463eb81465978b5de77e207af9ee1b416ca0053 Mon Sep 17 00:00:00 2001 From: Gregory Anders <8965202+gpanders@users.noreply.github.com> Date: Wed, 13 Apr 2022 08:04:56 -0600 Subject: fix(api): correctly pass f-args for nvim_create_user_command (#18098) Skip runs of whitespace and do not include `\` characters when followed by another `\` or whitespace. This matches the behavior of when used with `:command`. --- src/nvim/ex_docmd.c | 52 +++++++++++++++++++++++++++++++++---------------- src/nvim/lua/executor.c | 22 ++++++++++++--------- 2 files changed, 48 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 482bf69f67..cbfe6e3789 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5774,26 +5774,44 @@ static void ex_delcommand(exarg_T *eap) /// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback. /// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator. /// -/// @note If no separator is found start = 0 and end = length - 1 -/// @param[in] arg String to split -/// @param[in] iter Iteration counter -/// @param[out] start Start of the split -/// @param[out] end End of the split -/// @param[in] length Length of the string +/// @param[in] arg String to split +/// @param[in] arglen Length of {arg} +/// @param[inout] end Index of last character of previous iteration +/// @param[out] buf Buffer to copy string into +/// @param[out] len Length of string in {buf} /// -/// @return false if it's the last split (don't call again), true otherwise (call again). -bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int length) -{ - int pos; - *start = *end + (iter > 1 ? 2 : 0); // Skip whitespace after the first split - for (pos = *start; pos < length - 2; pos++) { - if (arg[pos] != '\\' && ascii_iswhite(arg[pos + 1])) { - *end = pos; - return true; +/// @return true if iteration is complete, else false +bool uc_split_args_iter(const char_u *arg, size_t arglen, size_t *end, char *buf, size_t *len) +{ + if (!arglen) { + return true; + } + + size_t pos = *end; + while (pos < arglen && ascii_iswhite(arg[pos])) { + pos++; + } + + size_t l = 0; + for (; pos < arglen - 1; pos++) { + if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) { + buf[l++] = arg[++pos]; + } else { + buf[l++] = arg[pos]; + if (ascii_iswhite(arg[pos + 1])) { + *end = pos + 1; + *len = l; + return false; + } } } - *end = length - 1; - return false; + + if (pos < arglen && !ascii_iswhite(arg[pos])) { + buf[l++] = arg[pos]; + } + + *len = l; + return true; } /// split and quote args for diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 21625854fb..81396f1715 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1869,17 +1869,21 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) } else { // Commands with more than one possible argument we split lua_pop(lstate, 1); // Pop the reference of opts.args - int length = (int)STRLEN(eap->arg); - int start = 0; - int end = 0; + size_t length = STRLEN(eap->arg); + size_t end = 0; + size_t len = 0; int i = 1; - bool res = true; - while (res) { - res = uc_split_args_iter(eap->arg, i, &start, &end, length); - lua_pushlstring(lstate, (const char *)eap->arg + start, (size_t)(end - start + 1)); - lua_rawseti(lstate, -2, i); - i++; + char *buf = xcalloc(length, sizeof(char)); + bool done = false; + while (!done) { + done = uc_split_args_iter(eap->arg, length, &end, buf, &len); + if (len > 0) { + lua_pushlstring(lstate, buf, len); + lua_rawseti(lstate, -2, i); + i++; + } } + xfree(buf); } lua_setfield(lstate, -2, "fargs"); -- cgit From 9938740ca6eae26de7c6deff8dd8318136ee5113 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 13 Apr 2022 17:04:38 +0200 Subject: vim-patch:8.2.4746: supercollider filetype not recognized (#18102) Problem: Supercollider filetype not recognized. Solution: Match file extentions and check file contents to detect supercollider. (closes vim/vim#10142) https://github.com/vim/vim/commit/8cac20ed42b7b7fc9c6b54e3055ca1047f50b8ca --- src/nvim/testdir/test_filetype.vim | 52 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index aeb6e12ead..197a9edb76 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -463,12 +463,11 @@ let s:filename_checks = { \ 'sass': ['file.sass'], \ 'sather': ['file.sa'], \ 'sbt': ['file.sbt'], - \ 'scala': ['file.scala', 'file.sc'], + \ 'scala': ['file.scala'], \ 'scheme': ['file.scm', 'file.ss', 'file.sld', 'file.rkt', 'file.rktd', 'file.rktl'], \ 'scilab': ['file.sci', 'file.sce'], \ 'screen': ['.screenrc', 'screenrc'], \ 'sexplib': ['file.sexp'], - \ 'scdoc': ['file.scd'], \ 'scss': ['file.scss'], \ 'sd': ['file.sd'], \ 'sdc': ['file.sdc'], @@ -516,6 +515,7 @@ let s:filename_checks = { \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], \ 'stp': ['file.stp'], \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'], + \ 'supercollider': ['file.quark'], \ 'surface': ['file.sface'], \ 'svg': ['file.svg'], \ 'svn': ['svn-commitfile.tmp', 'svn-commit-file.tmp', 'svn-commit.tmp'], @@ -1470,6 +1470,54 @@ func Test_prg_file() filetype off endfunc +" Test dist#ft#FTsc() +func Test_sc_file() + filetype on + + " SC file mehtods are defined 'Class : Method' + call writefile(['SCNvimDocRenderer : SCDocHTMLRenderer {'], 'srcfile.sc') + split srcfile.sc + call assert_equal('supercollider', &filetype) + bwipe! + call delete('srcfile.sc') + + " SC classes are defined with '+ Class {}' + call writefile(['+ SCNvim {', '*methodArgs {|method|'], 'srcfile.sc') + split srcfile.sc + call assert_equal('supercollider', &filetype) + bwipe! + call delete('srcfile.sc') + + " Some SC class files start with comment and define methods many lines later + call writefile(['// Query', '//Method','^this {'], 'srcfile.sc') + split srcfile.sc + call assert_equal('supercollider', &filetype) + bwipe! + call delete('srcfile.sc') + + " Some SC class files put comments between method declaration after class + call writefile(['PingPong {', '//comment','*ar { arg'], 'srcfile.sc') + split srcfile.sc + call assert_equal('supercollider', &filetype) + bwipe! + call delete('srcfile.sc') + + filetype off +endfunc + +" Test dist#ft#FTscd() +func Test_scd_file() + filetype on + + call writefile(['ijq(1)'], 'srcfile.scd') + split srcfile.scd + call assert_equal('scdoc', &filetype) + bwipe! + call delete('srcfile.scd') + + filetype off +endfunc + func Test_src_file() filetype on -- cgit From 0fb571e3b5043f136f2394d84b942b8c93fdde45 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Mon, 11 Apr 2022 18:29:48 +0200 Subject: refactor: add pure attribute to pure functions This will allow compilers that support the pure attribute to make further optimizations to functions. --- src/nvim/arabic.c | 2 ++ src/nvim/autocmd.c | 7 +++++++ src/nvim/buffer_updates.c | 1 + src/nvim/context.c | 2 ++ src/nvim/cursor_shape.c | 3 +++ src/nvim/diff.c | 2 ++ src/nvim/digraph.c | 2 ++ src/nvim/ex_getln.c | 2 ++ 8 files changed, 21 insertions(+) (limited to 'src') diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 5dcc3d3d0d..db78e0e68f 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -974,6 +974,7 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c) /// @param one First character. /// @param two Character just after "one". bool arabic_combine(int one, int two) + FUNC_ATTR_PURE { if (one == a_LAM) { return arabic_maycombine(two); @@ -984,6 +985,7 @@ bool arabic_combine(int one, int two) /// Check whether we are dealing with a character that could be regarded as an /// Arabic combining character, need to check the character before this. bool arabic_maycombine(int two) + FUNC_ATTR_PURE { if (p_arshape && !p_tbidi) { return two == a_ALEF_MADDA diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 1b146f82c6..77d4115b59 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -379,6 +379,7 @@ static void au_cleanup(void) // Get the first AutoPat for a particular event. AutoPat *au_get_autopat_for_event(event_T event) + FUNC_ATTR_PURE { return first_autopat[(int)event]; } @@ -1143,6 +1144,7 @@ int autocmd_register(int64_t id, event_T event, char_u *pat, int patlen, int gro } size_t aucmd_pattern_length(char_u *pat) + FUNC_ATTR_PURE { if (*pat == NUL) { return 0; @@ -1175,6 +1177,7 @@ size_t aucmd_pattern_length(char_u *pat) } char_u *aucmd_next_pattern(char_u *pat, size_t patlen) + FUNC_ATTR_PURE { pat = pat + patlen; if (*pat == ',') { @@ -2383,6 +2386,7 @@ theend: // Checks if a pattern is buflocal bool aupat_is_buflocal(char_u *pat, int patlen) + FUNC_ATTR_PURE { return patlen >= 8 && STRNCMP(pat, "update_channels) || kv_size(buf->update_callbacks); } diff --git a/src/nvim/context.c b/src/nvim/context.c index 9434b4e0ea..c30a05a3c8 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -33,6 +33,7 @@ void ctx_free_all(void) /// Returns the size of the context stack. size_t ctx_size(void) + FUNC_ATTR_PURE { return kv_size(ctx_stack); } @@ -40,6 +41,7 @@ size_t ctx_size(void) /// Returns pointer to Context object with given zero-based index from the top /// of context stack or NULL if index is out of bounds. Context *ctx_get(size_t index) + FUNC_ATTR_PURE { if (index < kv_size(ctx_stack)) { return &kv_Z(ctx_stack, index); diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 0e4a4bcfb0..73adff6579 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -277,6 +277,7 @@ char *parse_shape_opt(int what) /// /// @param exclusive If 'selection' option is "exclusive". bool cursor_is_block_during_visual(bool exclusive) + FUNC_ATTR_PURE { int mode_idx = exclusive ? SHAPE_IDX_VE : SHAPE_IDX_V; return (SHAPE_BLOCK == shape_table[mode_idx].shape @@ -300,6 +301,7 @@ int cursor_mode_str2int(const char *mode) /// Check if a syntax id is used as a cursor style. bool cursor_mode_uses_syn_id(int syn_id) + FUNC_ATTR_PURE { if (*p_guicursor == NUL) { return false; @@ -316,6 +318,7 @@ bool cursor_mode_uses_syn_id(int syn_id) /// Return the index into shape_table[] for the current mode. int cursor_get_mode_idx(void) + FUNC_ATTR_PURE { if (State == SHOWMATCH) { return SHAPE_IDX_SM; diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 0b55fb877c..9e8fa2e62d 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -883,6 +883,7 @@ theend: /// diff will be used anyway. /// int diff_internal(void) + FUNC_ATTR_PURE { return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; } @@ -2250,6 +2251,7 @@ bool diffopt_horizontal(void) // Return true if 'diffopt' contains "hiddenoff". bool diffopt_hiddenoff(void) + FUNC_ATTR_PURE { return (diff_flags & DIFF_HIDDEN_OFF) != 0; } diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 083c868607..0e148543aa 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1551,6 +1551,7 @@ int get_digraph(bool cmdline) /// @return If no match, return "char2". If "meta_char" is true and "char1" // is a space, return "char2" | 0x80. static int getexactdigraph(int char1, int char2, bool meta_char) + FUNC_ATTR_PURE { int retval = 0; @@ -1601,6 +1602,7 @@ static int getexactdigraph(int char1, int char2, bool meta_char) /// /// @return The digraph. int digraph_get(int char1, int char2, bool meta_char) + FUNC_ATTR_PURE { int retval; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a7c0b06050..33e71115a4 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2604,6 +2604,7 @@ char_u *getexline(int c, void *cookie, int indent, bool do_concat) } bool cmdline_overstrike(void) + FUNC_ATTR_PURE { return ccline.overstrike; } @@ -2611,6 +2612,7 @@ bool cmdline_overstrike(void) /// Return true if the cursor is at the end of the cmdline. bool cmdline_at_end(void) + FUNC_ATTR_PURE { return (ccline.cmdpos >= ccline.cmdlen); } -- cgit From 8486c87e58beb4c88957398db1890e0836a0c75e Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Wed, 13 Apr 2022 23:11:56 +0200 Subject: vim-patch:8.2.4747: no filetype override for .sys files (#18105) Problem: No filetype override for .sys files. Solution: Add g:filetype_sys. (Patrick Meiser-Knosowski, closes vim/vim#10181) https://github.com/vim/vim/commit/f420ff2440a009acd9573fdb6ad6d53509d78009 --- src/nvim/testdir/test_filetype.vim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 197a9edb76..85d9a75824 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -1474,7 +1474,7 @@ endfunc func Test_sc_file() filetype on - " SC file mehtods are defined 'Class : Method' + " SC file methods are defined 'Class : Method' call writefile(['SCNvimDocRenderer : SCDocHTMLRenderer {'], 'srcfile.sc') split srcfile.sc call assert_equal('supercollider', &filetype) @@ -1561,6 +1561,13 @@ func Test_sys_file() call assert_equal('bat', &filetype) bwipe! + " Users preference set by g:filetype_sys + let g:filetype_sys = 'sys' + split sysfile.sys + call assert_equal('sys', &filetype) + unlet g:filetype_sys + bwipe! + " RAPID header start with a line containing only "%%%", " but is not always present. call writefile(['%%%'], 'sysfile.sys') -- cgit From 4503cb6b642e0b1ba8157154af6a220387e607f0 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 15 Apr 2022 11:15:47 +0200 Subject: vim-patch:8.2.4750: small pieces of dead code (#18113) Problem: Small pieces of dead code. Solution: Remove the dead code. (Goc Dundar, closes vim/vim#10190) Rename the qftf_cb struct member to avoid confusion. https://github.com/vim/vim/commit/b836658a04ee5456deca2ee523de9efe51252da3 --- src/nvim/ex_cmds.c | 6 ++---- src/nvim/option.c | 4 +--- src/nvim/os/env.c | 2 +- src/nvim/quickfix.c | 20 ++++++++++---------- 4 files changed, 14 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 98aabe89b3..61bd9571b5 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4328,7 +4328,7 @@ skip: } \ kv_push(preview_lines.subresults, current_match); \ } \ - } while (0) + } while (0) // Push the match to preview_lines. PUSH_PREVIEW_LINES(); @@ -4593,9 +4593,7 @@ void ex_global(exarg_T *eap) return; } else { delim = *cmd; // get the delimiter - if (delim) { - ++cmd; // skip delimiter if there is one - } + cmd++; // skip delimiter if there is one pat = cmd; // remember start of pattern cmd = skip_regexp(cmd, delim, p_magic, &eap->arg); if (cmd[0] == delim) { // end delimiter found diff --git a/src/nvim/option.c b/src/nvim/option.c index f6037fc20a..83d56c01fe 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2661,9 +2661,7 @@ ambw_end: int x2 = -1; int x3 = -1; - if (*p != NUL) { - p += utfc_ptr2len(p); - } + p += utfc_ptr2len(p); if (*p != NUL) { x2 = *p++; } diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index b738d36234..e958a39ad2 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -715,7 +715,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo int c = (int)STRLEN(var); // if var[] ends in a path separator and tail[] starts // with it, skip a character - if (*var != NUL && after_pathsep((char *)dst, (char *)dst + c) + if (after_pathsep((char *)dst, (char *)dst + c) #if defined(BACKSLASH_IN_FILENAME) && dst[-1] != ':' #endif diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 4ad5e40fee..f8d2d37a91 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -103,7 +103,7 @@ typedef struct qf_list_S { char_u *qf_title; ///< title derived from the command that created ///< the error list or set by setqflist typval_T *qf_ctx; ///< context set by setqflist/setloclist - Callback qftf_cb; ///< 'quickfixtextfunc' callback function + Callback qf_qftf_cb; ///< 'quickfixtextfunc' callback function struct dir_stack_T *qf_dir_stack; char_u *qf_directory; @@ -2040,7 +2040,7 @@ static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl) } else { to_qfl->qf_ctx = NULL; } - callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb); + callback_copy(&to_qfl->qf_qftf_cb, &from_qfl->qf_qftf_cb); if (from_qfl->qf_count) { if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) { @@ -3448,7 +3448,7 @@ static void qf_free(qf_list_T *qfl) XFREE_CLEAR(qfl->qf_title); tv_free(qfl->qf_ctx); qfl->qf_ctx = NULL; - callback_free(&qfl->qftf_cb); + callback_free(&qfl->qf_qftf_cb); qfl->qf_id = 0; qfl->qf_changedtick = 0L; } @@ -4109,10 +4109,10 @@ static list_T *call_qftf_func(qf_list_T *qfl, int qf_winid, long start_idx, long // If 'quickfixtextfunc' is set, then use the user-supplied function to get // the text to display. Use the local value of 'quickfixtextfunc' if it is // set. - if (qfl->qftf_cb.type != kCallbackNone) { - cb = &qfl->qftf_cb; + if (qfl->qf_qftf_cb.type != kCallbackNone) { + cb = &qfl->qf_qftf_cb; } - if (cb != NULL && cb->type != kCallbackNone) { + if (cb->type != kCallbackNone) { typval_T args[1]; typval_T rettv; @@ -6258,10 +6258,10 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict) { int status; - if (qfl->qftf_cb.type != kCallbackNone) { + if (qfl->qf_qftf_cb.type != kCallbackNone) { typval_T tv; - callback_put(&qfl->qftf_cb, &tv); + callback_put(&qfl->qf_qftf_cb, &tv); status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv); tv_clear(&tv); } else { @@ -6361,9 +6361,9 @@ static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di) { Callback cb; - callback_free(&qfl->qftf_cb); + callback_free(&qfl->qf_qftf_cb); if (callback_from_typval(&cb, &di->di_tv)) { - qfl->qftf_cb = cb; + qfl->qf_qftf_cb = cb; } return OK; } -- cgit From e63e5d1dbd3dd4711efa0ecf9e844ff308b370a6 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Fri, 15 Apr 2022 12:35:06 +0200 Subject: docs: typo fixes (#17859) Co-authored-by: Elias Alves Moura Co-authored-by: venkatesh Co-authored-by: zeertzjq Co-authored-by: Vikas Raj <24727447+numToStr@users.noreply.github.com> Co-authored-by: Steve Vermeulen Co-authored-by: Evgeni Chasnovski Co-authored-by: rwxd Co-authored-by: casswedson <58050969+casswedson@users.noreply.github.com> --- src/nvim/README.md | 2 +- src/nvim/api/autocmd.c | 6 +++--- src/nvim/api/window.c | 3 +-- src/nvim/ex_getln.c | 2 +- src/nvim/highlight_group.c | 2 +- src/nvim/memline.c | 6 ++---- src/nvim/normal.c | 2 +- src/nvim/screen.c | 2 +- src/nvim/screen.h | 2 +- src/nvim/testdir/test_breakindent.vim | 2 +- src/nvim/testdir/test_cmdline.vim | 2 +- src/nvim/testdir/test_debugger.vim | 2 +- src/nvim/testdir/test_increment.vim | 2 +- src/nvim/testdir/test_quickfix.vim | 2 +- src/nvim/testdir/test_regexp_latin.vim | 2 +- src/nvim/testdir/test_sort.vim | 2 +- 16 files changed, 19 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/README.md b/src/nvim/README.md index 4efb42b896..c7cb233c73 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -68,7 +68,7 @@ Configure the sanitizer(s) via these environment variables: export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" # Show backtraces in the logs. export UBSAN_OPTIONS=print_stacktrace=1 - export MSAN_OPTIONS="log_path=${HOME}/logs/tsan" + export MSAN_OPTIONS="log_path=${HOME}/logs/msan" export TSAN_OPTIONS="log_path=${HOME}/logs/tsan" Logs will be written to `${HOME}/logs/*san.PID` then. diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c index ccf4ae3d02..a012f3d7fc 100644 --- a/src/nvim/api/autocmd.c +++ b/src/nvim/api/autocmd.c @@ -360,10 +360,10 @@ cleanup: /// pattern = { "*.py", "*.pyi" } ///
/// -/// Examples values for event: +/// Example values for event: ///
-///   "BufPreWrite"
-///   {"CursorHold", "BufPreWrite", "BufPostWrite"}
+///   "BufWritePre"
+///   {"CursorHold", "BufWritePre", "BufWritePost"}
 /// 
/// /// @param event (string|array) The event or events to register this autocommand diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index fd33a82be3..3a3a65f812 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -139,8 +139,7 @@ Integer nvim_win_get_height(Window window, Error *err) return win->w_height; } -/// Sets the window height. This will only succeed if the screen is split -/// horizontally. +/// Sets the window height. /// /// @param window Window handle, or 0 for current window /// @param height Height as a count of rows diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a7c0b06050..49bd8e7b21 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5852,7 +5852,7 @@ HistoryType get_histtype(const char *const name, const size_t len, const bool re static int last_maptick = -1; // last seen maptick /// Add the given string to the given history. If the string is already in the -/// history then it is moved to the front. "histype" may be one of he HIST_ +/// history then it is moved to the front. "histype" may be one of the HIST_ /// values. /// /// @parma in_map consider maptick when inside a mapping diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index d448f3a646..3092aaefab 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -446,7 +446,7 @@ const char *const highlight_init_cmdline[] = { "default link NvimInvalidSpacing ErrorMsg", - // Not actually invalid, but we highlight user that he is doing something + // Not actually invalid, but we show the user that they are doing something // wrong. "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue", NULL, diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5f6e1ea273..55480ab384 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -3944,10 +3944,8 @@ static void ml_updatechunk(buf_T *buf, linenr_T line, long len, int updtype) } else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL && curix == buf->b_ml.ml_usedchunks - 1 && buf->b_ml.ml_line_count - line <= 1) { - /* - * We are in the last chunk and it is cheap to crate a new one - * after this. Do it now to avoid the loop above later on - */ + // We are in the last chunk and it is cheap to create a new one + // after this. Do it now to avoid the loop above later on curchnk = buf->b_ml.ml_chunksize + curix + 1; buf->b_ml.ml_usedchunks++; if (line == buf->b_ml.ml_line_count) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index a8769c4c80..d6b3b53c86 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4533,7 +4533,7 @@ static void nv_scroll(cmdarg_T *cap) validate_botline(curwin); // make sure w_empty_rows is valid half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { - // Count half he number of filler lines to be "below this + // Count half the number of filler lines to be "below this // line" and half to be "above the next line". if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) { n--; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5a33ebfb37..c6fd9e5dff 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4840,7 +4840,7 @@ static int get_corner_sep_connector(win_T *wp, WindowCorner corner) } } -/// Draw seperator connecting characters on the corners of window "wp" +/// Draw separator connecting characters on the corners of window "wp" static void draw_sep_connectors_win(win_T *wp) { // Don't draw separator connectors unless global statusline is enabled and the window has diff --git a/src/nvim/screen.h b/src/nvim/screen.h index 5e86dacd25..330ff1d4a1 100644 --- a/src/nvim/screen.h +++ b/src/nvim/screen.h @@ -27,7 +27,7 @@ typedef enum { WC_BOTTOM_RIGHT } WindowCorner; -/// By default, all widows are draw on a single rectangular grid, represented by +/// By default, all windows are drawn on a single rectangular grid, represented by /// this ScreenGrid instance. In multigrid mode each window will have its own /// grid, then this is only used for global screen elements that hasn't been /// externalized. diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 438edb0257..a37751e748 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -770,7 +770,7 @@ func Test_breakindent20_list() \ "shall make no law ", \ ] call s:compare_lines(expect, lines) - " set mininum indent + " set minimum indent setl briopt=min:5 redraw! let lines = s:screen_lines2(1, 6, 20) diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index c589d941da..5c2f6f8d0b 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -1127,7 +1127,7 @@ func Test_cmdwin_tabpage() tabedit " v8.2.1919 isn't ported yet, so E492 is thrown after E11 here. " v8.2.1183 also isn't ported yet, so we also can't assert E11 directly. - " For now, assert E11 and E492 seperately. When v8.2.1183 is ported, the + " For now, assert E11 and E492 separately. When v8.2.1183 is ported, the " assert for E492 will fail and this workaround should be removed. " call assert_fails("silent norm q/g :I\", 'E11:') call assert_fails("silent norm q/g ", 'E11:') diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim index a396efc09e..e038c0096a 100644 --- a/src/nvim/testdir/test_debugger.vim +++ b/src/nvim/testdir/test_debugger.vim @@ -976,7 +976,7 @@ func Test_debug_backtrace_level() \ 'line 1: let s:file1_var = ''file1''' \ ]) - " step throught the initial declarations + " step through the initial declarations call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] ) call RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] ) call RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) diff --git a/src/nvim/testdir/test_increment.vim b/src/nvim/testdir/test_increment.vim index 6d08cd40a8..12fe52f057 100644 --- a/src/nvim/testdir/test_increment.vim +++ b/src/nvim/testdir/test_increment.vim @@ -285,7 +285,7 @@ endfunc " 1 " 1 " 1 -" Expexted: +" Expected: " 1) g Ctrl-A on block selected indented lines " 2 " 1 diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 29722ef09b..e43db4d692 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -5049,7 +5049,7 @@ func Test_quickfix_window_fails_to_open() call delete('XquickfixFails') endfunc -" Test for updating the quickfix buffer whenever the assocaited quickfix list +" Test for updating the quickfix buffer whenever the associated quickfix list " is changed. func Xqfbuf_update(cchar) call s:setup_commands(a:cchar) diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim index 13e44b090f..45e60a6d44 100644 --- a/src/nvim/testdir/test_regexp_latin.vim +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -686,7 +686,7 @@ func Test_matchstr_with_ze() bwipe! endfunc -" Check a pattern with a look beind crossing a line boundary +" Check a pattern with a look behind crossing a line boundary func Test_lookbehind_across_line() new call append(0, ['Behind:', 'asdfasd Date: Fri, 15 Apr 2022 21:21:51 +0800 Subject: vim-patch:8.2.4752: wrong 'statusline' value can cause illegal memory access (#18117) Problem: Wrong 'statusline' value can cause illegal memory access. Solution: Properly check the value. (closes vim/vim#10192) https://github.com/vim/vim/commit/5dc294a7b63ed0e508dd360bc4d98173f1a1aeec Make two error messages static variables. --- src/nvim/option.c | 23 +++++++++++++++-------- src/nvim/testdir/test_options.vim | 8 ++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/nvim/option.c b/src/nvim/option.c index 83d56c01fe..897c12a6c4 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -335,6 +335,9 @@ static char_u SHM_ALL[] = { 0, }; +static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence"); +static char e_unbalanced_groups[] = N_("E542: unbalanced groups"); + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.c.generated.h" #endif @@ -2918,8 +2921,8 @@ ambw_end: curbuf->b_help = (curbuf->b_p_bt[0] == 'h'); redraw_titles(); } - } else if (gvarp == &p_stl || varp == &p_ruf) { - // 'statusline' or 'rulerformat' + } else if (gvarp == &p_stl || varp == &p_tal || varp == &p_ruf) { + // 'statusline', 'tabline' or 'rulerformat' int wid; if (varp == &p_ruf) { // reset ru_wid first @@ -2938,7 +2941,7 @@ ambw_end: errmsg = check_stl_option(p_ruf); } } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') { - // check 'statusline' only if it doesn't start with "%!" + // check 'statusline' or 'tabline' only if it doesn't start with "%!" errmsg = check_stl_option(s); } if (varp == &p_ruf && errmsg == NULL) { @@ -3724,7 +3727,7 @@ static char *set_chars_option(win_T *wp, char_u **varp, bool set) } /// Check validity of options with the 'statusline' format. -/// Return error message or NULL. +/// Return an untranslated error message or NULL. char *check_stl_option(char_u *s) { int groupdepth = 0; @@ -3773,18 +3776,22 @@ char *check_stl_option(char_u *s) return illegal_char(errbuf, sizeof(errbuf), *s); } if (*s == '{') { - int reevaluate = (*s == '%'); - s++; + bool reevaluate = (*++s == '%'); + + if (reevaluate && *++s == '}') { + // "}" is not allowed immediately after "%{%" + return illegal_char(errbuf, sizeof(errbuf), '}'); + } while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) { s++; } if (*s != '}') { - return N_("E540: Unclosed expression sequence"); + return e_unclosed_expression_sequence; } } } if (groupdepth != 0) { - return N_("E542: unbalanced groups"); + return e_unbalanced_groups; } return NULL; } diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 8612b7013b..d16d89ec2e 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -281,8 +281,16 @@ func Test_set_errors() call assert_fails('set rulerformat=%15(%%', 'E542:') call assert_fails('set statusline=%$', 'E539:') call assert_fails('set statusline=%{', 'E540:') + call assert_fails('set statusline=%{%', 'E540:') + call assert_fails('set statusline=%{%}', 'E539:') call assert_fails('set statusline=%(', 'E542:') call assert_fails('set statusline=%)', 'E542:') + call assert_fails('set tabline=%$', 'E539:') + call assert_fails('set tabline=%{', 'E540:') + call assert_fails('set tabline=%{%', 'E540:') + call assert_fails('set tabline=%{%}', 'E539:') + call assert_fails('set tabline=%(', 'E542:') + call assert_fails('set tabline=%)', 'E542:') if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. -- cgit From 3f2e9298bdd971a4d2baa298aff7c6f2c2c1ad1a Mon Sep 17 00:00:00 2001 From: Charlie Groves Date: Fri, 15 Apr 2022 21:58:48 -0400 Subject: chore: remove vestigial sfname freeing (#18123) This freeing is necessary in Vim since the alloc can fail. Since we're using xcalloc, that's not possible and the freeing will never run. --- src/nvim/buffer.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4948e2bb69..4d914acea4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1775,19 +1775,6 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, int fl clear_wininfo(buf); buf->b_wininfo = xcalloc(1, sizeof(wininfo_T)); - if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) { - if (buf->b_sfname != buf->b_ffname) { - XFREE_CLEAR(buf->b_sfname); - } else { - buf->b_sfname = NULL; - } - XFREE_CLEAR(buf->b_ffname); - if (buf != curbuf) { - free_buffer(buf); - } - return NULL; - } - if (buf == curbuf) { // free all things allocated for this buffer buf_freeall(buf, 0); -- cgit From 7a2fcbbbecc96c0179aa9449a1d4e3b5d936a054 Mon Sep 17 00:00:00 2001 From: Dundar Göc Date: Thu, 14 Apr 2022 12:37:17 +0200 Subject: refactor: replace char_u variables and functions with char Work on https://github.com/neovim/neovim/issues/459 --- src/nvim/buffer_defs.h | 12 +-- src/nvim/eval/funcs.c | 4 +- src/nvim/ex_docmd.c | 5 +- src/nvim/menu.c | 230 ++++++++++++++++++++++++------------------------- src/nvim/screen.c | 4 +- 5 files changed, 127 insertions(+), 128 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 375bebc5ac..298bec808c 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1148,15 +1148,15 @@ typedef struct VimMenu vimmenu_T; struct VimMenu { int modes; ///< Which modes is this menu visible for int enabled; ///< for which modes the menu is enabled - char_u *name; ///< Name of menu, possibly translated - char_u *dname; ///< Displayed Name ("name" without '&') - char_u *en_name; ///< "name" untranslated, NULL when + char *name; ///< Name of menu, possibly translated + char *dname; ///< Displayed Name ("name" without '&') + char *en_name; ///< "name" untranslated, NULL when ///< was not translated - char_u *en_dname; ///< NULL when "dname" untranslated + char *en_dname; ///< NULL when "dname" untranslated int mnemonic; ///< mnemonic key (after '&') - char_u *actext; ///< accelerator text (after TAB) + char *actext; ///< accelerator text (after TAB) long priority; ///< Menu order priority - char_u *strings[MENU_MODES]; ///< Mapped string for each mode + char *strings[MENU_MODES]; ///< Mapped string for each mode int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode bool silent[MENU_MODES]; ///< A silent flag for each mode vimmenu_T *children; ///< Children of sub-menu diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 609db3990b..5d192eb4b3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -2182,7 +2182,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const strmodes = tv_get_string(&argvars[1]); modes = get_menu_cmd_modes(strmodes, false, NULL, NULL); } - menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); + menu_get((char *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); } /// "expandcmd()" function @@ -3256,7 +3256,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (xpc.xp_context == EXPAND_MENUS) { - set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false); + set_context_in_menu_cmd(&xpc, "menu", (char *)xpc.xp_pattern, false); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index cbfe6e3789..4ffed77c14 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3610,8 +3610,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) // EX_XFILE: file names are handled above. if (!(ea.argt & EX_XFILE)) { if (context == EXPAND_MENUS) { - return (const char *)set_context_in_menu_cmd(xp, cmd, - (char_u *)arg, forceit); + return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit); } else if (context == EXPAND_COMMANDS) { return arg; } else if (context == EXPAND_MAPPINGS) { @@ -3717,7 +3716,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return (const char *)set_context_in_menu_cmd(xp, cmd, (char_u *)arg, forceit); + return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 0db9d69a7e..0af39b0b0b 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -38,20 +38,20 @@ /// The character for each menu mode -static char_u menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' }; +static char menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' }; -static char_u e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu"); -static char_u e_othermode[] = N_("E328: Menu only exists in another mode"); -static char_u e_nomenu[] = N_("E329: No menu \"%s\""); +static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu"); +static char e_othermode[] = N_("E328: Menu only exists in another mode"); +static char e_nomenu[] = N_("E329: No menu \"%s\""); // Return true if "name" is a window toolbar menu name. -static bool menu_is_winbar(const char_u *const name) +static bool menu_is_winbar(const char *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return (STRNCMP(name, "WinBar", 6) == 0); } -static vimmenu_T **get_root_menu(const char_u *const name) +static vimmenu_T **get_root_menu(const char *const name) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return &root_menu; @@ -63,13 +63,13 @@ void ex_menu(exarg_T *eap) { char *menu_path; int modes; - char_u *map_to; // command mapped to the menu entry + char *map_to; // command mapped to the menu entry int noremap; bool silent = false; int unmenu; - char_u *map_buf; - char_u *arg; - char_u *p; + char *map_buf; + char *arg; + char *p; int i; long pri_tab[MENUDEPTH + 1]; TriState enable = kNone; // kTrue for "menu enable", @@ -77,22 +77,22 @@ void ex_menu(exarg_T *eap) vimmenu_T menuarg; modes = get_menu_cmd_modes((char *)eap->cmd, eap->forceit, &noremap, &unmenu); - arg = eap->arg; + arg = (char *)eap->arg; for (;;) { if (STRNCMP(arg, "