Merge pull request #27061 from luukvbaal/extmark

fix(extmarks): do not remove invalid marks from decor upon deletion
This commit is contained in:
bfredl
2024-01-19 10:49:13 +01:00
committed by GitHub
6 changed files with 45 additions and 29 deletions

View File

@@ -2594,8 +2594,8 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {*opts})
*nvim_buf_get_extmarks()* *nvim_buf_get_extmarks()*
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts}) nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
Gets |extmarks| (including |signs|) in "traversal order" from a |charwise| Gets |extmarks| in "traversal order" from a |charwise| region defined by
region defined by buffer positions (inclusive, 0-indexed |api-indexing|). buffer positions (inclusive, 0-indexed |api-indexing|).
Region can be given as (row,col) tuples, or valid extmark ids (whose Region can be given as (row,col) tuples, or valid extmark ids (whose
positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
@@ -2611,6 +2611,10 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
the `overlap` option might be useful. Otherwise only the start position of the `overlap` option might be useful. Otherwise only the start position of
an extmark will be considered. an extmark will be considered.
Note: legacy signs placed through the |:sign| commands are implemented as
extmarks and will show up here. Their details array will contain a
`sign_name` field.
Example: >lua Example: >lua
local api = vim.api local api = vim.api
local pos = api.nvim_win_get_cursor(0) local pos = api.nvim_win_get_cursor(0)
@@ -2742,7 +2746,9 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts})
text around the mark was deleted and then restored by text around the mark was deleted and then restored by
undo. Defaults to true. undo. Defaults to true.
• invalidate : boolean that indicates whether to hide the • invalidate : boolean that indicates whether to hide the
extmark if the entirety of its range is deleted. If extmark if the entirety of its range is deleted. For
hidden marks, an "invalid" key is added to the "details"
array of |nvim_buf_get_extmarks()| and family. If
"undo_restore" is false, the extmark is deleted instead. "undo_restore" is false, the extmark is deleted instead.
• priority: a priority value for the highlight group or sign • priority: a priority value for the highlight group or sign
attribute. For example treesitter highlighting uses a attribute. For example treesitter highlighting uses a

View File

@@ -323,8 +323,8 @@ function vim.api.nvim_buf_get_commands(buffer, opts) end
--- @return integer[] --- @return integer[]
function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- Gets `extmarks` (including `signs`) in "traversal order" from a `charwise` --- Gets `extmarks` in "traversal order" from a `charwise` region defined by
--- region defined by buffer positions (inclusive, 0-indexed `api-indexing`). --- buffer positions (inclusive, 0-indexed `api-indexing`).
--- Region can be given as (row,col) tuples, or valid extmark ids (whose --- Region can be given as (row,col) tuples, or valid extmark ids (whose
--- positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) --- positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
--- respectively, thus the following are equivalent: --- respectively, thus the following are equivalent:
@@ -339,6 +339,9 @@ function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- Note: when using extmark ranges (marks with a end_row/end_col position) --- Note: when using extmark ranges (marks with a end_row/end_col position)
--- the `overlap` option might be useful. Otherwise only the start position of --- the `overlap` option might be useful. Otherwise only the start position of
--- an extmark will be considered. --- an extmark will be considered.
--- Note: legacy signs placed through the `:sign` commands are implemented as
--- extmarks and will show up here. Their details array will contain a
--- `sign_name` field.
--- Example: --- Example:
--- ---
--- ```lua --- ```lua
@@ -567,7 +570,9 @@ function vim.api.nvim_buf_line_count(buffer) end
--- text around the mark was deleted and then restored by --- text around the mark was deleted and then restored by
--- undo. Defaults to true. --- undo. Defaults to true.
--- • invalidate : boolean that indicates whether to hide the --- • invalidate : boolean that indicates whether to hide the
--- extmark if the entirety of its range is deleted. If --- extmark if the entirety of its range is deleted. For
--- hidden marks, an "invalid" key is added to the "details"
--- array of `nvim_buf_get_extmarks()` and family. If
--- "undo_restore" is false, the extmark is deleted instead. --- "undo_restore" is false, the extmark is deleted instead.
--- • priority: a priority value for the highlight group or sign --- • priority: a priority value for the highlight group or sign
--- attribute. For example treesitter highlighting uses a --- attribute. For example treesitter highlighting uses a

View File

@@ -215,8 +215,8 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return extmark_to_array(extmark, false, details, hl_name); return extmark_to_array(extmark, false, details, hl_name);
} }
/// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise| /// Gets |extmarks| in "traversal order" from a |charwise| region defined by
/// region defined by buffer positions (inclusive, 0-indexed |api-indexing|). /// buffer positions (inclusive, 0-indexed |api-indexing|).
/// ///
/// Region can be given as (row,col) tuples, or valid extmark ids (whose /// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1) /// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
@@ -234,6 +234,10 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// the `overlap` option might be useful. Otherwise only the start position /// the `overlap` option might be useful. Otherwise only the start position
/// of an extmark will be considered. /// of an extmark will be considered.
/// ///
/// Note: legacy signs placed through the |:sign| commands are implemented
/// as extmarks and will show up here. Their details array will contain a
/// `sign_name` field.
///
/// Example: /// Example:
/// ///
/// ```lua /// ```lua
@@ -434,7 +438,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// if text around the mark was deleted and then restored by undo. /// if text around the mark was deleted and then restored by undo.
/// Defaults to true. /// Defaults to true.
/// - invalidate : boolean that indicates whether to hide the /// - invalidate : boolean that indicates whether to hide the
/// extmark if the entirety of its range is deleted. If /// extmark if the entirety of its range is deleted. For
/// hidden marks, an "invalid" key is added to the "details"
/// array of |nvim_buf_get_extmarks()| and family. If
/// "undo_restore" is false, the extmark is deleted instead. /// "undo_restore" is false, the extmark is deleted instead.
/// - priority: a priority value for the highlight group or sign /// - priority: a priority value for the highlight group or sign
/// attribute. For example treesitter highlighting uses a /// attribute. For example treesitter highlighting uses a

View File

@@ -150,7 +150,11 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
} }
if (mt_decor_any(key)) { if (mt_decor_any(key)) {
buf_decor_remove(buf, key.pos.row, key2.pos.row, mt_decor(key), true); if (mt_invalid(key)) {
decor_free(mt_decor(key));
} else {
buf_decor_remove(buf, key.pos.row, key2.pos.row, mt_decor(key), true);
}
} }
// TODO(bfredl): delete it from current undo header, opportunistically? // TODO(bfredl): delete it from current undo header, opportunistically?
@@ -352,14 +356,12 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
// Push mark to undo header // Push mark to undo header
if (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark))) { if (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark))) {
ExtmarkSavePos pos; ExtmarkSavePos pos = {
pos.mark = mt_lookup_key(mark); .mark = mt_lookup_key(mark),
pos.invalidated = invalidated; .invalidated = invalidated,
pos.old_row = mark.pos.row; .old_row = mark.pos.row,
pos.old_col = mark.pos.col; .old_col = mark.pos.col
pos.row = -1; };
pos.col = -1;
undo.data.savepos = pos; undo.data.savepos = pos;
undo.type = kExtmarkSavePos; undo.type = kExtmarkSavePos;
kv_push(*uvp, undo); kv_push(*uvp, undo);
@@ -393,22 +395,17 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo)
} else if (undo_info.type == kExtmarkSavePos) { } else if (undo_info.type == kExtmarkSavePos) {
ExtmarkSavePos pos = undo_info.data.savepos; ExtmarkSavePos pos = undo_info.data.savepos;
if (undo) { if (undo) {
if (pos.old_row >= 0) { if (pos.old_row >= 0
extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); && extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col)
} && pos.invalidated) {
if (pos.invalidated) {
MarkTreeIter itr[1] = { 0 }; MarkTreeIter itr[1] = { 0 };
MTKey mark = marktree_lookup(curbuf->b_marktree, pos.mark, itr); MTKey mark = marktree_lookup(curbuf->b_marktree, pos.mark, itr);
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID; mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID;
MTPos end = marktree_get_altpos(curbuf->b_marktree, mark, itr); MTPos end = marktree_get_altpos(curbuf->b_marktree, mark, itr);
buf_put_decor(curbuf, mt_decor(mark), mark.pos.row, end.row); buf_put_decor(curbuf, mt_decor(mark), mark.pos.row, end.row);
} }
// Redo
} else {
if (pos.row >= 0) {
extmark_setraw(curbuf, pos.mark, pos.row, pos.col);
}
} }
// No Redo since kExtmarkSplice will move marks back
} else if (undo_info.type == kExtmarkMove) { } else if (undo_info.type == kExtmarkMove) {
ExtmarkMove move = undo_info.data.move; ExtmarkMove move = undo_info.data.move;
if (undo) { if (undo) {

View File

@@ -45,8 +45,6 @@ typedef struct {
uint64_t mark; // raw mark id of the marktree uint64_t mark; // raw mark id of the marktree
int old_row; int old_row;
colnr_T old_col; colnr_T old_col;
int row;
colnr_T col;
bool invalidated; bool invalidated;
} ExtmarkSavePos; } ExtmarkSavePos;

View File

@@ -1712,6 +1712,10 @@ describe('API/extmarks', function()
aaa bbb ccc |*2 aaa bbb ccc |*2
| |
]]) ]])
-- decor is not removed twice
command('d3')
api.nvim_buf_del_extmark(0, ns, 1)
command('silent undo')
-- mark is deleted with undo_restore == false -- mark is deleted with undo_restore == false
set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' }) set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' })
set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' }) set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' })