From 48a869dc6d29514e943070da9f22f702f5179826 Mon Sep 17 00:00:00 2001 From: Björn Linse Date: Mon, 20 Jan 2020 19:29:12 +0100 Subject: shed biking: it's always extmarks, never marks extended --- src/nvim/api/buffer.c | 2 +- src/nvim/api/private/helpers.c | 2 +- src/nvim/api/vim.c | 2 +- src/nvim/buffer.c | 2 +- src/nvim/change.c | 2 +- src/nvim/edit.c | 2 +- src/nvim/ex_cmds.c | 2 +- src/nvim/extmark.c | 910 +++++++++++++++++++++++++++++++++++++++++ src/nvim/extmark.h | 93 +++++ src/nvim/extmark_defs.h | 37 ++ src/nvim/fold.c | 2 +- src/nvim/indent.c | 2 +- src/nvim/map.h | 2 +- src/nvim/mark.c | 2 +- src/nvim/mark.h | 2 +- src/nvim/mark_extended.c | 910 ----------------------------------------- src/nvim/mark_extended.h | 93 ----- src/nvim/mark_extended_defs.h | 37 -- src/nvim/ops.c | 2 +- src/nvim/screen.c | 2 +- src/nvim/undo.c | 2 +- src/nvim/undo_defs.h | 2 +- 22 files changed, 1056 insertions(+), 1056 deletions(-) create mode 100644 src/nvim/extmark.c create mode 100644 src/nvim/extmark.h create mode 100644 src/nvim/extmark_defs.h delete mode 100644 src/nvim/mark_extended.c delete mode 100644 src/nvim/mark_extended.h delete mode 100644 src/nvim/mark_extended_defs.h (limited to 'src') diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 3106011fe2..a666ed92da 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -26,7 +26,7 @@ #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/move.h" #include "nvim/syntax.h" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 37e31e0807..a1745ef777 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -24,7 +24,7 @@ #include "nvim/eval/typval.h" #include "nvim/map_defs.h" #include "nvim/map.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/option.h" #include "nvim/option_defs.h" #include "nvim/version.h" diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index baa0387fd8..9c58ce853b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -40,7 +40,7 @@ #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/state.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/syntax.h" #include "nvim/getchar.h" #include "nvim/os/input.h" diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 837fcb5cc1..eb71a8fc5e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -53,7 +53,7 @@ #include "nvim/indent_c.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/change.c b/src/nvim/change.c index 7eb6ea7328..a341b8fce1 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -17,7 +17,7 @@ #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/memline.h" #include "nvim/misc1.h" #include "nvim/move.h" diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 0c183add16..68fa99484c 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -28,7 +28,7 @@ #include "nvim/indent.h" #include "nvim/indent_c.h" #include "nvim/main.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 53caaa6a67..bc6821f60f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -40,7 +40,7 @@ #include "nvim/buffer_updates.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/message.h" diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c new file mode 100644 index 0000000000..d60723c755 --- /dev/null +++ b/src/nvim/extmark.c @@ -0,0 +1,910 @@ +// 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 + +// Implements extended marks for plugins. Each mark exists in a btree of +// lines containing btrees of columns. +// +// The btree provides efficient range lookups. +// A map of pointers to the marks is used for fast lookup by mark id. +// +// Marks are moved by calls to extmark_splice. Additionally mark_adjust +// might adjust extmarks to line inserts/deletes. +// +// Undo/Redo of marks is implemented by storing the call arguments to +// extmark_splice. The list of arguments is applied in extmark_apply_undo. +// The only case where we have to copy extmarks is for the area being effected +// by a delete. +// +// Marks live in namespaces that allow plugins/users to segregate marks +// from other users. +// +// For possible ideas for efficency improvements see: +// http://blog.atom.io/2015/06/16/optimizing-an-important-atom-primitive.html +// TODO(bfredl): These ideas could be used for an enhanced btree, which +// wouldn't need separate line and column layers. +// Other implementations exist in gtk and tk toolkits. +// +// Deleting marks only happens when explicitly calling extmark_del, deleteing +// over a range of marks will only move the marks. Deleting on a mark will +// leave it in same position unless it is on the EOL of a line. + +#include +#include "nvim/api/vim.h" +#include "nvim/vim.h" +#include "nvim/charset.h" +#include "nvim/extmark.h" +#include "nvim/buffer_updates.h" +#include "nvim/memline.h" +#include "nvim/pos.h" +#include "nvim/globals.h" +#include "nvim/map.h" +#include "nvim/lib/kbtree.h" +#include "nvim/undo.h" +#include "nvim/buffer.h" +#include "nvim/syntax.h" +#include "nvim/highlight.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "extmark.c.generated.h" +#endif + +static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { + if (!buf->b_extmark_ns) { + if (!put) { + return NULL; + } + buf->b_extmark_ns = map_new(uint64_t, ExtmarkNs)(); + buf->b_extmark_index = map_new(uint64_t, ExtmarkItem)(); + } + + ExtmarkNs *ns = map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); + if (put && ns->map == NULL) { + ns->map = map_new(uint64_t, uint64_t)(); + ns->free_id = 1; + } + return ns; +} + + +/// Create or update an extmark +/// +/// must not be used during iteration! +/// @returns the mark id +uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, + int row, colnr_T col, ExtmarkOp op) +{ + ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); + mtpos_t old_pos; + uint64_t mark = 0; + + if (id == 0) { + id = ns->free_id++; + } else { + uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); + if (old_mark) { + if (old_mark & MARKTREE_PAIRED_FLAG) { + extmark_del(buf, ns_id, id); + } else { + // TODO(bfredl): we need to do more if "revising" a decoration mark. + MarkTreeIter itr[1]; + old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); + assert(itr->node); + if (old_pos.row == row && old_pos.col == col) { + map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, old_mark); + mark = marktree_revise(buf->b_marktree, itr); + goto revised; + } + marktree_del_itr(buf->b_marktree, itr, false); + } + } else { + ns->free_id = MAX(ns->free_id, id+1); + } + } + + mark = marktree_put(buf->b_marktree, row, col, true); +revised: + map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, + (ExtmarkItem){ ns_id, id, 0, + KV_INITIAL_VALUE }); + map_put(uint64_t, uint64_t)(ns->map, id, mark); + + 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); + } + return id; +} + +static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) +{ + MarkTreeIter itr[1]; + mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); + if (pos.row == -1) { + return false; + } + + if (pos.row == row && pos.col == col) { + return true; + } + + marktree_move(buf->b_marktree, itr, row, col); + return true; +} + +// Remove an extmark +// Returns 0 on missing id +bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_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) { + return false; + } + + MarkTreeIter itr[1]; + mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); + assert(pos.row >= 0); + marktree_del_itr(buf->b_marktree, itr, false); + ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); + + if (mark & MARKTREE_PAIRED_FLAG) { + mtpos_t pos2 = marktree_lookup(buf->b_marktree, + mark|MARKTREE_END_FLAG, itr); + assert(pos2.row >= 0); + marktree_del_itr(buf->b_marktree, itr, false); + if (item.hl_id && pos2.row >= pos.row) { + redraw_buf_range_later(buf, pos.row+1, pos2.row+1); + } + } + + if (kv_size(item.virt_text)) { + redraw_buf_line_later(buf, pos.row+1); + } + clear_virttext(&item.virt_text); + + 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) +{ + if (!buf->b_extmark_ns) { + return false; + } + + bool marks_cleared = false; + + bool all_ns = (ns_id == 0); + ExtmarkNs *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, uint64_t) *delete_set = NULL; + if (delete_set == NULL) { + delete_set = map_new(uint64_t, uint64_t)(); + } + + MarkTreeIter itr[1]; + 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)) { + break; + } + uint64_t *del_status = map_ref(uint64_t, uint64_t)(delete_set, mark.id, + false); + if (del_status) { + marktree_del_itr(buf->b_marktree, itr, false); + map_del(uint64_t, uint64_t)(delete_set, mark.id); + if (*del_status > 0) { + redraw_buf_range_later(buf, (linenr_T)(*del_status), mark.row+1); + } + 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)) { + if (kv_size(item.virt_text)) { + redraw_buf_line_later(buf, mark.row+1); + } + clear_virttext(&item.virt_text); + marks_cleared = true; + if (mark.id & MARKTREE_PAIRED_FLAG) { + uint64_t other = mark.id ^ MARKTREE_END_FLAG; + uint64_t status = item.hl_id ? ((uint64_t)mark.row+1) : 0; + map_put(uint64_t, uint64_t)(delete_set, other, status); + } + 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, mark.id); + marktree_del_itr(buf->b_marktree, itr, false); + } else { + marktree_itr_next(buf->b_marktree, itr); + } + } + uint64_t id, status; + map_foreach(delete_set, id, status, { + mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); + assert(itr->node); + marktree_del_itr(buf->b_marktree, itr, false); + if (status > 0) { + redraw_buf_range_later(buf, (linenr_T)status, pos.row+1); + } + }); + map_clear(uint64_t, uint64_t)(delete_set); + 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 +ExtmarkArray extmark_get(buf_T *buf, uint64_t ns_id, + int l_row, colnr_T l_col, + int u_row, colnr_T u_col, + int64_t amount, bool reverse) +{ + ExtmarkArray array = KV_INITIAL_VALUE; + MarkTreeIter itr[1]; + // Find all the marks + marktree_itr_get_ext(buf->b_marktree, (mtpos_t){ l_row, l_col }, + itr, reverse, false, NULL); + int order = reverse ? -1 : 1; + while ((int64_t)kv_size(array) < amount) { + mtmark_t mark = marktree_itr_current(itr); + if (mark.row < 0 + || (mark.row - u_row) * order > 0 + || (mark.row == u_row && (mark.col - u_col) * order > 0)) { + break; + } + 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 (reverse) { + marktree_itr_prev(buf->b_marktree, itr); + } else { + marktree_itr_next(buf->b_marktree, itr); + } + } + return array; +} + +// Lookup an extmark by id +ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) +{ + ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); + ExtmarkInfo ret = { 0, 0, -1, -1 }; + if (!ns) { + 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); + assert(pos.row >= 0); + + ret.ns_id = ns_id; + ret.mark_id = id; + ret.row = pos.row; + ret.col = pos.col; + + return ret; +} + + +// free extmarks from the buffer +void extmark_free_all(buf_T *buf) +{ + if (!buf->b_extmark_ns) { + return; + } + + uint64_t id; + ExtmarkNs ns; + ExtmarkItem item; + + marktree_clear(buf->b_marktree); + + map_foreach(buf->b_extmark_ns, id, ns, { + (void)id; + map_free(uint64_t, uint64_t)(ns.map); + }); + map_free(uint64_t, ExtmarkNs)(buf->b_extmark_ns); + buf->b_extmark_ns = NULL; + + map_foreach(buf->b_extmark_index, id, item, { + (void)id; + clear_virttext(&item.virt_text); + }); + map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); + buf->b_extmark_index = NULL; +} + + +// 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); + if (!uhp) { + return; + } + + ExtmarkSavePos pos; + pos.mark = mark; + pos.old_row = -1; + pos.old_col = -1; + pos.row = row; + pos.col = col; + + ExtmarkUndoObject undo = { .type = kExtmarkSavePos, + .data.savepos = pos }; + + kv_push(uhp->uh_extmark, undo); +} + +/// copy extmarks data between range +/// +/// useful when we cannot simply reverse the operation. This will do nothing on +/// redo, enforces correct position when undo. +void u_extmark_copy(buf_T *buf, + int l_row, colnr_T l_col, + int u_row, colnr_T u_col) +{ + u_header_T *uhp = u_force_get_undo_header(buf); + if (!uhp) { + return; + } + + ExtmarkUndoObject undo; + + MarkTreeIter itr[1]; + 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)) { + break; + } + ExtmarkSavePos pos; + pos.mark = mark.id; + pos.old_row = mark.row; + pos.old_col = mark.col; + pos.row = -1; + pos.col = -1; + + undo.data.savepos = pos; + undo.type = kExtmarkSavePos; + kv_push(uhp->uh_extmark, undo); + + marktree_itr_next(buf->b_marktree, itr); + } +} + +/// undo or redo an extmark operation +void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) +{ + // splice: any text operation changing position (except :move) + if (undo_info.type == kExtmarkSplice) { + // Undo + ExtmarkSplice splice = undo_info.data.splice; + if (undo) { + extmark_splice(curbuf, + splice.start_row, splice.start_col, + splice.newextent_row, splice.newextent_col, + splice.oldextent_row, splice.oldextent_col, + kExtmarkNoUndo); + + } else { + extmark_splice(curbuf, + splice.start_row, splice.start_col, + splice.oldextent_row, splice.oldextent_col, + splice.newextent_row, splice.newextent_col, + kExtmarkNoUndo); + } + // kExtmarkSavePos + } else if (undo_info.type == kExtmarkSavePos) { + ExtmarkSavePos pos = undo_info.data.savepos; + if (undo) { + if (pos.old_row >= 0) { + extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); + } + // Redo + } else { + if (pos.row >= 0) { + extmark_setraw(curbuf, pos.mark, pos.row, pos.col); + } + } + } else if (undo_info.type == kExtmarkMove) { + ExtmarkMove move = undo_info.data.move; + if (undo) { + extmark_move_region(curbuf, + move.new_row, move.new_col, + move.extent_row, move.extent_col, + move.start_row, move.start_col, + kExtmarkNoUndo); + } else { + extmark_move_region(curbuf, + move.start_row, move.start_col, + move.extent_row, move.extent_col, + move.new_row, move.new_col, + kExtmarkNoUndo); + } + } +} + + +// 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) +{ + if (!curbuf_splice_pending) { + int old_extent, new_extent; + if (amount == MAXLNUM) { + old_extent = (int)(line2 - line1+1); + new_extent = (int)(amount_after + old_extent); + } else { + // A region is either deleted (amount == MAXLNUM) or + // added (line2 == MAXLNUM). The only other case is :move + // which is handled by a separate entry point extmark_move_region. + assert(line2 == MAXLNUM); + old_extent = 0; + new_extent = (int)amount; + } + extmark_splice(buf, + (int)line1-1, 0, + old_extent, 0, + new_extent, 0, undo); + } +} + +void extmark_splice(buf_T *buf, + int start_row, colnr_T start_col, + int oldextent_row, colnr_T oldextent_col, + int newextent_row, colnr_T newextent_col, + ExtmarkOp undo) +{ + buf_updates_send_splice(buf, start_row, start_col, + oldextent_row, oldextent_col, + newextent_row, newextent_col); + + if (undo == kExtmarkUndo && (oldextent_row > 0 || oldextent_col > 0)) { + // Copy marks that would be effected by delete + // TODO(bfredl): Be "smart" about gravity here, left-gravity at the + // beginning and right-gravity at the end need not be preserved. + // Also be smart about marks that already have been saved (important for + // merge!) + int end_row = start_row + oldextent_row; + int end_col = (oldextent_row ? 0 : start_col) + oldextent_col; + u_extmark_copy(buf, start_row, start_col, end_row, end_col); + } + + + marktree_splice(buf->b_marktree, start_row, start_col, + oldextent_row, oldextent_col, + newextent_row, newextent_col); + + if (undo == kExtmarkUndo) { + u_header_T *uhp = u_force_get_undo_header(buf); + if (!uhp) { + return; + } + + bool merged = false; + // TODO(bfredl): this is quite rudimentary. We merge small (within line) + // inserts with each other and small deletes with each other. Add full + // merge algorithm later. + if (oldextent_row == 0 && newextent_row == 0 && kv_size(uhp->uh_extmark)) { + ExtmarkUndoObject *item = &kv_A(uhp->uh_extmark, + kv_size(uhp->uh_extmark)-1); + if (item->type == kExtmarkSplice) { + ExtmarkSplice *splice = &item->data.splice; + if (splice->start_row == start_row && splice->oldextent_row == 0 + && splice->newextent_row == 0) { + if (oldextent_col == 0 && start_col >= splice->start_col + && start_col <= splice->start_col+splice->newextent_col) { + splice->newextent_col += newextent_col; + merged = true; + } else if (newextent_col == 0 + && start_col == splice->start_col+splice->newextent_col) { + splice->oldextent_col += oldextent_col; + merged = true; + } else if (newextent_col == 0 + && start_col + oldextent_col == splice->start_col) { + splice->start_col = start_col; + splice->oldextent_col += oldextent_col; + merged = true; + } + } + } + } + + if (!merged) { + ExtmarkSplice splice; + splice.start_row = start_row; + splice.start_col = start_col; + splice.oldextent_row = oldextent_row; + splice.oldextent_col = oldextent_col; + splice.newextent_row = newextent_row; + splice.newextent_col = newextent_col; + + kv_push(uhp->uh_extmark, + ((ExtmarkUndoObject){ .type = kExtmarkSplice, + .data.splice = splice })); + } + } +} + + +void extmark_move_region(buf_T *buf, + int start_row, colnr_T start_col, + int extent_row, colnr_T extent_col, + int new_row, colnr_T new_col, + ExtmarkOp undo) +{ + // TODO(bfredl): this is not synced to the buffer state inside the callback. + // But unless we make the undo implementation smarter, this is not ensured + // anyway. + buf_updates_send_splice(buf, start_row, start_col, + extent_row, extent_col, + 0, 0); + + marktree_move_region(buf->b_marktree, start_row, start_col, + extent_row, extent_col, + new_row, new_col); + + buf_updates_send_splice(buf, new_row, new_col, + 0, 0, + extent_row, extent_col); + + + if (undo == kExtmarkUndo) { + u_header_T *uhp = u_force_get_undo_header(buf); + if (!uhp) { + return; + } + + ExtmarkMove move; + move.start_row = start_row; + move.start_col = start_col; + move.extent_row = extent_row; + move.extent_col = extent_col; + move.new_row = new_row; + move.new_col = new_col; + + kv_push(uhp->uh_extmark, + ((ExtmarkUndoObject){ .type = kExtmarkMove, + .data.move = move })); + } +} + +uint64_t src2ns(Integer *src_id) +{ + if (*src_id == 0) { + *src_id = (Integer)nvim_create_namespace((String)STRING_INIT); + } + if (*src_id < 0) { + return UINT64_MAX; + } else { + return (uint64_t)(*src_id); + } +} + +/// Adds a decoration to a buffer. +/// +/// Unlike matchaddpos() highlights, these follow changes to the the buffer +/// texts. Decorations are represented internally and in the API as extmarks. +/// +/// @param buf The buffer to add decorations to +/// @param ns_id A valid namespace id. +/// @param hl_id Id of the highlight group to use (or zero) +/// @param start_row The line to highlight +/// @param start_col First column to highlight +/// @param end_row The line to highlight +/// @param end_col The last column to highlight +/// @param virt_text Virtual text (currently placed at the EOL of start_row) +/// @return The extmark id inside the namespace +uint64_t extmark_add_decoration(buf_T *buf, uint64_t ns_id, int hl_id, + int start_row, colnr_T start_col, + int end_row, colnr_T end_col, + VirtText virt_text) +{ + ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); + ExtmarkItem item; + item.ns_id = ns_id; + item.mark_id = ns->free_id++; + item.hl_id = hl_id; + item.virt_text = virt_text; + + uint64_t mark; + + if (end_row > -1) { + mark = marktree_put_pair(buf->b_marktree, + start_row, start_col, true, + end_row, end_col, false); + } else { + mark = marktree_put(buf->b_marktree, start_row, start_col, true); + } + + map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, item); + map_put(uint64_t, uint64_t)(ns->map, item.mark_id, mark); + + redraw_buf_range_later(buf, start_row+1, + (end_row >= 0 ? end_row : start_row) + 1); + return item.mark_id; +} + +/// Add highlighting to a buffer, bounded by two cursor positions, +/// with an offset. +/// +/// @param buf Buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Highlight group id +/// @param pos_start Cursor position to start the hightlighting at +/// @param pos_end Cursor position to end the highlighting at +/// @param offset Move the whole highlighting this many columns to the right +void bufhl_add_hl_pos_offset(buf_T *buf, + int src_id, + int hl_id, + lpos_T pos_start, + lpos_T pos_end, + colnr_T offset) +{ + colnr_T hl_start = 0; + colnr_T hl_end = 0; + + // TODO(bfredl): if decoration had blocky mode, we could avoid this loop + for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { + if (pos_start.lnum < lnum && lnum < pos_end.lnum) { + hl_start = offset-1; + hl_end = MAXCOL; + } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { + hl_start = pos_start.col + offset; + hl_end = MAXCOL; + } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { + hl_start = offset-1; + hl_end = pos_end.col + offset; + } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { + hl_start = pos_start.col + offset; + hl_end = pos_end.col + offset; + } + (void)extmark_add_decoration(buf, (uint64_t)src_id, hl_id, + (int)lnum-1, hl_start, (int)lnum-1, hl_end, + VIRTTEXT_EMPTY); + } +} + +void clear_virttext(VirtText *text) +{ + for (size_t i = 0; i < kv_size(*text); i++) { + xfree(kv_A(*text, i).text); + } + kv_destroy(*text); + *text = (VirtText)KV_INITIAL_VALUE; +} + +VirtText *extmark_find_virttext(buf_T *buf, int row, uint64_t ns_id) +{ + MarkTreeIter itr[1]; + 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) { + break; + } + ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, + mark.id, false); + if (item && (ns_id == 0 || ns_id == item->ns_id) + && kv_size(item->virt_text)) { + return &item->virt_text; + } + marktree_itr_next(buf->b_marktree, itr); + } + return NULL; +} + + +bool extmark_decorations_reset(buf_T *buf, DecorationState *state) +{ + state->row = -1; + return buf->b_extmark_index; +} + + +bool extmark_decorations_start(buf_T *buf, int top_row, DecorationState *state) +{ + kv_size(state->active) = 0; + state->top_row = top_row; + marktree_itr_get(buf->b_marktree, top_row, 0, state->itr); + if (!state->itr->node) { + return false; + } + 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 + break; + } + // TODO(bfredl): dedicated flag for being a decoration? + if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { + goto next_mark; + } + mtpos_t altpos = marktree_lookup(buf->b_marktree, + mark.id^MARKTREE_END_FLAG, NULL); + + uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; + ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, + start_id, false); + if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row + && !kv_size(item->virt_text)) + || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { + goto next_mark; + } + + if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { + int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; + VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; + HlRange range; + if (mark.id&MARKTREE_END_FLAG) { + range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, + attr_id, vt }; + } else { + range = (HlRange){ mark.row, mark.col, altpos.row, + altpos.col, attr_id, vt }; + } + kv_push(state->active, range); + } +next_mark: + if (marktree_itr_node_done(state->itr)) { + break; + } + marktree_itr_next(buf->b_marktree, state->itr); + } + + return true; // TODO(bfredl): check if available in the region +} + +bool extmark_decorations_line(buf_T *buf, int row, DecorationState *state) +{ + if (state->row == -1) { + extmark_decorations_start(buf, row, state); + } + state->row = row; + state->col_until = -1; + return true; // TODO(bfredl): be more precise +} + +int extmark_decorations_col(buf_T *buf, int col, DecorationState *state) +{ + if (col <= state->col_until) { + return state->current; + } + state->col_until = MAXCOL; + while (true) { + mtmark_t mark = marktree_itr_current(state->itr); + if (mark.row < 0 || mark.row > state->row) { + break; + } else if (mark.row == state->row && mark.col > col) { + state->col_until = mark.col-1; + break; + } + + if ((mark.id&MARKTREE_END_FLAG)) { + // TODO(bfredl): check decorations flag + goto next_mark; + } + mtpos_t endpos = marktree_lookup(buf->b_marktree, + mark.id|MARKTREE_END_FLAG, NULL); + + ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, + mark.id, false); + + if (endpos.row < mark.row + || (endpos.row == mark.row && endpos.col <= mark.col)) { + if (!kv_size(item->virt_text)) { + goto next_mark; + } + } + + if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { + int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; + VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; + kv_push(state->active, ((HlRange){ mark.row, mark.col, + endpos.row, endpos.col, + attr_id, vt })); + } + +next_mark: + marktree_itr_next(buf->b_marktree, state->itr); + } + + int attr = 0; + size_t j = 0; + for (size_t i = 0; i < kv_size(state->active); i++) { + HlRange item = kv_A(state->active, i); + 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 && item.virt_text)) { + keep = false; + } + } else { + if (item.start_row < state->row + || (item.start_row == state->row && item.start_col <= col)) { + active = true; + if (item.end_row == state->row) { + state->col_until = MIN(state->col_until, item.end_col-1); + } + } else { + if (item.start_row == state->row) { + state->col_until = MIN(state->col_until, item.start_col-1); + } + } + } + if (active && item.attr_id > 0) { + attr = hl_combine_attr(attr, item.attr_id); + } + if (keep) { + kv_A(state->active, j++) = kv_A(state->active, i); + } + } + kv_size(state->active) = j; + state->current = attr; + return attr; +} + +VirtText *extmark_decorations_virt_text(buf_T *buf, DecorationState *state) +{ + extmark_decorations_col(buf, MAXCOL, state); + for (size_t i = 0; i < kv_size(state->active); i++) { + HlRange item = kv_A(state->active, i); + if (item.start_row == state->row && item.virt_text) { + return item.virt_text; + } + } + return NULL; +} diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h new file mode 100644 index 0000000000..829cbe0236 --- /dev/null +++ b/src/nvim/extmark.h @@ -0,0 +1,93 @@ +#ifndef NVIM_EXTMARK_H +#define NVIM_EXTMARK_H + +#include "nvim/buffer_defs.h" +#include "nvim/extmark_defs.h" +#include "nvim/marktree.h" + +EXTERN int extmark_splice_pending INIT(= 0); + +typedef struct +{ + uint64_t ns_id; + uint64_t mark_id; + int row; + colnr_T col; +} ExtmarkInfo; + +typedef kvec_t(ExtmarkInfo) ExtmarkArray; + + +// delete the columns between mincol and endcol +typedef struct { + int start_row; + colnr_T start_col; + int oldextent_row; + colnr_T oldextent_col; + int newextent_row; + colnr_T newextent_col; +} ExtmarkSplice; + +// adjust marks after :move operation +typedef struct { + int start_row; + int start_col; + int extent_row; + int extent_col; + int new_row; + int new_col; +} ExtmarkMove; + +// extmark was updated +typedef struct { + uint64_t mark; // raw mark id of the marktree + int old_row; + colnr_T old_col; + int row; + colnr_T col; +} ExtmarkSavePos; + +typedef enum { + kExtmarkSplice, + kExtmarkMove, + kExtmarkUpdate, + kExtmarkSavePos, + kExtmarkClear, +} UndoObjectType; + +// TODO(bfredl): reduce the number of undo action types +struct undo_object { + UndoObjectType type; + union { + ExtmarkSplice splice; + ExtmarkMove move; + ExtmarkSavePos savepos; + } data; +}; + + +typedef struct { + int start_row; + int start_col; + int end_row; + int end_col; + int attr_id; + VirtText *virt_text; +} HlRange; + +typedef struct { + MarkTreeIter itr[1]; + kvec_t(HlRange) active; + int top_row; + int row; + int col_until; + int current; + VirtText *virt_text; +} DecorationState; + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "extmark.h.generated.h" +#endif + +#endif // NVIM_EXTMARK_H diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h new file mode 100644 index 0000000000..c927048981 --- /dev/null +++ b/src/nvim/extmark_defs.h @@ -0,0 +1,37 @@ +#ifndef NVIM_EXTMARK_DEFS_H +#define NVIM_EXTMARK_DEFS_H + +#include "nvim/pos.h" // for colnr_T +#include "nvim/lib/kvec.h" + +typedef struct { + char *text; + int hl_id; +} VirtTextChunk; + +typedef kvec_t(VirtTextChunk) VirtText; +#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) + +typedef struct +{ + uint64_t ns_id; + uint64_t mark_id; + int hl_id; // highlight group + // TODO(bfredl): virt_text is pretty larger than the rest, + // pointer indirection? + VirtText virt_text; +} ExtmarkItem; + +typedef struct undo_object ExtmarkUndoObject; +typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t; + +// Undo/redo extmarks + +typedef enum { + kExtmarkNOOP, // Extmarks shouldn't be moved + kExtmarkUndo, // Operation should be reversable/undoable + kExtmarkNoUndo, // Operation should not be reversable + kExtmarkUndoNoRedo, // Operation should be undoable, but not redoable +} ExtmarkOp; + +#endif // NVIM_EXTMARK_DEFS_H diff --git a/src/nvim/fold.c b/src/nvim/fold.c index addfab8f08..0b14a6affb 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -22,7 +22,7 @@ #include "nvim/func_attr.h" #include "nvim/indent.h" #include "nvim/buffer_updates.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mark.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 2c5fdd8ea6..f8018c039d 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -13,7 +13,7 @@ #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/misc1.h" diff --git a/src/nvim/map.h b/src/nvim/map.h index 761938776d..0ad7865bf0 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -4,7 +4,7 @@ #include #include "nvim/map_defs.h" -#include "nvim/mark_extended_defs.h" +#include "nvim/extmark_defs.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" #include "nvim/highlight_defs.h" diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 4a7452493a..fa7c7d61c9 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -20,7 +20,7 @@ #include "nvim/ex_cmds.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/mark.h b/src/nvim/mark.h index d8370c367a..b3d9b5d95a 100644 --- a/src/nvim/mark.h +++ b/src/nvim/mark.h @@ -6,7 +6,7 @@ #include "nvim/buffer_defs.h" #include "nvim/func_attr.h" #include "nvim/mark_defs.h" -#include "nvim/mark_extended_defs.h" +#include "nvim/extmark_defs.h" #include "nvim/memory.h" #include "nvim/pos.h" #include "nvim/os/time.h" diff --git a/src/nvim/mark_extended.c b/src/nvim/mark_extended.c deleted file mode 100644 index b60d847676..0000000000 --- a/src/nvim/mark_extended.c +++ /dev/null @@ -1,910 +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 - -// Implements extended marks for plugins. Each mark exists in a btree of -// lines containing btrees of columns. -// -// The btree provides efficient range lookups. -// A map of pointers to the marks is used for fast lookup by mark id. -// -// Marks are moved by calls to extmark_splice. Additionally mark_adjust -// might adjust extmarks to line inserts/deletes. -// -// Undo/Redo of marks is implemented by storing the call arguments to -// extmark_splice. The list of arguments is applied in extmark_apply_undo. -// The only case where we have to copy extmarks is for the area being effected -// by a delete. -// -// Marks live in namespaces that allow plugins/users to segregate marks -// from other users. -// -// For possible ideas for efficency improvements see: -// http://blog.atom.io/2015/06/16/optimizing-an-important-atom-primitive.html -// TODO(bfredl): These ideas could be used for an enhanced btree, which -// wouldn't need separate line and column layers. -// Other implementations exist in gtk and tk toolkits. -// -// Deleting marks only happens when explicitly calling extmark_del, deleteing -// over a range of marks will only move the marks. Deleting on a mark will -// leave it in same position unless it is on the EOL of a line. - -#include -#include "nvim/api/vim.h" -#include "nvim/vim.h" -#include "nvim/charset.h" -#include "nvim/mark_extended.h" -#include "nvim/buffer_updates.h" -#include "nvim/memline.h" -#include "nvim/pos.h" -#include "nvim/globals.h" -#include "nvim/map.h" -#include "nvim/lib/kbtree.h" -#include "nvim/undo.h" -#include "nvim/buffer.h" -#include "nvim/syntax.h" -#include "nvim/highlight.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mark_extended.c.generated.h" -#endif - -static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { - if (!buf->b_extmark_ns) { - if (!put) { - return NULL; - } - buf->b_extmark_ns = map_new(uint64_t, ExtmarkNs)(); - buf->b_extmark_index = map_new(uint64_t, ExtmarkItem)(); - } - - ExtmarkNs *ns = map_ref(uint64_t, ExtmarkNs)(buf->b_extmark_ns, ns_id, put); - if (put && ns->map == NULL) { - ns->map = map_new(uint64_t, uint64_t)(); - ns->free_id = 1; - } - return ns; -} - - -/// Create or update an extmark -/// -/// must not be used during iteration! -/// @returns the mark id -uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, - int row, colnr_T col, ExtmarkOp op) -{ - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - mtpos_t old_pos; - uint64_t mark = 0; - - if (id == 0) { - id = ns->free_id++; - } else { - uint64_t old_mark = map_get(uint64_t, uint64_t)(ns->map, id); - if (old_mark) { - if (old_mark & MARKTREE_PAIRED_FLAG) { - extmark_del(buf, ns_id, id); - } else { - // TODO(bfredl): we need to do more if "revising" a decoration mark. - MarkTreeIter itr[1]; - old_pos = marktree_lookup(buf->b_marktree, old_mark, itr); - assert(itr->node); - if (old_pos.row == row && old_pos.col == col) { - map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, old_mark); - mark = marktree_revise(buf->b_marktree, itr); - goto revised; - } - marktree_del_itr(buf->b_marktree, itr, false); - } - } else { - ns->free_id = MAX(ns->free_id, id+1); - } - } - - mark = marktree_put(buf->b_marktree, row, col, true); -revised: - map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, - (ExtmarkItem){ ns_id, id, 0, - KV_INITIAL_VALUE }); - map_put(uint64_t, uint64_t)(ns->map, id, mark); - - 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); - } - return id; -} - -static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) -{ - MarkTreeIter itr[1]; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - if (pos.row == -1) { - return false; - } - - if (pos.row == row && pos.col == col) { - return true; - } - - marktree_move(buf->b_marktree, itr, row, col); - return true; -} - -// Remove an extmark -// Returns 0 on missing id -bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_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) { - return false; - } - - MarkTreeIter itr[1]; - mtpos_t pos = marktree_lookup(buf->b_marktree, mark, itr); - assert(pos.row >= 0); - marktree_del_itr(buf->b_marktree, itr, false); - ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); - - if (mark & MARKTREE_PAIRED_FLAG) { - mtpos_t pos2 = marktree_lookup(buf->b_marktree, - mark|MARKTREE_END_FLAG, itr); - assert(pos2.row >= 0); - marktree_del_itr(buf->b_marktree, itr, false); - if (item.hl_id && pos2.row >= pos.row) { - redraw_buf_range_later(buf, pos.row+1, pos2.row+1); - } - } - - if (kv_size(item.virt_text)) { - redraw_buf_line_later(buf, pos.row+1); - } - clear_virttext(&item.virt_text); - - 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) -{ - if (!buf->b_extmark_ns) { - return false; - } - - bool marks_cleared = false; - - bool all_ns = (ns_id == 0); - ExtmarkNs *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, uint64_t) *delete_set = NULL; - if (delete_set == NULL) { - delete_set = map_new(uint64_t, uint64_t)(); - } - - MarkTreeIter itr[1]; - 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)) { - break; - } - uint64_t *del_status = map_ref(uint64_t, uint64_t)(delete_set, mark.id, - false); - if (del_status) { - marktree_del_itr(buf->b_marktree, itr, false); - map_del(uint64_t, uint64_t)(delete_set, mark.id); - if (*del_status > 0) { - redraw_buf_range_later(buf, (linenr_T)(*del_status), mark.row+1); - } - 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)) { - if (kv_size(item.virt_text)) { - redraw_buf_line_later(buf, mark.row+1); - } - clear_virttext(&item.virt_text); - marks_cleared = true; - if (mark.id & MARKTREE_PAIRED_FLAG) { - uint64_t other = mark.id ^ MARKTREE_END_FLAG; - uint64_t status = item.hl_id ? ((uint64_t)mark.row+1) : 0; - map_put(uint64_t, uint64_t)(delete_set, other, status); - } - 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, mark.id); - marktree_del_itr(buf->b_marktree, itr, false); - } else { - marktree_itr_next(buf->b_marktree, itr); - } - } - uint64_t id, status; - map_foreach(delete_set, id, status, { - mtpos_t pos = marktree_lookup(buf->b_marktree, id, itr); - assert(itr->node); - marktree_del_itr(buf->b_marktree, itr, false); - if (status > 0) { - redraw_buf_range_later(buf, (linenr_T)status, pos.row+1); - } - }); - map_clear(uint64_t, uint64_t)(delete_set); - 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 -ExtmarkArray extmark_get(buf_T *buf, uint64_t ns_id, - int l_row, colnr_T l_col, - int u_row, colnr_T u_col, - int64_t amount, bool reverse) -{ - ExtmarkArray array = KV_INITIAL_VALUE; - MarkTreeIter itr[1]; - // Find all the marks - marktree_itr_get_ext(buf->b_marktree, (mtpos_t){ l_row, l_col }, - itr, reverse, false, NULL); - int order = reverse ? -1 : 1; - while ((int64_t)kv_size(array) < amount) { - mtmark_t mark = marktree_itr_current(itr); - if (mark.row < 0 - || (mark.row - u_row) * order > 0 - || (mark.row == u_row && (mark.col - u_col) * order > 0)) { - break; - } - 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 (reverse) { - marktree_itr_prev(buf->b_marktree, itr); - } else { - marktree_itr_next(buf->b_marktree, itr); - } - } - return array; -} - -// Lookup an extmark by id -ExtmarkInfo extmark_from_id(buf_T *buf, uint64_t ns_id, uint64_t id) -{ - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, false); - ExtmarkInfo ret = { 0, 0, -1, -1 }; - if (!ns) { - 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); - assert(pos.row >= 0); - - ret.ns_id = ns_id; - ret.mark_id = id; - ret.row = pos.row; - ret.col = pos.col; - - return ret; -} - - -// free extmarks from the buffer -void extmark_free_all(buf_T *buf) -{ - if (!buf->b_extmark_ns) { - return; - } - - uint64_t id; - ExtmarkNs ns; - ExtmarkItem item; - - marktree_clear(buf->b_marktree); - - map_foreach(buf->b_extmark_ns, id, ns, { - (void)id; - map_free(uint64_t, uint64_t)(ns.map); - }); - map_free(uint64_t, ExtmarkNs)(buf->b_extmark_ns); - buf->b_extmark_ns = NULL; - - map_foreach(buf->b_extmark_index, id, item, { - (void)id; - clear_virttext(&item.virt_text); - }); - map_free(uint64_t, ExtmarkItem)(buf->b_extmark_index); - buf->b_extmark_index = NULL; -} - - -// 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); - if (!uhp) { - return; - } - - ExtmarkSavePos pos; - pos.mark = mark; - pos.old_row = -1; - pos.old_col = -1; - pos.row = row; - pos.col = col; - - ExtmarkUndoObject undo = { .type = kExtmarkSavePos, - .data.savepos = pos }; - - kv_push(uhp->uh_extmark, undo); -} - -/// copy extmarks data between range -/// -/// useful when we cannot simply reverse the operation. This will do nothing on -/// redo, enforces correct position when undo. -void u_extmark_copy(buf_T *buf, - int l_row, colnr_T l_col, - int u_row, colnr_T u_col) -{ - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - ExtmarkUndoObject undo; - - MarkTreeIter itr[1]; - 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)) { - break; - } - ExtmarkSavePos pos; - pos.mark = mark.id; - pos.old_row = mark.row; - pos.old_col = mark.col; - pos.row = -1; - pos.col = -1; - - undo.data.savepos = pos; - undo.type = kExtmarkSavePos; - kv_push(uhp->uh_extmark, undo); - - marktree_itr_next(buf->b_marktree, itr); - } -} - -/// undo or redo an extmark operation -void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) -{ - // splice: any text operation changing position (except :move) - if (undo_info.type == kExtmarkSplice) { - // Undo - ExtmarkSplice splice = undo_info.data.splice; - if (undo) { - extmark_splice(curbuf, - splice.start_row, splice.start_col, - splice.newextent_row, splice.newextent_col, - splice.oldextent_row, splice.oldextent_col, - kExtmarkNoUndo); - - } else { - extmark_splice(curbuf, - splice.start_row, splice.start_col, - splice.oldextent_row, splice.oldextent_col, - splice.newextent_row, splice.newextent_col, - kExtmarkNoUndo); - } - // kExtmarkSavePos - } else if (undo_info.type == kExtmarkSavePos) { - ExtmarkSavePos pos = undo_info.data.savepos; - if (undo) { - if (pos.old_row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.old_row, pos.old_col); - } - // Redo - } else { - if (pos.row >= 0) { - extmark_setraw(curbuf, pos.mark, pos.row, pos.col); - } - } - } else if (undo_info.type == kExtmarkMove) { - ExtmarkMove move = undo_info.data.move; - if (undo) { - extmark_move_region(curbuf, - move.new_row, move.new_col, - move.extent_row, move.extent_col, - move.start_row, move.start_col, - kExtmarkNoUndo); - } else { - extmark_move_region(curbuf, - move.start_row, move.start_col, - move.extent_row, move.extent_col, - move.new_row, move.new_col, - kExtmarkNoUndo); - } - } -} - - -// 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) -{ - if (!curbuf_splice_pending) { - int old_extent, new_extent; - if (amount == MAXLNUM) { - old_extent = (int)(line2 - line1+1); - new_extent = (int)(amount_after + old_extent); - } else { - // A region is either deleted (amount == MAXLNUM) or - // added (line2 == MAXLNUM). The only other case is :move - // which is handled by a separate entry point extmark_move_region. - assert(line2 == MAXLNUM); - old_extent = 0; - new_extent = (int)amount; - } - extmark_splice(buf, - (int)line1-1, 0, - old_extent, 0, - new_extent, 0, undo); - } -} - -void extmark_splice(buf_T *buf, - int start_row, colnr_T start_col, - int oldextent_row, colnr_T oldextent_col, - int newextent_row, colnr_T newextent_col, - ExtmarkOp undo) -{ - buf_updates_send_splice(buf, start_row, start_col, - oldextent_row, oldextent_col, - newextent_row, newextent_col); - - if (undo == kExtmarkUndo && (oldextent_row > 0 || oldextent_col > 0)) { - // Copy marks that would be effected by delete - // TODO(bfredl): Be "smart" about gravity here, left-gravity at the - // beginning and right-gravity at the end need not be preserved. - // Also be smart about marks that already have been saved (important for - // merge!) - int end_row = start_row + oldextent_row; - int end_col = (oldextent_row ? 0 : start_col) + oldextent_col; - u_extmark_copy(buf, start_row, start_col, end_row, end_col); - } - - - marktree_splice(buf->b_marktree, start_row, start_col, - oldextent_row, oldextent_col, - newextent_row, newextent_col); - - if (undo == kExtmarkUndo) { - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - bool merged = false; - // TODO(bfredl): this is quite rudimentary. We merge small (within line) - // inserts with each other and small deletes with each other. Add full - // merge algorithm later. - if (oldextent_row == 0 && newextent_row == 0 && kv_size(uhp->uh_extmark)) { - ExtmarkUndoObject *item = &kv_A(uhp->uh_extmark, - kv_size(uhp->uh_extmark)-1); - if (item->type == kExtmarkSplice) { - ExtmarkSplice *splice = &item->data.splice; - if (splice->start_row == start_row && splice->oldextent_row == 0 - && splice->newextent_row == 0) { - if (oldextent_col == 0 && start_col >= splice->start_col - && start_col <= splice->start_col+splice->newextent_col) { - splice->newextent_col += newextent_col; - merged = true; - } else if (newextent_col == 0 - && start_col == splice->start_col+splice->newextent_col) { - splice->oldextent_col += oldextent_col; - merged = true; - } else if (newextent_col == 0 - && start_col + oldextent_col == splice->start_col) { - splice->start_col = start_col; - splice->oldextent_col += oldextent_col; - merged = true; - } - } - } - } - - if (!merged) { - ExtmarkSplice splice; - splice.start_row = start_row; - splice.start_col = start_col; - splice.oldextent_row = oldextent_row; - splice.oldextent_col = oldextent_col; - splice.newextent_row = newextent_row; - splice.newextent_col = newextent_col; - - kv_push(uhp->uh_extmark, - ((ExtmarkUndoObject){ .type = kExtmarkSplice, - .data.splice = splice })); - } - } -} - - -void extmark_move_region(buf_T *buf, - int start_row, colnr_T start_col, - int extent_row, colnr_T extent_col, - int new_row, colnr_T new_col, - ExtmarkOp undo) -{ - // TODO(bfredl): this is not synced to the buffer state inside the callback. - // But unless we make the undo implementation smarter, this is not ensured - // anyway. - buf_updates_send_splice(buf, start_row, start_col, - extent_row, extent_col, - 0, 0); - - marktree_move_region(buf->b_marktree, start_row, start_col, - extent_row, extent_col, - new_row, new_col); - - buf_updates_send_splice(buf, new_row, new_col, - 0, 0, - extent_row, extent_col); - - - if (undo == kExtmarkUndo) { - u_header_T *uhp = u_force_get_undo_header(buf); - if (!uhp) { - return; - } - - ExtmarkMove move; - move.start_row = start_row; - move.start_col = start_col; - move.extent_row = extent_row; - move.extent_col = extent_col; - move.new_row = new_row; - move.new_col = new_col; - - kv_push(uhp->uh_extmark, - ((ExtmarkUndoObject){ .type = kExtmarkMove, - .data.move = move })); - } -} - -uint64_t src2ns(Integer *src_id) -{ - if (*src_id == 0) { - *src_id = (Integer)nvim_create_namespace((String)STRING_INIT); - } - if (*src_id < 0) { - return UINT64_MAX; - } else { - return (uint64_t)(*src_id); - } -} - -/// Adds a decoration to a buffer. -/// -/// Unlike matchaddpos() highlights, these follow changes to the the buffer -/// texts. Decorations are represented internally and in the API as extmarks. -/// -/// @param buf The buffer to add decorations to -/// @param ns_id A valid namespace id. -/// @param hl_id Id of the highlight group to use (or zero) -/// @param start_row The line to highlight -/// @param start_col First column to highlight -/// @param end_row The line to highlight -/// @param end_col The last column to highlight -/// @param virt_text Virtual text (currently placed at the EOL of start_row) -/// @return The extmark id inside the namespace -uint64_t extmark_add_decoration(buf_T *buf, uint64_t ns_id, int hl_id, - int start_row, colnr_T start_col, - int end_row, colnr_T end_col, - VirtText virt_text) -{ - ExtmarkNs *ns = buf_ns_ref(buf, ns_id, true); - ExtmarkItem item; - item.ns_id = ns_id; - item.mark_id = ns->free_id++; - item.hl_id = hl_id; - item.virt_text = virt_text; - - uint64_t mark; - - if (end_row > -1) { - mark = marktree_put_pair(buf->b_marktree, - start_row, start_col, true, - end_row, end_col, false); - } else { - mark = marktree_put(buf->b_marktree, start_row, start_col, true); - } - - map_put(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark, item); - map_put(uint64_t, uint64_t)(ns->map, item.mark_id, mark); - - redraw_buf_range_later(buf, start_row+1, - (end_row >= 0 ? end_row : start_row) + 1); - return item.mark_id; -} - -/// Add highlighting to a buffer, bounded by two cursor positions, -/// with an offset. -/// -/// @param buf Buffer to add highlights to -/// @param src_id src_id to use or 0 to use a new src_id group, -/// or -1 for ungrouped highlight. -/// @param hl_id Highlight group id -/// @param pos_start Cursor position to start the hightlighting at -/// @param pos_end Cursor position to end the highlighting at -/// @param offset Move the whole highlighting this many columns to the right -void bufhl_add_hl_pos_offset(buf_T *buf, - int src_id, - int hl_id, - lpos_T pos_start, - lpos_T pos_end, - colnr_T offset) -{ - colnr_T hl_start = 0; - colnr_T hl_end = 0; - - // TODO(bfredl): if decoration had blocky mode, we could avoid this loop - for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { - if (pos_start.lnum < lnum && lnum < pos_end.lnum) { - hl_start = offset-1; - hl_end = MAXCOL; - } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { - hl_start = pos_start.col + offset; - hl_end = MAXCOL; - } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { - hl_start = offset-1; - hl_end = pos_end.col + offset; - } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { - hl_start = pos_start.col + offset; - hl_end = pos_end.col + offset; - } - (void)extmark_add_decoration(buf, (uint64_t)src_id, hl_id, - (int)lnum-1, hl_start, (int)lnum-1, hl_end, - VIRTTEXT_EMPTY); - } -} - -void clear_virttext(VirtText *text) -{ - for (size_t i = 0; i < kv_size(*text); i++) { - xfree(kv_A(*text, i).text); - } - kv_destroy(*text); - *text = (VirtText)KV_INITIAL_VALUE; -} - -VirtText *extmark_find_virttext(buf_T *buf, int row, uint64_t ns_id) -{ - MarkTreeIter itr[1]; - 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) { - break; - } - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - if (item && (ns_id == 0 || ns_id == item->ns_id) - && kv_size(item->virt_text)) { - return &item->virt_text; - } - marktree_itr_next(buf->b_marktree, itr); - } - return NULL; -} - - -bool extmark_decorations_reset(buf_T *buf, DecorationState *state) -{ - state->row = -1; - return buf->b_extmark_index; -} - - -bool extmark_decorations_start(buf_T *buf, int top_row, DecorationState *state) -{ - kv_size(state->active) = 0; - state->top_row = top_row; - marktree_itr_get(buf->b_marktree, top_row, 0, state->itr); - if (!state->itr->node) { - return false; - } - 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 - break; - } - // TODO(bfredl): dedicated flag for being a decoration? - if ((mark.row < top_row && mark.id&MARKTREE_END_FLAG)) { - goto next_mark; - } - mtpos_t altpos = marktree_lookup(buf->b_marktree, - mark.id^MARKTREE_END_FLAG, NULL); - - uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - start_id, false); - if ((!(mark.id&MARKTREE_END_FLAG) && altpos.row < top_row - && !kv_size(item->virt_text)) - || ((mark.id&MARKTREE_END_FLAG) && altpos.row >= top_row)) { - goto next_mark; - } - - if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { - int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; - VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; - HlRange range; - if (mark.id&MARKTREE_END_FLAG) { - range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col, - attr_id, vt }; - } else { - range = (HlRange){ mark.row, mark.col, altpos.row, - altpos.col, attr_id, vt }; - } - kv_push(state->active, range); - } -next_mark: - if (marktree_itr_node_done(state->itr)) { - break; - } - marktree_itr_next(buf->b_marktree, state->itr); - } - - return true; // TODO(bfredl): check if available in the region -} - -bool extmark_decorations_line(buf_T *buf, int row, DecorationState *state) -{ - if (state->row == -1) { - extmark_decorations_start(buf, row, state); - } - state->row = row; - state->col_until = -1; - return true; // TODO(bfredl): be more precise -} - -int extmark_decorations_col(buf_T *buf, int col, DecorationState *state) -{ - if (col <= state->col_until) { - return state->current; - } - state->col_until = MAXCOL; - while (true) { - mtmark_t mark = marktree_itr_current(state->itr); - if (mark.row < 0 || mark.row > state->row) { - break; - } else if (mark.row == state->row && mark.col > col) { - state->col_until = mark.col-1; - break; - } - - if ((mark.id&MARKTREE_END_FLAG)) { - // TODO(bfredl): check decorations flag - goto next_mark; - } - mtpos_t endpos = marktree_lookup(buf->b_marktree, - mark.id|MARKTREE_END_FLAG, NULL); - - ExtmarkItem *item = map_ref(uint64_t, ExtmarkItem)(buf->b_extmark_index, - mark.id, false); - - if (endpos.row < mark.row - || (endpos.row == mark.row && endpos.col <= mark.col)) { - if (!kv_size(item->virt_text)) { - goto next_mark; - } - } - - if (item && (item->hl_id > 0 || kv_size(item->virt_text))) { - int attr_id = item->hl_id > 0 ? syn_id2attr(item->hl_id) : 0; - VirtText *vt = kv_size(item->virt_text) ? &item->virt_text : NULL; - kv_push(state->active, ((HlRange){ mark.row, mark.col, - endpos.row, endpos.col, - attr_id, vt })); - } - -next_mark: - marktree_itr_next(buf->b_marktree, state->itr); - } - - int attr = 0; - size_t j = 0; - for (size_t i = 0; i < kv_size(state->active); i++) { - HlRange item = kv_A(state->active, i); - 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 && item.virt_text)) { - keep = false; - } - } else { - if (item.start_row < state->row - || (item.start_row == state->row && item.start_col <= col)) { - active = true; - if (item.end_row == state->row) { - state->col_until = MIN(state->col_until, item.end_col-1); - } - } else { - if (item.start_row == state->row) { - state->col_until = MIN(state->col_until, item.start_col-1); - } - } - } - if (active && item.attr_id > 0) { - attr = hl_combine_attr(attr, item.attr_id); - } - if (keep) { - kv_A(state->active, j++) = kv_A(state->active, i); - } - } - kv_size(state->active) = j; - state->current = attr; - return attr; -} - -VirtText *extmark_decorations_virt_text(buf_T *buf, DecorationState *state) -{ - extmark_decorations_col(buf, MAXCOL, state); - for (size_t i = 0; i < kv_size(state->active); i++) { - HlRange item = kv_A(state->active, i); - if (item.start_row == state->row && item.virt_text) { - return item.virt_text; - } - } - return NULL; -} diff --git a/src/nvim/mark_extended.h b/src/nvim/mark_extended.h deleted file mode 100644 index f809148d9b..0000000000 --- a/src/nvim/mark_extended.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef NVIM_MARK_EXTENDED_H -#define NVIM_MARK_EXTENDED_H - -#include "nvim/buffer_defs.h" -#include "nvim/mark_extended_defs.h" -#include "nvim/marktree.h" - -EXTERN int extmark_splice_pending INIT(= 0); - -typedef struct -{ - uint64_t ns_id; - uint64_t mark_id; - int row; - colnr_T col; -} ExtmarkInfo; - -typedef kvec_t(ExtmarkInfo) ExtmarkArray; - - -// delete the columns between mincol and endcol -typedef struct { - int start_row; - colnr_T start_col; - int oldextent_row; - colnr_T oldextent_col; - int newextent_row; - colnr_T newextent_col; -} ExtmarkSplice; - -// adjust marks after :move operation -typedef struct { - int start_row; - int start_col; - int extent_row; - int extent_col; - int new_row; - int new_col; -} ExtmarkMove; - -// extmark was updated -typedef struct { - uint64_t mark; // raw mark id of the marktree - int old_row; - colnr_T old_col; - int row; - colnr_T col; -} ExtmarkSavePos; - -typedef enum { - kExtmarkSplice, - kExtmarkMove, - kExtmarkUpdate, - kExtmarkSavePos, - kExtmarkClear, -} UndoObjectType; - -// TODO(bfredl): reduce the number of undo action types -struct undo_object { - UndoObjectType type; - union { - ExtmarkSplice splice; - ExtmarkMove move; - ExtmarkSavePos savepos; - } data; -}; - - -typedef struct { - int start_row; - int start_col; - int end_row; - int end_col; - int attr_id; - VirtText *virt_text; -} HlRange; - -typedef struct { - MarkTreeIter itr[1]; - kvec_t(HlRange) active; - int top_row; - int row; - int col_until; - int current; - VirtText *virt_text; -} DecorationState; - - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "mark_extended.h.generated.h" -#endif - -#endif // NVIM_MARK_EXTENDED_H diff --git a/src/nvim/mark_extended_defs.h b/src/nvim/mark_extended_defs.h deleted file mode 100644 index 439f7f0b36..0000000000 --- a/src/nvim/mark_extended_defs.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef NVIM_MARK_EXTENDED_DEFS_H -#define NVIM_MARK_EXTENDED_DEFS_H - -#include "nvim/pos.h" // for colnr_T -#include "nvim/lib/kvec.h" - -typedef struct { - char *text; - int hl_id; -} VirtTextChunk; - -typedef kvec_t(VirtTextChunk) VirtText; -#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) - -typedef struct -{ - uint64_t ns_id; - uint64_t mark_id; - int hl_id; // highlight group - // TODO(bfredl): virt_text is pretty larger than the rest, - // pointer indirection? - VirtText virt_text; -} ExtmarkItem; - -typedef struct undo_object ExtmarkUndoObject; -typedef kvec_t(ExtmarkUndoObject) extmark_undo_vec_t; - -// Undo/redo extmarks - -typedef enum { - kExtmarkNOOP, // Extmarks shouldn't be moved - kExtmarkUndo, // Operation should be reversable/undoable - kExtmarkNoUndo, // Operation should not be reversable - kExtmarkUndoNoRedo, // Operation should be undoable, but not redoable -} ExtmarkOp; - -#endif // NVIM_MARK_EXTENDED_DEFS_H diff --git a/src/nvim/ops.c b/src/nvim/ops.c index da2b81fd0a..641323ae5e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -31,7 +31,7 @@ #include "nvim/indent.h" #include "nvim/log.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index cb155cfc65..a451451726 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -87,7 +87,7 @@ #include "nvim/highlight.h" #include "nvim/main.h" #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" diff --git a/src/nvim/undo.c b/src/nvim/undo.c index fda647106d..1f74bada41 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -93,7 +93,7 @@ #include "nvim/buffer_updates.h" #include "nvim/pos.h" // MAXLNUM #include "nvim/mark.h" -#include "nvim/mark_extended.h" +#include "nvim/extmark.h" #include "nvim/memline.h" #include "nvim/message.h" #include "nvim/misc1.h" diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 0fa3b415ec..cc2c39a711 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -4,7 +4,7 @@ #include // for time_t #include "nvim/pos.h" -#include "nvim/mark_extended_defs.h" +#include "nvim/extmark_defs.h" #include "nvim/mark_defs.h" typedef struct u_header u_header_T; -- cgit