From 29ad2ebc1688176a1c7acaa81103dac289de0ad1 Mon Sep 17 00:00:00 2001 From: Blaž Hrastnik Date: Mon, 4 May 2020 10:30:59 +0900 Subject: api: set_text: fix validation and some issues fix double free because intermediary lines weren't xmemdup'd. NL-for-NUL dance. Normalize row indices and perform more validation. Adjust the cursor position if it's on the right side of the replacement. Tests and documentation. --- test/functional/api/buffer_spec.lua | 83 +++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) (limited to 'test') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 8ed642b43e..60ff246436 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -392,6 +392,89 @@ describe('api/buf', function() end) end) + describe('nvim_buf_get_lines, nvim_buf_set_text', function() + local get_lines, set_text = curbufmeths.get_lines, curbufmeths.set_text + local line_count = curbufmeths.line_count + + it('works', function() + insert([[ + hello foo! + text + ]]) + + eq({'hello foo!'}, get_lines(0, 1, true)) + + + -- can replace a single word + set_text(0, 6, 0, 9, {'world'}) + eq({'hello world!', 'text'}, get_lines(0, 2, true)) + + -- can replace with multiple lines + local err = set_text(0, 6, 0, 11, {'foo', 'wo', 'more'}) + eq({'hello foo', 'wo', 'more!', 'text'}, get_lines(0, 4, true)) + + -- will join multiple lines if needed + set_text(0, 6, 3, 4, {'bar'}) + eq({'hello bar'}, get_lines(0, 1, true)) + end) + + it('updates the cursor position', function() + insert([[ + hello world! + ]]) + + -- position the cursor on `!` + curwin('set_cursor', {1, 11}) + -- replace 'world' with 'foo' + set_text(0, 6, 0, 11, {'foo'}) + eq('hello foo!', curbuf_depr('get_line', 0)) + -- cursor should be moved left by two columns (replacement is shorter by 2 chars) + eq({1, 9}, curwin('get_cursor')) + end) + + it('can handle NULs', function() + set_text(0, 0, 0, 0, {'ab\0cd'}) + eq('ab\0cd', curbuf_depr('get_line', 0)) + end) + + it('adjusts extmarks', function() + local ns = request('nvim_create_namespace', "my-fancy-plugin") + insert([[ + foo bar + baz + ]]) + local id1 = curbufmeths.set_extmark(ns, 0, 0, 1, {}) + local id2 = curbufmeths.set_extmark(ns, 0, 0, 7, {}) + local id3 = curbufmeths.set_extmark(ns, 0, 1, 1, {}) + set_text(0, 4, 0, 7, {"q"}) + + -- TODO: if we set text at 0,3, what happens to the mark at 0,3 + + eq({'foo q', 'baz'}, get_lines(0, 2, true)) + -- mark before replacement point is unaffected + rv = curbufmeths.get_extmark_by_id(ns, id1) + eq({0, 1}, rv) + -- mark gets shifted back because the replacement was shorter + rv = curbufmeths.get_extmark_by_id(ns, id2) + eq({0, 5}, rv) + -- mark on the next line is unaffected + rv = curbufmeths.get_extmark_by_id(ns, id3) + eq({1, 1}, rv) + + -- replacing the text spanning two lines will adjust the mark on the next line + set_text(0, 3, 1, 3, {"qux"}) + rv = curbufmeths.get_extmark_by_id(ns, id3) + eq({'fooqux', ''}, get_lines(0, 2, true)) + eq({0, 6}, rv) + -- but mark before replacement point is still unaffected + rv = curbufmeths.get_extmark_by_id(ns, id1) + eq({0, 1}, rv) + -- and the mark in the middle was shifted to the end of the insertion + rv = curbufmeths.get_extmark_by_id(ns, id2) + eq({0, 6}, rv) + end) + end) + describe('nvim_buf_get_offset', function() local get_offset = curbufmeths.get_offset it('works', function() -- cgit From 45b14f88db1b332938f4a73be28966ae60563a50 Mon Sep 17 00:00:00 2001 From: Tony Chen Date: Mon, 21 Dec 2020 17:51:52 -0800 Subject: api: set_text: rebase, update to new api, and add more tests --- test/functional/api/buffer_spec.lua | 63 +++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index 60ff246436..dbe585a064 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -409,6 +409,14 @@ describe('api/buf', function() set_text(0, 6, 0, 9, {'world'}) eq({'hello world!', 'text'}, get_lines(0, 2, true)) + -- can insert text + set_text(0, 0, 0, 0, {'well '}) + eq({'well hello world!', 'text'}, get_lines(0, 2, true)) + + -- can delete text + set_text(0, 0, 0, 5, {''}) + eq({'hello world!', 'text'}, get_lines(0, 2, true)) + -- can replace with multiple lines local err = set_text(0, 6, 0, 11, {'foo', 'wo', 'more'}) eq({'hello foo', 'wo', 'more!', 'text'}, get_lines(0, 4, true)) @@ -418,6 +426,43 @@ describe('api/buf', function() eq({'hello bar'}, get_lines(0, 1, true)) end) + pending('can handle multibyte characters', function() + insert([[ + hellØ world! + ]]) + + eq({'hellØ world!'}, get_lines(0, 1, true)) + + -- inserting multibyte + set_text(0, 11, 0, 11, {'Ø'}) + eq({'hellØ worldØ!'}, get_lines(0, 1, true)) + + -- deleting multibyte + set_text(0, 0, 0, 6, {''}) + eq({'worldØ!'}, get_lines(0, 1, true)) + end) + + it('works with undo', function() + insert([[ + hello world! + ]]) + + -- setting text + set_text(0, 0, 0, 0, {'well '}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + + -- deleting text + set_text(0, 0, 0, 6, {''}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + + -- inserting newlines + set_text(0, 0, 0, 0, {'hello', 'mr '}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) + end) + it('updates the cursor position', function() insert([[ hello world! @@ -443,34 +488,34 @@ describe('api/buf', function() foo bar baz ]]) - local id1 = curbufmeths.set_extmark(ns, 0, 0, 1, {}) - local id2 = curbufmeths.set_extmark(ns, 0, 0, 7, {}) - local id3 = curbufmeths.set_extmark(ns, 0, 1, 1, {}) + local id1 = curbufmeths.set_extmark(ns, 0, 1, {}) + local id2 = curbufmeths.set_extmark(ns, 0, 7, {}) + local id3 = curbufmeths.set_extmark(ns, 1, 1, {}) set_text(0, 4, 0, 7, {"q"}) -- TODO: if we set text at 0,3, what happens to the mark at 0,3 eq({'foo q', 'baz'}, get_lines(0, 2, true)) -- mark before replacement point is unaffected - rv = curbufmeths.get_extmark_by_id(ns, id1) + rv = curbufmeths.get_extmark_by_id(ns, id1, {}) eq({0, 1}, rv) -- mark gets shifted back because the replacement was shorter - rv = curbufmeths.get_extmark_by_id(ns, id2) + rv = curbufmeths.get_extmark_by_id(ns, id2, {}) eq({0, 5}, rv) -- mark on the next line is unaffected - rv = curbufmeths.get_extmark_by_id(ns, id3) + rv = curbufmeths.get_extmark_by_id(ns, id3, {}) eq({1, 1}, rv) -- replacing the text spanning two lines will adjust the mark on the next line set_text(0, 3, 1, 3, {"qux"}) - rv = curbufmeths.get_extmark_by_id(ns, id3) + rv = curbufmeths.get_extmark_by_id(ns, id3, {}) eq({'fooqux', ''}, get_lines(0, 2, true)) eq({0, 6}, rv) -- but mark before replacement point is still unaffected - rv = curbufmeths.get_extmark_by_id(ns, id1) + rv = curbufmeths.get_extmark_by_id(ns, id1, {}) eq({0, 1}, rv) -- and the mark in the middle was shifted to the end of the insertion - rv = curbufmeths.get_extmark_by_id(ns, id2) + rv = curbufmeths.get_extmark_by_id(ns, id2, {}) eq({0, 6}, rv) end) end) -- cgit From f7d01a65d50a4783527acd2de3998b65e7b78331 Mon Sep 17 00:00:00 2001 From: chentau Date: Mon, 21 Dec 2020 18:24:15 -0800 Subject: api: set_text: more tests, and fixing lint removing pending virtcol tests Allow passing in empty array as a shorthand for array with empty string; add more documentation add check for start_row as well --- test/functional/api/buffer_spec.lua | 59 +++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 32 deletions(-) (limited to 'test') diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index dbe585a064..fb8ed6a9d7 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -394,7 +394,6 @@ describe('api/buf', function() describe('nvim_buf_get_lines, nvim_buf_set_text', function() local get_lines, set_text = curbufmeths.get_lines, curbufmeths.set_text - local line_count = curbufmeths.line_count it('works', function() insert([[ @@ -418,7 +417,7 @@ describe('api/buf', function() eq({'hello world!', 'text'}, get_lines(0, 2, true)) -- can replace with multiple lines - local err = set_text(0, 6, 0, 11, {'foo', 'wo', 'more'}) + set_text(0, 6, 0, 11, {'foo', 'wo', 'more'}) eq({'hello foo', 'wo', 'more!', 'text'}, get_lines(0, 4, true)) -- will join multiple lines if needed @@ -426,25 +425,10 @@ describe('api/buf', function() eq({'hello bar'}, get_lines(0, 1, true)) end) - pending('can handle multibyte characters', function() - insert([[ - hellØ world! - ]]) - - eq({'hellØ world!'}, get_lines(0, 1, true)) - - -- inserting multibyte - set_text(0, 11, 0, 11, {'Ø'}) - eq({'hellØ worldØ!'}, get_lines(0, 1, true)) - - -- deleting multibyte - set_text(0, 0, 0, 6, {''}) - eq({'worldØ!'}, get_lines(0, 1, true)) - end) - it('works with undo', function() insert([[ hello world! + foo bar ]]) -- setting text @@ -461,6 +445,11 @@ describe('api/buf', function() set_text(0, 0, 0, 0, {'hello', 'mr '}) feed('u') eq({'hello world!'}, get_lines(0, 1, true)) + + -- deleting newlines + set_text(0, 0, 1, 4, {'hello'}) + feed('u') + eq({'hello world!'}, get_lines(0, 1, true)) end) it('updates the cursor position', function() @@ -493,30 +482,36 @@ describe('api/buf', function() local id3 = curbufmeths.set_extmark(ns, 1, 1, {}) set_text(0, 4, 0, 7, {"q"}) - -- TODO: if we set text at 0,3, what happens to the mark at 0,3 - eq({'foo q', 'baz'}, get_lines(0, 2, true)) -- mark before replacement point is unaffected - rv = curbufmeths.get_extmark_by_id(ns, id1, {}) - eq({0, 1}, rv) + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) -- mark gets shifted back because the replacement was shorter - rv = curbufmeths.get_extmark_by_id(ns, id2, {}) - eq({0, 5}, rv) + eq({0, 5}, curbufmeths.get_extmark_by_id(ns, id2, {})) -- mark on the next line is unaffected - rv = curbufmeths.get_extmark_by_id(ns, id3, {}) - eq({1, 1}, rv) + eq({1, 1}, curbufmeths.get_extmark_by_id(ns, id3, {})) -- replacing the text spanning two lines will adjust the mark on the next line set_text(0, 3, 1, 3, {"qux"}) - rv = curbufmeths.get_extmark_by_id(ns, id3, {}) eq({'fooqux', ''}, get_lines(0, 2, true)) - eq({0, 6}, rv) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id3, {})) -- but mark before replacement point is still unaffected - rv = curbufmeths.get_extmark_by_id(ns, id1, {}) - eq({0, 1}, rv) + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) -- and the mark in the middle was shifted to the end of the insertion - rv = curbufmeths.get_extmark_by_id(ns, id2, {}) - eq({0, 6}, rv) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id2, {})) + + -- marks should be put back into the same place after undoing + set_text(0, 0, 0, 2, {''}) + feed('u') + eq({0, 1}, curbufmeths.get_extmark_by_id(ns, id1, {})) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id2, {})) + eq({0, 6}, curbufmeths.get_extmark_by_id(ns, id3, {})) + + -- marks should be shifted over by the correct number of bytes for multibyte + -- chars + set_text(0, 0, 0, 0, {'Ø'}) + eq({0, 3}, curbufmeths.get_extmark_by_id(ns, id1, {})) + eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id2, {})) + eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id3, {})) end) end) -- cgit From 39d098f9f9dc244a84958202e221ed0bdc6ee88a Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Wed, 30 Dec 2020 11:31:17 +0100 Subject: api: set_text: fix some byte count issues add byte count tests update documentation --- test/functional/lua/buffer_updates_spec.lua | 67 ++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 9f2b1b6e52..67dc5f5a16 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -288,7 +288,8 @@ describe('lua: nvim_buf_attach on_bytes', function() -- TODO: while we are brewing the real strong coffe, -- verify should check buf_get_offset after every check_events if verify then - meths.buf_get_offset(0, meths.buf_line_count(0)) + local len = meths.buf_get_offset(0, meths.buf_line_count(0)) + eq(len == -1 and 1 or len, string.len(shadowbytes)) end exec_lua("return test_register(...)", 0, "test1", false, false, true) meths.buf_get_changedtick(0) @@ -510,6 +511,70 @@ describe('lua: nvim_buf_attach on_bytes', function() { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 1, 1 }; } end) + + it('nvim_buf_set_text insert', function() + local check_events = setup_eventcheck(verify, {"bastext"}) + meths.buf_set_text(0, 0, 3, 0, 3, {"fiol","kontra"}) + check_events { + { "test1", "bytes", 1, 3, 0, 3, 3, 0, 0, 0, 1, 6, 11 }; + } + + meths.buf_set_text(0, 1, 6, 1, 6, {"punkt","syntgitarr","övnings"}) + check_events { + { "test1", "bytes", 1, 4, 1, 6, 14, 0, 0, 0, 2, 8, 25 }; + } + + eq({ "basfiol", "kontrapunkt", "syntgitarr", "övningstext" }, + meths.buf_get_lines(0, 0, -1, true)) + end) + + it('nvim_buf_set_text replace', function() + local check_events = setup_eventcheck(verify, origlines) + + meths.buf_set_text(0, 2, 3, 2, 8, {"very text"}) + check_events { + { "test1", "bytes", 1, 3, 2, 3, 35, 0, 5, 5, 0, 9, 9 }; + } + + meths.buf_set_text(0, 3, 5, 3, 7, {" splitty","line "}) + check_events { + { "test1", "bytes", 1, 4, 3, 5, 57, 0, 2, 2, 1, 5, 14 }; + } + + meths.buf_set_text(0, 0, 8, 1, 2, {"JOINY"}) + check_events { + { "test1", "bytes", 1, 5, 0, 8, 8, 1, 2, 10, 0, 5, 5 }; + } + + meths.buf_set_text(0, 4, 0, 6, 0, {"was 5,6",""}) + check_events { + { "test1", "bytes", 1, 6, 4, 0, 75, 2, 0, 32, 1, 0, 8 }; + } + + eq({ "originalJOINYiginal line 2", "orivery text line 3", "origi splitty", + "line l line 4", "was 5,6", " indented line" }, + meths.buf_get_lines(0, 0, -1, true)) + + end) + + it('nvim_buf_set_text delete', function() + local check_events = setup_eventcheck(verify, origlines) + + -- really {""} but accepts {} as a shorthand + meths.buf_set_text(0, 0, 0, 1, 0, {}) + check_events { + { "test1", "bytes", 1, 3, 0, 0, 0, 1, 0, 16, 0, 0, 0 }; + } + + -- TODO(bfredl): this works but is not as convenient as set_lines + meths.buf_set_text(0, 4, 15, 5, 17, {""}) + check_events { + { "test1", "bytes", 1, 4, 4, 15, 79, 1, 17, 18, 0, 0, 0 }; + } + eq({ "original line 2", "original line 3", "original line 4", + "original line 5", "original line 6" }, + meths.buf_get_lines(0, 0, -1, true)) + end) end describe('(with verify) handles', function() -- cgit