aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/buffer.c80
-rw-r--r--src/nvim/change.c24
-rw-r--r--src/nvim/edit.c14
-rw-r--r--src/nvim/mark_extended.c4
-rw-r--r--src/nvim/ops.c28
-rw-r--r--test/functional/api/mark_extended_spec.lua9
-rw-r--r--test/functional/ui/bufhl_spec.lua155
7 files changed, 272 insertions, 42 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 79f339b3aa..1244b8502c 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -5626,6 +5626,86 @@ void bufhl_mark_adjust(buf_T* buf,
}
}
+/// Adjust a placed highlight for column changes and joined/broken lines
+bool bufhl_mark_col_adjust(buf_T *buf,
+ linenr_T lnum,
+ colnr_T mincol,
+ long lnum_amount,
+ long col_amount)
+{
+ bool moved = false;
+ BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, false);
+ if (!lineinfo) {
+ // Old line empty, nothing to do
+ return false;
+ }
+ // Create the new line below only if needed
+ BufhlLine *lineinfo2 = NULL;
+
+ colnr_T delcol = MAXCOL;
+ if (lnum_amount == 0 && col_amount < 0) {
+ delcol = mincol+(int)col_amount;
+ }
+
+ size_t newidx = 0;
+ for (size_t i = 0; i < kv_size(lineinfo->items); i++) {
+ BufhlItem *item = &kv_A(lineinfo->items, i);
+ bool delete = false;
+ if (item->start >= mincol) {
+ moved = true;
+ item->start += (int)col_amount;
+ if (item->stop < MAXCOL) {
+ item->stop += (int)col_amount;
+ }
+ if (lnum_amount != 0) {
+ if (lineinfo2 == NULL) {
+ lineinfo2 = bufhl_tree_ref(&buf->b_bufhl_info,
+ lnum+lnum_amount, true);
+ }
+ kv_push(lineinfo2->items, *item);
+ delete = true;
+ }
+ } else {
+ if (item->start >= delcol) {
+ moved = true;
+ item->start = delcol;
+ }
+ if (item->stop == MAXCOL || item->stop+1 >= mincol) {
+ if (item->stop == MAXCOL) {
+ if (delcol < MAXCOL
+ && delcol > (colnr_T)STRLEN(ml_get_buf(buf, lnum, false))) {
+ delete = true;
+ }
+ } else {
+ moved = true;
+ item->stop += (int)col_amount;
+ }
+ assert(lnum_amount >= 0);
+ if (lnum_amount > 0) {
+ item->stop = MAXCOL;
+ }
+ } else if (item->stop+1 >= delcol) {
+ moved = true;
+ item->stop = delcol-1;
+ }
+ // we covered the entire range with a visual delete or something
+ if (item->stop < item->start) {
+ delete = true;
+ }
+ }
+
+ if (!delete) {
+ if (i != newidx) {
+ kv_A(lineinfo->items, newidx) = kv_A(lineinfo->items, i);
+ }
+ newidx++;
+ }
+ }
+ kv_size(lineinfo->items) = newidx;
+
+ return moved;
+}
+
/// Get highlights to display at a specific line
///
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 7558055696..8a782c2b20 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -359,6 +359,24 @@ void changed_bytes(linenr_T lnum, colnr_T col)
}
}
+/// insert/delete bytes at column
+///
+/// Like changed_bytes() but also adjust extmark for "added" bytes.
+/// When "added" is negative text was deleted.
+static void inserted_bytes(linenr_T lnum, colnr_T col, int added)
+{
+ if (added > 0) {
+ extmark_col_adjust(curbuf, lnum, col+1, 0, added, kExtmarkUndo);
+ } else if (added < 0) {
+ // TODO(bfredl): next revision of extmarks should handle both these
+ // with the same entry point. Also with more sane params..
+ extmark_col_adjust_delete(curbuf, lnum, col+2,
+ col+(-added)+1, kExtmarkUndo, 0);
+ }
+
+ changed_bytes(lnum, col);
+}
+
/// Appended "count" lines below line "lnum" in the current buffer.
/// Must be called AFTER the change and after mark_adjust().
/// Takes care of marking the buffer to be redrawn and sets the changed flag.
@@ -630,7 +648,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
ml_replace(lnum, newp, false);
// mark the buffer as changed and prepare for displaying
- changed_bytes(lnum, (colnr_T)col);
+ inserted_bytes(lnum, (colnr_T)col, (int)(newlen - oldlen));
// If we're in Insert or Replace mode and 'showmatch' is set, then briefly
// show the match for right parens and braces.
@@ -676,7 +694,7 @@ void ins_str(char_u *s)
assert(bytes >= 0);
memmove(newp + col + newlen, oldp + col, (size_t)bytes);
ml_replace(lnum, newp, false);
- changed_bytes(lnum, col);
+ inserted_bytes(lnum, col, newlen);
curwin->w_cursor.col += newlen;
}
@@ -797,7 +815,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
}
// mark the buffer as changed and prepare for displaying
- changed_bytes(lnum, curwin->w_cursor.col);
+ inserted_bytes(lnum, col, -count);
return OK;
}
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index cd0f3f4b9d..eecea03a19 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -5600,9 +5600,6 @@ insertchar (
do_digraph(buf[i-1]); /* may be the start of a digraph */
buf[i] = NUL;
ins_str(buf);
- extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
- (colnr_T)(curwin->w_cursor.col + 1), 0,
- (long)STRLEN(buf), kExtmarkUndo);
if (flags & INSCHAR_CTRLV) {
redo_literal(*buf);
i = 1;
@@ -5613,9 +5610,6 @@ insertchar (
} else {
int cc;
- extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
- (colnr_T)(curwin->w_cursor.col + 1), 0,
- 1, kExtmarkUndo);
if ((cc = utf_char2len(c)) > 1) {
char_u buf[MB_MAXBYTES + 1];
@@ -8506,14 +8500,6 @@ static bool ins_tab(void)
temp -= get_nolist_virtcol() % temp;
- // Move extmarks
- extmark_col_adjust(curbuf,
- curwin->w_cursor.lnum,
- curwin->w_cursor.col,
- 0,
- temp,
- kExtmarkUndo);
-
/*
* Insert the first space with ins_char(). It will delete one char in
* replace mode. Insert the rest with ins_str(); it will not delete any
diff --git a/src/nvim/mark_extended.c b/src/nvim/mark_extended.c
index 01745f484d..91c2f919ce 100644
--- a/src/nvim/mark_extended.c
+++ b/src/nvim/mark_extended.c
@@ -910,6 +910,9 @@ void extmark_col_adjust(buf_T *buf, linenr_T lnum,
bool marks_moved = extmark_col_adjust_impl(buf, lnum, mincol, lnum_amount,
false, col_amount);
+ marks_moved |= bufhl_mark_col_adjust(buf, lnum, mincol,
+ lnum_amount, col_amount);
+
if (undo == kExtmarkUndo && marks_moved) {
u_extmark_col_adjust(buf, lnum, mincol, lnum_amount, col_amount);
}
@@ -938,6 +941,7 @@ void extmark_col_adjust_delete(buf_T *buf, linenr_T lnum,
marks_moved = extmark_col_adjust_impl(buf, lnum, mincol, 0,
true, (long)endcol);
+ marks_moved |= bufhl_mark_col_adjust(buf, lnum, endcol, 0, mincol-(endcol+1));
// Deletes at the end of the line have different behaviour than the normal
// case when deleted.
// Cleanup any marks that are floating beyond the end of line.
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 2301b2159f..f2d35d5e43 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1644,8 +1644,6 @@ int op_delete(oparg_T *oap)
curwin->w_cursor.col = 0;
(void)del_bytes((colnr_T)n, !virtual_op,
oap->op_type == OP_DELETE && !oap->is_VIsual);
- extmark_col_adjust(curbuf, curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-n, kExtmarkUndo);
curwin->w_cursor = curpos; // restore curwin->w_cursor
(void)do_join(2, false, false, false, false);
}
@@ -1685,7 +1683,6 @@ setmarks:
if (oap->is_VIsual == false) {
endcol = MAX(endcol - 1, mincol);
}
- extmark_col_adjust_delete(curbuf, lnum, mincol, endcol, kExtmarkUndo, 0);
}
return OK;
}
@@ -2279,7 +2276,7 @@ void op_insert(oparg_T *oap, long count1)
colnr_T col = oap->start.col;
for (linenr_T lnum = oap->start.lnum; lnum <= oap->end.lnum; lnum++) {
extmark_col_adjust(curbuf, lnum, col, 0, 1, kExtmarkUndo);
- }
+ }
}
/*
@@ -4279,14 +4276,14 @@ format_lines(
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len, 0, kExtmarkUndo);
+ (long)-next_leader_len, 0, kExtmarkNOOP);
} else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
int indent = (int)getwhitecols_curline();
if (indent > 0) {
- (void)del_bytes(indent, FALSE, FALSE);
+ (void)del_bytes(indent, false, false);
mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent, 0, kExtmarkUndo);
+ (colnr_T)0, 0L, (long)-indent, 0, kExtmarkNOOP);
}
}
curwin->w_cursor.lnum--;
@@ -4951,23 +4948,6 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
curbuf->b_op_end.col--;
}
- // if buf1 wasn't allocated, only a singe ASCII char was changed in-place.
- if (did_change && buf1 != NULL) {
- extmark_col_adjust_delete(curbuf,
- pos->lnum,
- startpos.col + 2,
- endpos.col + 1 + length,
- kExtmarkUndo,
- 0);
- long col_amount = (long)STRLEN(buf1);
- extmark_col_adjust(curbuf,
- pos->lnum,
- startpos.col + 1,
- 0,
- col_amount,
- kExtmarkUndo);
- }
-
theend:
xfree(buf1);
if (visual) {
diff --git a/test/functional/api/mark_extended_spec.lua b/test/functional/api/mark_extended_spec.lua
index 76db9f9d81..1f6c00b7d2 100644
--- a/test/functional/api/mark_extended_spec.lua
+++ b/test/functional/api/mark_extended_spec.lua
@@ -422,7 +422,7 @@ describe('Extmarks buffer api', function()
set_extmark(ns, marks[1], 1, 2)
-- Insert a fullwidth (two col) tilde, NICE
feed('0i~<esc>')
- check_undo_redo(ns, marks[1], 1, 2, 1, 3)
+ check_undo_redo(ns, marks[1], 1, 2, 1, 5)
end)
it('marks move with blockwise inserts #extmarks', function()
@@ -475,6 +475,13 @@ describe('Extmarks buffer api', function()
check_undo_redo(ns, marks[2], 0, 3, 1, 2)
end)
+ it('deleting right before a mark works #extmarks', function()
+ -- op_delete in ops.c
+ set_extmark(ns, marks[1], 0, 2)
+ feed('0lx')
+ check_undo_redo(ns, marks[1], 0, 2, 0, 1)
+ end)
+
it('deleting on a mark works #extmarks', function()
-- op_delete in ops.c
set_extmark(ns, marks[1], 0, 2)
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index 65c5f67726..f589bb0e83 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -217,6 +217,161 @@ describe('Buffer highlighting', function()
|
]])
end)
+
+ it('and adjusting columns', function()
+ -- insert before
+ feed('ggiquite <esc>')
+ screen:expect{grid=[[
+ quite^ a {5:longer} example |
+ in {6:order} to {7:de}{5:monstr}{7:ate} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed('u')
+ screen:expect{grid=[[
+ ^a {5:longer} example |
+ in {6:order} to {7:de}{5:monstr}{7:ate} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 1 change; before #2 {MATCH:.*}|
+ ]]}
+
+ -- change/insert in the middle
+ feed('+fesAAAA')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:ordAAAA^r} to {7:de}{5:monstr}{7:ate} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {7:-- INSERT --} |
+ ]]}
+
+ feed('<esc>tdD')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:ordAAAAr} t^o |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ feed('u')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:ordAAAAr} to^ demonstrate |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 1 change; before #4 {MATCH:.*}|
+ ]]}
+
+ feed('u')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:ord^er} to demonstrate |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 1 change; before #3 {MATCH:.*}|
+ ]]}
+ end)
+
+ it('and joining lines', function()
+ feed('ggJJJ')
+ screen:expect{grid=[[
+ a {5:longer} example in {6:order} to {7:de}{5:monstr}{7:ate}|
+ {7: combin}{8:ing hi}{7:ghlights^ }{8:from diff}{7:erent sou}|
+ {7:rces} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- TODO(bfredl): perhaps better undo
+ feed('uuu')
+ screen:expect{grid=[[
+ ^a longer example |
+ in order to demonstrate |
+ combining highlights |
+ from different sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 1 more line; before #2 {MATCH:.*}|
+ ]]}
+ end)
+
+ it('and splitting lines', function()
+ feed('2Gtti<cr>')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:order} |
+ ^ to {7:de}{5:monstr}{7:ate} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {7:-- INSERT --} |
+ ]]}
+
+ -- TODO(bfredl): keep both "parts" after split, requires proper extmark ranges
+ feed('<esc>tsi<cr>')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:order} |
+ to {7:de}{5:mo} |
+ ^nstrate |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {7:-- INSERT --} |
+ ]]}
+
+ -- TODO(bfredl): perhaps better undo
+ feed('<esc>u')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in {6:order} |
+ to demo{7:^nstrat}{8:e} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ 1 line less; before #3 {MATCH:.*}|
+ ]]}
+
+ feed('<esc>u')
+ screen:expect{grid=[[
+ a {5:longer} example |
+ in order^ to demonstrate |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ 1 line less; before #2 {MATCH:.*}|
+ ]]}
+ end)
end)
it('prioritizes latest added highlight', function()